From 534116b53fae0aae1919fb4d99ba4cee0ee20c44 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Flori Date: Fri, 27 Sep 2013 18:46:29 +0200 Subject: [PATCH 001/546] Improve naive point counting and implement zeta_function. --- .../hyperelliptic_finite_field.py | 183 +++++++++++------- 1 file changed, 108 insertions(+), 75 deletions(-) diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py index 8a000386cd7..bc8ef0690c8 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py @@ -9,6 +9,8 @@ - Alyson Deines, Marina Gresham, Gagan Sekhon, (2010) +- Daniel Krenn (2011) + - Jean-Pierre Flori, Jan Tuitman (2013) EXAMPLES:: @@ -25,6 +27,7 @@ # Copyright (C) 2007 Robert Bradshaw # Copyright (C) 2010 Alyson Deines , Marina Gresham # , Gagan Sekhon +# Copyright (C) 2011 Daniel Krenn # Copyright (C) 2013 Jean-Pierre Flori , # Jan Tuitman # @@ -281,15 +284,13 @@ def frobenius_polynomial_cardinalities(self, a=None): sage: H.frobenius_polynomial_cardinalities() x^4 - x^3 - 52*x^2 - 37*x + 1369 - Over a non-prime field, this currently does not work for ``g \geq 2``:: + Curve over a non-prime field:: - sage: K. = GF(17**3) + sage: K. = GF(7**2) sage: R. = PolynomialRing(K) sage: H = HyperellipticCurve(t^5 + z*t + z^2) sage: H.frobenius_polynomial_cardinalities() - Traceback (most recent call last): - ... - NotImplementedError: Naive method only implemented over base field or extensions of prime fields in odd characteristic. + x^4 + 8*x^3 + 70*x^2 + 392*x + 2401 This method may actually be useful when `hypellfrob` does not work:: @@ -354,7 +355,7 @@ def frobenius_polynomial_matrix(self, M=None, algorithm='hypellfrob'): sage: H.frobenius_polynomial_matrix() x^8 + 281*x^7 + 55939*x^6 + 14144175*x^5 + 3156455369*x^4 + 707194605825*x^3 + 139841906155939*x^2 + 35122892542149719*x + 6249500014999800001 sage: H = HyperellipticCurve(t^15 + t^5 + 1) - sage: H.frobenius_polynomial_matrix() # long time + sage: H.frobenius_polynomial_matrix() # long time, 8s on a Corei7 x^14 - 76*x^13 + 220846*x^12 - 12984372*x^11 + 24374326657*x^10 - 1203243210304*x^9 + 1770558798515792*x^8 - 74401511415210496*x^7 + 88526169366991084208*x^6 - 3007987702642212810304*x^5 + 3046608028331197124223343*x^4 - 81145833008762983138584372*x^3 + 69007473838551978905211279154*x^2 - 1187357507124810002849977200076*x + 781140631562281254374947500349999 This `hypellfrob` program doesn't support non-prime fields:: @@ -413,27 +414,21 @@ def frobenius_polynomial(self): sage: H.frobenius_polynomial() x^6 - 14*x^5 + 1512*x^4 - 66290*x^3 + 3028536*x^2 - 56168126*x + 8036054027 - Curves defined over a non-prime field are only supported for ``g < 2`` - in odd characteristic and a naive algorithm is used - (and we should actually use fast point counting on elliptic curves):: + Curves defined over a non-prime field are only supported as well, + but a naive algorithm is used; especially when ``g = 1``, + fast point counting on elliptic curves should be used:: sage: K. = GF(23**3) sage: R. = PolynomialRing(K) sage: H = HyperellipticCurve(t^3 + z*t + 4) - sage: H.frobenius_polynomial() # long time + sage: H.frobenius_polynomial() # long time, 4s on a Corei7 x^2 - 15*x + 12167 - To support higher genera, we need a way of easily chacking for - squareness in quotient rings of polynomial rings over finite fields - of odd characteristic:: - - sage: K. = GF(23**3) + sage: K. = GF(3**3) sage: R. = PolynomialRing(K) sage: H = HyperellipticCurve(t^5 + z*t + z**3) sage: H.frobenius_polynomial() - Traceback (most recent call last): - ... - NotImplementedError: Naive method only implemented over base field or extensions of prime fields in odd characteristic. + x^4 - 3*x^3 + 10*x^2 - 81*x + 729 Over prime fields of odd characteristic, when ``h`` is non-zero, this naive algorithm is currently used as well, whereas we should @@ -442,7 +437,7 @@ def frobenius_polynomial(self): sage: K = GF(101) sage: R. = PolynomialRing(K) sage: H = HyperellipticCurve(t^5 + 27*t + 3, t) - sage: H.frobenius_polynomial() + sage: H.frobenius_polynomial() # long time, 3s on a Corei7 x^4 + 2*x^3 - 58*x^2 + 202*x + 10201 In even characteristic, the naive algorithm could cover all cases @@ -454,9 +449,7 @@ def frobenius_polynomial(self): sage: R. = PolynomialRing(K) sage: H = HyperellipticCurve(t^5 + z*t + z**3, t) sage: H.frobenius_polynomial() - Traceback (most recent call last): - ... - NotImplementedError: object does not support iteration + x^4 - x^3 + 16*x^2 - 32*x + 1024 """ K = self.base_ring() e = K.degree() @@ -792,13 +785,13 @@ def count_points_frobenius_polynomial(self, n=1, f=None): The following computation takes a long time as the complete characteristic polynomial of the frobenius is computed:: - sage: H.count_points_frobenius_polynomial(3) # long time, 20s on a Corei7 + sage: H.count_points_frobenius_polynomial(3) # long time, 20s on a Corei7 (when computed before the following test of course) [49491, 2500024375, 124992509154249] As the polynomial is cached, further computations of number of points are really fast:: - sage: H.count_points_frobenius_polynomial(19) + sage: H.count_points_frobenius_polynomial(19) # long time, because of the previous test [49491, 2500024375, 124992509154249, @@ -872,7 +865,7 @@ def count_points_exhaustive(self, n=1, naive=False): This behavior can be disabled by passing `naive=True`:: - sage: H.count_points_exhaustive(n=6, naive=True) + sage: H.count_points_exhaustive(n=6, naive=True) # long time, 7s on a Corei7 [9, 27, 108, 675, 3069, 16302] """ g = self.genus() @@ -1020,6 +1013,25 @@ def count_points(self, n=1): sage: H = HyperellipticCurve(t^13 + 3*t^5 + 5) sage: H.count_points(n=6) [112, 16360, 2045356, 260199160, 33038302802, 4195868633548] + + sage: P. = PolynomialRing(GF(3)) + sage: H = HyperellipticCurve(x^3+x^2+1) + sage: C1 = H.count_points(4); C1 + [6, 12, 18, 96] + sage: C2 = sage.schemes.generic.scheme.Scheme.count_points(H,4); C2 # long time, 2s on a Corei7 + [6, 12, 18, 96] + sage: C1 == C2 # long time, because we need C2 to be defined + True + + sage: P. = PolynomialRing(GF(9,'a')) + sage: H = HyperellipticCurve(x^5+x^2+1) + sage: H.count_points(5) + [18, 78, 738, 6366, 60018] + + sage: F. = GF(4); P. = F[] + sage: H = HyperellipticCurve(x^5+a*x^2+1, x+a+1) + sage: H.count_points(6) + [2, 24, 74, 256, 1082, 4272] """ K = self.base_ring() q = K.cardinality() @@ -1057,6 +1069,16 @@ def cardinality_exhaustive(self, extension_degree=1, algorithm=None): sage: H = HyperellipticCurve(t^7 + 3*t^5 + 5) sage: H.cardinality_exhaustive() 1025 + + sage: P. = PolynomialRing(GF(9,'a')) + sage: H = HyperellipticCurve(x^5+x^2+1) + sage: H.count_points(5) + [18, 78, 738, 6366, 60018] + + sage: F. = GF(4); P. = F[] + sage: H = HyperellipticCurve(x^5+a*x^2+1, x+a+1) + sage: H.count_points(6) + [2, 24, 74, 256, 1082, 4272] """ K = self.base_ring() g = self.genus() @@ -1095,68 +1117,39 @@ def cardinality_exhaustive(self, extension_degree=1, algorithm=None): a += 2 # affine points + if n == 1: + # the base field + L = K + fext = f + hext = h + else: + # extension of the prime field + from sage.categories.homset import Hom + L = GF(K.cardinality()**n, names='z') + P = L['t'] + emb = Hom(K, L)[0] + fext = P([emb(c) for c in f]) + + hext = P([emb(c) for c in h]) + if K.characteristic() == 2: - if n == 1: - # the base field - L = K - elif K.degree() == 1: - # extension of the prime field - L = GF(2**n, name='t') - else: - # general extension - # this might be costly, but everything here is anyway - # WARNING: - # 1. beware that if K is actually defined as a quotient of - # a polynomial ring, then polynomial_ring() will return - # will return the polynomial ring it is a quotient of, - # and not a polynomial ring on top of the quotient ring... - # IMHO this is a bug - # 2. if we rather used the PolynomialRing constructor, we would - # get the correct construction, but we then need to make sure - # we call trace enough below, e.g. with a while loop where we - # repeatedly apply trace() till the result lives in the prime - # field - # 3. for our application we don't really care as hyperelliptic - # curves defined over finite field represented as quotient rings - # won't end up in the HyperellipticCurve_finite_field class - # 4. and this does not work as a quotient ring of a polynomial - # ring over a finite field is not iterable, so the following - # for loop will fail at the moment - L = K.extension(K.polynomial_ring().irreducible_element(n)) for x in L: - s = f(x) - r = h(x) + s = fext(x) + r = hext(x) if r == 0: a += 1 - elif (s/r**2).trace().trace() == 0: - # currently extension of finite fields may be represented - # as quotient rings of polynomial rings, - # so for these we need: - # - one trace to go to the base field, - # - another trace to go to F_2. - # if L is actually represented directly as a finite field, - # the second trace does not hurt as tr(F_p) = id + elif (s/r**2).trace() == 0: a += 2 else: - if n == 1: - # L is the base field - L = K - elif K.degree() == 1: - # L is an extension of the prime field - L = GF(K.cardinality()**n, name='t') - else: - # there is no easy way to construct extensions of finite fields - # of odd characteristic in such a way that we can check for - # squares easily - raise NotImplementedError("Naive method only implemented over base field or extensions of prime fields in odd characteristic.") for x in L: - s = f(x) - r = h(x) + s = fext(x) + r = hext(x) d = r**2 + 4*s if d.is_zero(): a += 1 elif d.is_square(): a += 2 + return a def cardinality_hypellfrob(self, extension_degree=1, algorithm=None): @@ -1231,6 +1224,46 @@ def cardinality(self, extension_degree=1): # No smart method available return self.cardinality_exhaustive(n) + def zeta_function(self): + r""" + Gives the zeta function of the hyperelliptic curve. + + EXAMPLES:: + + sage: F = GF(2); R. = F[] + sage: H = HyperellipticCurve(t^9 + t, t^4) + sage: H.zeta_function() + (16*x^8 + 8*x^7 + 8*x^6 + 4*x^5 + 6*x^4 + 2*x^3 + 2*x^2 + x + 1)/(2*x^2 - 3*x + 1) + + sage: F. = GF(4); R. = F[] + sage: H = HyperellipticCurve(t^5 + t^3 + t^2 + t + 1, t^2 + t + 1) + sage: H.zeta_function() + (16*x^4 + 8*x^3 + x^2 + 2*x + 1)/(4*x^2 - 5*x + 1) + + sage: F. = GF(9); R. = F[] + sage: H = HyperellipticCurve(t^5 + a*t) + sage: H.zeta_function() + (81*x^4 + 72*x^3 + 32*x^2 + 8*x + 1)/(9*x^2 - 10*x + 1) + + sage: R. = PolynomialRing(GF(37)) + sage: H = HyperellipticCurve(t^5 + t + 2) + sage: H.zeta_function() + (1369*x^4 + 37*x^3 - 52*x^2 + x + 1)/(37*x^2 - 38*x + 1) + + A quadratic twist: + + :: + + sage: R. = PolynomialRing(GF(37)) + sage: H = HyperellipticCurve(2*t^5 + 2*t + 4) + sage: H.zeta_function() + (1369*x^4 - 37*x^3 - 52*x^2 - x + 1)/(37*x^2 - 38*x + 1) + """ + q = self.base_ring().cardinality() + P = self.frobenius_polynomial() + x = P.parent().gen(0) + return P.reverse() / ((1-x)*(1-q*x)) + #This where Cartier Matrix is actually computed. This is either called by E.Cartier_matrix, E.a_number, or E.Hasse_Witt @cached_method def _Cartier_matrix_cached(self): From 6fd33b240d2d7171068fff2d14ad367834c8fea7 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 8 Oct 2013 18:20:36 +0000 Subject: [PATCH 002/546] #15169: Fix FreeAlgebra element constructor from a base field. --- src/sage/algebras/free_algebra.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/sage/algebras/free_algebra.py b/src/sage/algebras/free_algebra.py index be36418284c..f96ac9692aa 100644 --- a/src/sage/algebras/free_algebra.py +++ b/src/sage/algebras/free_algebra.py @@ -586,6 +586,12 @@ def _element_constructor_(self, x): sage: L. = FreeAlgebra(K,3, implementation='letterplace') sage: F.1+(z+1)*L.2 b + (z+1)*c + + Check that :trac:`15169` is fixed:: + + sage: A. = FreeAlgebra(CC) + sage: A(2) + 2.00000000000000 """ if isinstance(x, FreeAlgebraElement): P = x.parent() @@ -621,8 +627,7 @@ def exp_to_monomial(T): x = R(x) if x == 0: return self.element_class(self,{}) - else: - return self.element_class(self,{F(1):x}) + return self.element_class(self,{self._basis_keys.one():x}) def _coerce_impl(self, x): """ From c1b5afea143f80ce6d68a4386ed6b410f8fcbb17 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 16 Oct 2013 16:51:48 +0000 Subject: [PATCH 003/546] #15289: Implemented indexed monoids and groups. --- src/doc/en/reference/misc/index.rst | 1 + src/sage/algebras/free_algebra.py | 24 +- .../algebras/steenrod/steenrod_algebra.py | 2 +- .../categories/graded_modules_with_basis.py | 4 +- src/sage/categories/modules_with_basis.py | 2 +- src/sage/combinat/combinatorial_algebra.py | 9 +- src/sage/combinat/free_module.py | 368 +------- src/sage/combinat/partition_algebra.py | 2 +- src/sage/combinat/sf/k_dual.py | 4 +- src/sage/combinat/sf/kschur.py | 2 +- src/sage/combinat/sf/new_kschur.py | 6 +- src/sage/combinat/symmetric_group_algebra.py | 4 +- src/sage/groups/all.py | 1 + src/sage/groups/indexed_group.py | 355 +++++++ src/sage/misc/indexed_generators.py | 399 ++++++++ src/sage/monoids/all.py | 2 +- src/sage/monoids/indexed_monoid.py | 867 ++++++++++++++++++ 17 files changed, 1696 insertions(+), 356 deletions(-) create mode 100644 src/sage/groups/indexed_group.py create mode 100644 src/sage/misc/indexed_generators.py create mode 100644 src/sage/monoids/indexed_monoid.py diff --git a/src/doc/en/reference/misc/index.rst b/src/doc/en/reference/misc/index.rst index 968ea302227..504788e66a6 100644 --- a/src/doc/en/reference/misc/index.rst +++ b/src/doc/en/reference/misc/index.rst @@ -53,6 +53,7 @@ Miscellaneous sage/misc/messaging sage/misc/viewer sage/misc/session + sage/misc/indexed_generators LaTeX ----- diff --git a/src/sage/algebras/free_algebra.py b/src/sage/algebras/free_algebra.py index f96ac9692aa..397e5e5da57 100644 --- a/src/sage/algebras/free_algebra.py +++ b/src/sage/algebras/free_algebra.py @@ -419,7 +419,7 @@ def __init__(self, R, n, names): raise TypeError("Argument R must be a ring.") self.__ngens = n #sage.structure.parent_gens.ParentWithGens.__init__(self, R, names) - self._basis_keys = FreeMonoid(n, names=names) + self._indices = FreeMonoid(n, names=names) Algebra.__init__(self, R, names, category=AlgebrasWithBasis(R)) def one_basis(self): @@ -434,7 +434,7 @@ def one_basis(self): sage: F.one_basis().parent() Free monoid on 2 generators (x, y) """ - return self._basis_keys.one() + return self._indices.one() # Needed for the category AlgebrasWithBasis (but not for Algebras) def term(self, index, coeff=None): @@ -603,7 +603,7 @@ def _element_constructor_(self, x): P = x.parent() if self.has_coerce_map_from(P): # letterplace versus generic ngens = P.ngens() - M = self._basis_keys + M = self._indices def exp_to_monomial(T): out = [] for i in xrange(len(T)): @@ -617,7 +617,7 @@ def exp_to_monomial(T): return sage_eval(x,locals=self.gens_dict()) R = self.base_ring() # coercion from free monoid - if isinstance(x, FreeMonoidElement) and x.parent() is self._basis_keys: + if isinstance(x, FreeMonoidElement) and x.parent() is self._indices: return self.element_class(self,{x:R(1)}) # coercion from the PBW basis if isinstance(x, PBWBasisOfFreeAlgebra.Element) \ @@ -627,7 +627,7 @@ def exp_to_monomial(T): x = R(x) if x == 0: return self.element_class(self,{}) - return self.element_class(self,{self._basis_keys.one():x}) + return self.element_class(self,{self._indices.one():x}) def _coerce_impl(self, x): """ @@ -732,7 +732,7 @@ def _coerce_impl(self, x): R = x.parent() # monoid - if R is self._basis_keys: + if R is self._indices: return self(x) # polynomial rings in the same variable over any base that coerces in: @@ -792,7 +792,7 @@ def _coerce_map_from_(self, R): b + (z+1)*c """ - if self._basis_keys.has_coerce_map_from(R): + if self._indices.has_coerce_map_from(R): return True # free algebras in the same variable over any base that coerces in: @@ -821,7 +821,7 @@ def gen(self,i): if i < 0 or not i < n: raise IndexError("Argument i (= %s) must be between 0 and %s."%(i, n-1)) R = self.base_ring() - F = self._basis_keys + F = self._indices return self.element_class(self,{F.gen(i):R(1)}) def quotient(self, mons, mats, names): @@ -876,7 +876,7 @@ def monoid(self): sage: F.monoid() Free monoid on 3 generators (x, y, z) """ - return self._basis_keys + return self._indices def g_algebra(self, relations, names=None, order='degrevlex', check = True): """ @@ -1035,7 +1035,7 @@ def lie_polynomial(self, w): """ if not w: return self.one() - M = self._basis_keys + M = self._indices if len(w) == 1: return self(M(w)) @@ -1258,7 +1258,7 @@ def one_basis(self): sage: PBW.one_basis().parent() Free monoid on 2 generators (x, y) """ - return self._basis_keys.one() + return self._indices.one() def algebra_generators(self): """ @@ -1272,7 +1272,7 @@ def algebra_generators(self): sage: all(g.parent() is PBW for g in gens) True """ - return tuple(self.monomial(x) for x in self._basis_keys.gens()) + return tuple(self.monomial(x) for x in self._indices.gens()) gens = algebra_generators diff --git a/src/sage/algebras/steenrod/steenrod_algebra.py b/src/sage/algebras/steenrod/steenrod_algebra.py index 2dee9c80c18..924b66ee835 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra.py +++ b/src/sage/algebras/steenrod/steenrod_algebra.py @@ -2194,7 +2194,7 @@ def basis(self, d=None): """ from sage.sets.family import Family if d is None: - return Family(self._basis_keys, self.monomial) + return Family(self._indices, self.monomial) else: return Family([self.monomial(tuple(a)) for a in self._basis_fcn(d)]) diff --git a/src/sage/categories/graded_modules_with_basis.py b/src/sage/categories/graded_modules_with_basis.py index 8a23060699e..e21f2cf040a 100644 --- a/src/sage/categories/graded_modules_with_basis.py +++ b/src/sage/categories/graded_modules_with_basis.py @@ -76,9 +76,9 @@ def basis(self, d=None): """ from sage.sets.family import Family if d is None: - return Family(self._basis_keys, self.monomial) + return Family(self._indices, self.monomial) else: - return Family(self._basis_keys.subset(size=d), self.monomial) + return Family(self._indices.subset(size=d), self.monomial) class ElementMethods: diff --git a/src/sage/categories/modules_with_basis.py b/src/sage/categories/modules_with_basis.py index 01bfbe589c3..031887700cc 100644 --- a/src/sage/categories/modules_with_basis.py +++ b/src/sage/categories/modules_with_basis.py @@ -198,7 +198,7 @@ def basis(self): [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] """ from sage.combinat.family import Family - return Family(self._basis_keys, self.monomial) + return Family(self._indices, self.monomial) def module_morphism(self, on_basis = None, diagonal = None, triangular = None, **keywords): r""" diff --git a/src/sage/combinat/combinatorial_algebra.py b/src/sage/combinat/combinatorial_algebra.py index e0c0758691e..c03ce567a5e 100644 --- a/src/sage/combinat/combinatorial_algebra.py +++ b/src/sage/combinat/combinatorial_algebra.py @@ -38,7 +38,7 @@ sage: ps(2) 2*p[] -The important things to define are ._basis_keys which +The important things to define are ._indices which specifies the combinatorial class that indexes the basis elements, ._one which specifies the identity element in the algebra, ._name which specifies the name of the algebra, .print_options is used to set @@ -192,8 +192,11 @@ def __init__(self, R, cc = None, element_class = None, category = None): category = AlgebrasWithBasis(R) # for backward compatibility - if cc is None and hasattr(self, "_basis_keys"): - cc = self._basis_keys + if cc is None: + if hasattr(self, "_basis_keys"): + cc = self._basis_keys + elif hasattr(self, "_indices"): + cc = self._indices assert(cc is not None) CombinatorialFreeModule.__init__(self, R, cc, element_class, category = category) diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index e4675469705..e6b2ae0e95b 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -29,6 +29,7 @@ from sage.combinat.dict_addition import dict_addition, dict_linear_combination from sage.sets.family import Family from sage.misc.ascii_art import AsciiArt, empty_ascii_art +from sage.misc.indexed_generators import IndexedGenerators # TODO: move the content of this class to CombinatorialFreeModule.Element and ModulesWithBasis.Element class CombinatorialFreeModuleElement(Element): @@ -554,7 +555,7 @@ def coefficient(self, m): """ # NT: coefficient_fast should be the default, just with appropriate assertions # that can be turned on or off - C = self.parent()._basis_keys + C = self.parent()._indices assert m in C, "%s should be an element of %s"%(m, C) if hasattr(C, "element_class") and not isinstance(m, C.element_class): m = C(m) @@ -946,7 +947,7 @@ def _divide_if_possible(x, y): else: return q -class CombinatorialFreeModule(UniqueRepresentation, Module): +class CombinatorialFreeModule(UniqueRepresentation, Module, IndexedGenerators): r""" Class for free modules with a named basis @@ -1189,7 +1190,7 @@ class CombinatorialFreeModule(UniqueRepresentation, Module): """ @staticmethod - def __classcall_private__(cls, base_ring, basis_keys, category = None, **keywords): + def __classcall_private__(cls, base_ring, basis_keys, category = None, prefix="B", **keywords): """ TESTS:: @@ -1231,11 +1232,11 @@ def __classcall_private__(cls, base_ring, basis_keys, category = None, **keyword latex_bracket = keywords.get('latex_bracket', None) if isinstance(latex_bracket, list): keywords['latex_bracket'] = tuple(latex_bracket) - return super(CombinatorialFreeModule, cls).__classcall__(cls, base_ring, basis_keys, category = category, **keywords) + return super(CombinatorialFreeModule, cls).__classcall__(cls, base_ring, basis_keys, category = category, prefix=prefix, **keywords) Element = CombinatorialFreeModuleElement - def __init__(self, R, basis_keys, element_class = None, category = None, **kwds): + def __init__(self, R, basis_keys, element_class = None, category = None, prefix="B", **kwds): r""" TESTS:: @@ -1259,7 +1260,7 @@ def __init__(self, R, basis_keys, element_class = None, category = None, **kwds) TESTS: - Regression test for #10127: ``self._basis_keys`` needs to be + Regression test for #10127: ``self._indices`` needs to be set early enough, in case the initialization of the categories use ``self.basis().keys()``. This occured on several occasions in non trivial constructions. In the following example, @@ -1301,38 +1302,39 @@ def __init__(self, R, basis_keys, element_class = None, category = None, **kwds) basis_keys = FiniteEnumeratedSet(basis_keys) if not hasattr(self, "_name"): self._name = "Free module generated by %s"%(basis_keys,) # note: cc may be a tuple! - self._basis_keys = basis_keys # Needs to be done early: #10127 + + # ignore the optional 'key' since it only affects CachedRepresentation + kwds.pop('key', None) + # This needs to be first as per #10127 + IndexedGenerators.__init__(self, basis_keys, prefix, **kwds) Parent.__init__(self, base = R, category = category, # Could we get rid of this? element_constructor = self._element_constructor_) - self._order = None - # printing options for elements (set when initializing self). - # This includes self._repr_option_bracket (kept for backwards - # compatibility, declared to be True by default, needs to be - # overridden explicitly). - self._print_options = {'prefix': "B", - 'bracket': None, - 'latex_bracket': False, - 'latex_prefix': None, - 'scalar_mult': "*", - 'latex_scalar_mult': None, - 'tensor_symbol': None, - 'monomial_cmp': cmp} - # 'bracket': its default value here is None, meaning that - # the value of self._repr_option_bracket is used; the default - # value of that attribute is True -- see immediately before - # the method _repr_term. If 'bracket' is any value - # except None, then it overrides the value of - # self._repr_option_bracket. Future users might consider - # using 'bracket' instead of _repr_option_bracket. + # For backwards compatibility + _repr_term = IndexedGenerators._repr_generator + _latex_term = IndexedGenerators._latex_generator - # ignore the optional 'key' since it only affects CachedRepresentation - kwds.pop('key', None) - self.print_options(**kwds) + def _ascii_art_term(self, m): + r""" + Return an ascii art representing the generator indexed by ``m``. + + TESTS:: + + sage: R = NonCommutativeSymmetricFunctions(QQ).R() + sage: ascii_art(R.one()) + 1 + """ + from sage.misc.ascii_art import AsciiArt + try: + if m == self.one_basis(): + return AsciiArt(["1"]) + except StandardError: + pass + return IndexedGenerators._ascii_art_generator(self, m) # mostly for backward compatibility @lazy_attribute @@ -1529,13 +1531,13 @@ def _element_constructor_(self, x): raise TypeError, "do not know how to make x (= %s) an element of %s"%(x, self) #x is an element of the basis enumerated set; # This is a very ugly way of testing this - elif ((hasattr(self._basis_keys, 'element_class') and - isinstance(self._basis_keys.element_class, type) and - isinstance(x, self._basis_keys.element_class)) - or (sage.structure.element.parent(x) == self._basis_keys)): + elif ((hasattr(self._indices, 'element_class') and + isinstance(self._indices.element_class, type) and + isinstance(x, self._indices.element_class)) + or (sage.structure.element.parent(x) == self._indices)): return self.monomial(x) - elif x in self._basis_keys: - return self.monomial(self._basis_keys(x)) + elif x in self._indices: + return self.monomial(self._indices(x)) else: if hasattr(self, '_coerce_end'): try: @@ -1580,7 +1582,7 @@ def combinatorial_class(self): """ from sage.misc.superseded import deprecation deprecation(6136, '"FM.combinatorial_class()" is deprecated. Use "F.basis().keys()" instead !') - return self._basis_keys + return self._indices def dimension(self): """ @@ -1603,7 +1605,7 @@ def dimension(self): sage: s.dimension() +Infinity """ - return self._basis_keys.cardinality() + return self._indices.cardinality() def gens(self): """ @@ -1699,294 +1701,6 @@ def from_vector(self, vector): cc = self.get_order() return self._from_dict(dict( (cc[index], coeff) for (index,coeff) in vector.iteritems())) - def prefix(self): - """ - Returns the prefix used when displaying elements of self. - - EXAMPLES:: - - sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c']) - sage: F.prefix() - 'B' - - :: - - sage: X = SchubertPolynomialRing(QQ) - sage: X.prefix() - 'X' - """ - return self._print_options['prefix'] - - def print_options(self, **kwds): - """ - Return the current print options, or set an option. - - INPUT: all of the input is optional; if present, it should be - in the form of keyword pairs, such as - ``latex_bracket='('``. The allowable keywords are: - - - ``prefix`` - - ``latex_prefix`` - - ``bracket`` - - ``latex_bracket`` - - ``scalar_mult`` - - ``latex_scalar_mult`` - - ``tensor_symbol`` - - ``monomial_cmp`` - - See the documentation for :class:`CombinatorialFreeModule` for - descriptions of the effects of setting each of these options. - - OUTPUT: if the user provides any input, set the appropriate - option(s) and return nothing. Otherwise, return the - dictionary of settings for print and LaTeX representations. - - EXAMPLES:: - - sage: F = CombinatorialFreeModule(ZZ, [1,2,3], prefix='x') - sage: F.print_options() - {...'prefix': 'x'...} - sage: F.print_options(bracket='(') - sage: F.print_options() - {...'bracket': '('...} - - TESTS:: - - sage: sorted(F.print_options().items()) - [('bracket', '('), ('latex_bracket', False), ('latex_prefix', None), ('latex_scalar_mult', None), ('monomial_cmp', ), ('prefix', 'x'), ('scalar_mult', '*'), ('tensor_symbol', None)] - sage: F.print_options(bracket='[') # reset - """ - # don't just use kwds.get(...) because I want to distinguish - # between an argument like "option=None" and the option not - # being there altogether. - if kwds: - for option in kwds: - if option in ['prefix', 'latex_prefix', 'bracket', 'latex_bracket', - 'scalar_mult', 'latex_scalar_mult', 'tensor_symbol', - 'monomial_cmp' - ]: - self._print_options[option] = kwds[option] - else: - raise ValueError, '%s is not a valid print option.' % option - else: - return self._print_options - - _repr_option_bracket = True - - def _repr_term(self, m): - """ - Returns a string representing the basis element indexed by m. - - The output can be customized by setting any of the following - options when initializing the module: - - - prefix - - bracket - - scalar_mult - - Alternatively, one can use the :meth:`print_options` method - to achieve the same effect. To modify the bracket setting, - one can also set ``self._repr_option_bracket`` as long as one - has *not* set the ``bracket`` option: if the - ``bracket`` option is anything but ``None``, it overrides - the value of ``self._repr_option_bracket``. - - See the documentation for :class:`CombinatorialFreeModule` for - details on the initialization options. - - .. todo:: rename to ``_repr_monomial`` - - EXAMPLES:: - - sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c']) - sage: e = F.basis() - sage: e['a'] + 2*e['b'] # indirect doctest - B['a'] + 2*B['b'] - - sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix="F") - sage: e = F.basis() - sage: e['a'] + 2*e['b'] # indirect doctest - F['a'] + 2*F['b'] - - sage: QS3 = CombinatorialFreeModule(QQ, Permutations(3), prefix="") - sage: original_print_options = QS3.print_options() - sage: a = 2*QS3([1,2,3])+4*QS3([3,2,1]) - sage: a # indirect doctest - 2*[[1, 2, 3]] + 4*[[3, 2, 1]] - - sage: QS3.print_options(bracket = False) - sage: a # indirect doctest - 2*[1, 2, 3] + 4*[3, 2, 1] - - sage: QS3.print_options(prefix='') - sage: a # indirect doctest - 2*[1, 2, 3] + 4*[3, 2, 1] - - sage: QS3.print_options(bracket="|", scalar_mult=" *@* ") - sage: a # indirect doctest - 2 *@* |[1, 2, 3]| + 4 *@* |[3, 2, 1]| - - sage: QS3.print_options(**original_print_options) # reset - - TESTS:: - - sage: F = CombinatorialFreeModule(QQ, [('a', 'b'), ('c','d')]) - sage: e = F.basis() - sage: e[('a','b')] + 2*e[('c','d')] # indirect doctest - B[('a', 'b')] + 2*B[('c', 'd')] - """ - bracket = self._print_options.get('bracket', None) - bracket_d = {"{": "}", "[": "]", "(": ")"} - if bracket is None: - bracket = self._repr_option_bracket - if bracket is True: - left = "[" - right = "]" - elif bracket is False: - left = "" - right = "" - elif isinstance(bracket, (tuple, list)): - left = bracket[0] - right = bracket[1] - elif bracket in bracket_d: - left = bracket - right = bracket_d[bracket] - else: - left = bracket - right = bracket - return self.prefix() + left + repr(m) + right # mind the (m), to accept a tuple for m - - def _ascii_art_term(self, el): - r""" - Return an ascii art representing of the term. - - TESTS:: - - sage: R = NonCommutativeSymmetricFunctions(QQ).R() - sage: ascii_art(R[1,2,2,4]) - R - **** - ** - ** - * - sage: Partitions.global_options(diagram_str="#", convention="french") - sage: ascii_art(R[1,2,2,4]) - R - # - ## - ## - #### - """ - from sage.misc.ascii_art import ascii_art - try: - if el == self.one_basis(): - return AsciiArt(["1"]) - except StandardError: - pass - pref = AsciiArt([self.prefix()]) - r = pref * (AsciiArt([" "**Integer(len(pref))]) + ascii_art(el)) - r._baseline = r._h - 1 - return r - - def _latex_term(self, m): - r""" - Returns a string for the LaTeX code for the basis element - indexed by m. - - The output can be customized by setting any of the following - options when initializing the module: - - - prefix - - latex_prefix - - latex_bracket - - (Alternatively, one can use the :meth:`print_options` method - to achieve the same effect.) - - See the documentation for :class:`CombinatorialFreeModule` for - details on the initialization options. - - .. todo:: rename to ``_latex_monomial`` - - EXAMPLES:: - - sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c']) - sage: e = F.basis() - sage: latex(e['a'] + 2*e['b']) # indirect doctest - B_{a} + 2B_{b} - - sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix="C") - sage: e = F.basis() - sage: latex(e['a'] + 2*e['b']) # indirect doctest - C_{a} + 2C_{b} - - sage: QS3 = CombinatorialFreeModule(QQ, Permutations(3), prefix="", scalar_mult="*") - sage: original_print_options = QS3.print_options() - sage: a = 2*QS3([1,2,3])+4*QS3([3,2,1]) - sage: latex(a) # indirect doctest - 2[1, 2, 3] + 4[3, 2, 1] - sage: QS3.print_options(latex_bracket=True) - sage: latex(a) # indirect doctest - 2\left[ [1, 2, 3] \right] + 4\left[ [3, 2, 1] \right] - sage: QS3.print_options(latex_bracket="(") - sage: latex(a) # indirect doctest - 2\left( [1, 2, 3] \right) + 4\left( [3, 2, 1] \right) - sage: QS3.print_options(latex_bracket=('\\myleftbracket', '\\myrightbracket')) - sage: latex(a) # indirect doctest - 2\myleftbracket [1, 2, 3] \myrightbracket + 4\myleftbracket [3, 2, 1] \myrightbracket - sage: QS3.print_options(**original_print_options) # reset - - TESTS:: - - sage: F = CombinatorialFreeModule(QQ, [('a', 'b'), (0,1,2)]) - sage: e = F.basis() - sage: latex(e[('a','b')]) # indirect doctest - B_{('a', 'b')} - sage: latex(2*e[(0,1,2)]) # indirect doctest - 2B_{\left(0, 1, 2\right)} - sage: F = CombinatorialFreeModule(QQ, [('a', 'b'), (0,1,2)], prefix="") - sage: e = F.basis() - sage: latex(2*e[(0,1,2)]) # indirect doctest - 2\left(0, 1, 2\right) - """ - from sage.misc.latex import latex - - s = latex(m) - if s.find('\\text{\\textt') != -1: - # m contains "non-LaTeXed" strings, use string representation - s = str(m) - - # dictionary with left-right pairs of "brackets". put pairs - # in here accept \\left and \\right as prefixes. - bracket_d = {"{": "\\}", "[": "]", "(": ")", "\\{": "\\}", - "|": "|", "||": "||"} - bracket = self._print_options.get('latex_bracket', False) - if bracket is True: - left = "\\left[" - right = "\\right]" - elif bracket is False: - left = "" - right = "" - elif isinstance(bracket, (tuple, list)): - left = bracket[0] - right = bracket[1] - elif bracket in bracket_d: - left = bracket - right = bracket_d[bracket] - if left == "{": - left = "\\{" - left = "\\left" + left - right = "\\right" + right - else: - left = bracket - right = bracket - prefix = self._print_options.get('latex_prefix') - if prefix is None: - prefix = self._print_options.get('prefix') - if prefix == "": - return left + s + right - return "%s_{%s}" % (prefix, s) - def __cmp__(self, other): """ EXAMPLES:: @@ -2193,7 +1907,7 @@ def monomial(self): Term map from {'a', 'b', 'c'} to Free module generated by {'a', 'b', 'c'} over Rational Field """ # Should use a real Map, as soon as combinatorial_classes are enumerated sets, and therefore parents - return PoorManMap(self._monomial, domain = self._basis_keys, codomain = self, name = "Term map") + return PoorManMap(self._monomial, domain = self._indices, codomain = self, name = "Term map") def _sum_of_monomials(self, indices): """ diff --git a/src/sage/combinat/partition_algebra.py b/src/sage/combinat/partition_algebra.py index 3275d7f9e46..556fd51517a 100644 --- a/src/sage/combinat/partition_algebra.py +++ b/src/sage/combinat/partition_algebra.py @@ -1419,7 +1419,7 @@ def __init__(self, R, cclass, n, k, name=None, prefix=None): """ self.k = k self.n = n - self._basis_keys = cclass + self._indices = cclass self._name = "Generic partition algebra with k = %s and n = %s and basis %s"%( self.k, self.n, cclass) if name is None else name self._one = identity(ceil(self.k)) self._prefix = "" if prefix is None else prefix diff --git a/src/sage/combinat/sf/k_dual.py b/src/sage/combinat/sf/k_dual.py index 0df664fa70c..12086caf313 100644 --- a/src/sage/combinat/sf/k_dual.py +++ b/src/sage/combinat/sf/k_dual.py @@ -551,8 +551,8 @@ def _element_constructor_(self, x): else: raise TypeError("do not know how to make x (= %s) an element of %s"%(x, self)) #x is an element of the basis enumerated set; - elif x in self._basis_keys: - return self.monomial(self._basis_keys(x)) + elif x in self._indices: + return self.monomial(self._indices(x)) raise TypeError("do not know how to make x (= %s) an element of self (=%s)"%(x,self)) def ambient(self): diff --git a/src/sage/combinat/sf/kschur.py b/src/sage/combinat/sf/kschur.py index 857a3bcda0e..4277bd35364 100644 --- a/src/sage/combinat/sf/kschur.py +++ b/src/sage/combinat/sf/kschur.py @@ -243,7 +243,7 @@ def __init__(self, R, k, t=None): Sym = SymmetricFunctions(R) sfa.SymmetricFunctionAlgebra_generic.__init__(self, Sym) # so we need to take some counter measures - self._basis_keys = sage.combinat.partition.Partitions(max_part=k) + self._indices = sage.combinat.partition.Partitions(max_part=k) # The following line is just a temporary workaround to keep # the repr of those k-schur as they were before #13404; since # they are deprecated, there is no need to bother about them. diff --git a/src/sage/combinat/sf/new_kschur.py b/src/sage/combinat/sf/new_kschur.py index 2d7399e4dbe..cb9dfb0ce2d 100644 --- a/src/sage/combinat/sf/new_kschur.py +++ b/src/sage/combinat/sf/new_kschur.py @@ -327,8 +327,8 @@ def _element_constructor_(self, x): else: raise TypeError("do not know how to make x (= %s) an element of %s"%(x, self)) #x is an element of the basis enumerated set; - elif x in self._basis_keys: - return self.monomial(self._basis_keys(x)) + elif x in self._indices: + return self.monomial(self._indices(x)) raise TypeError("do not know how to make x (= %s) an element of self (=%s)"%(x,self)) def _convert_map_from_(self,Q): @@ -388,7 +388,7 @@ def __getitem__(self, c, *rest): else: c = Partition(list(c)) - if c not in self._basis_keys: + if c not in self._indices: raise TypeError("do not know how to make %s an element of %s"%(c,self)) return self.monomial(c) diff --git a/src/sage/combinat/symmetric_group_algebra.py b/src/sage/combinat/symmetric_group_algebra.py index a3937f9cbdb..b1bed8435cf 100644 --- a/src/sage/combinat/symmetric_group_algebra.py +++ b/src/sage/combinat/symmetric_group_algebra.py @@ -326,7 +326,7 @@ def algebra_generators(self): a[1] = 1 b = range(2, self.n+2) b[self.n-1] = 1 - return [self.monomial(self._basis_keys(a)), self.monomial(self._basis_keys(b))] + return [self.monomial(self._indices(a)), self.monomial(self._indices(b))] def _conjugacy_classes_representatives_underlying_group(self): r""" @@ -943,7 +943,7 @@ def __init__(self, R, n, q=None): Hecke algebra of the symmetric group of order 3 with q=1 on the T basis over Rational Field """ self.n = n - self._basis_keys = permutation.Permutations(n) + self._indices = permutation.Permutations(n) self._name = "Hecke algebra of the symmetric group of order %s"%self.n self._one = permutation.Permutation(range(1,n+1)) diff --git a/src/sage/groups/all.py b/src/sage/groups/all.py index 8f473c530e5..69520ad4e69 100644 --- a/src/sage/groups/all.py +++ b/src/sage/groups/all.py @@ -17,6 +17,7 @@ lazy_import('sage.groups.free_group', 'FreeGroup') lazy_import('sage.groups.braid', 'BraidGroup') +lazy_import('sage.groups.indexed_group', ['IndexedFreeGroup', 'IndexedFreeAbelianGroup']) lazy_import('sage.groups.affine_gps.affine_group', 'AffineGroup') lazy_import('sage.groups.affine_gps.euclidean_group', 'EuclideanGroup') diff --git a/src/sage/groups/indexed_group.py b/src/sage/groups/indexed_group.py new file mode 100644 index 00000000000..2d383ffdbe3 --- /dev/null +++ b/src/sage/groups/indexed_group.py @@ -0,0 +1,355 @@ +""" +Indexed Free Groups + +Free groups and free abelian groups implemented using an indexed set of +generators. + +AUTHORS: + +- Travis Scrimshaw (2013-10-16): Initial version +""" + +############################################################################## +# Copyright (C) 2013 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# The full text of the GPL is available at: +# +# http://www.gnu.org/licenses/ +############################################################################## + +from copy import copy +from sage.categories.groups import Groups +from sage.groups.group import Group, AbelianGroup +from sage.monoids.indexed_monoid import IndexedFreeMonoid, IndexedFreeAbelianMonoid +from sage.misc.cachefunc import cached_method +from sage.rings.integer import Integer +from sage.rings.infinity import infinity + +class IndexedFreeGroup(IndexedFreeMonoid, Group): + """ + An indexed free group. + + EXAMPLES:: + + sage: G = IndexedFreeGroup(ZZ) + sage: G + Free group indexed by Integer Ring + sage: G = IndexedFreeGroup('abcde') + sage: G + Free group indexed by {'a', 'b', 'c', 'd', 'e'} + """ + def __init__(self, indices, prefix, category=None, **kwds): + """ + Initialize ``self``. + + TESTS:: + + sage: G = IndexedFreeGroup(ZZ) + sage: TestSuite(G).run() + sage: G = IndexedFreeGroup('abc') + sage: TestSuite(G).run() + """ + Group.__init__(self) + category = Groups().or_subcategory(category) + IndexedFreeMonoid.__init__(self, indices, prefix, category, **kwds) + + def _repr_(self): + """ + TESTS:: + + sage: IndexedFreeGroup(ZZ) + Free group indexed by Integer Ring + """ + return 'Free group indexed by {}'.format(self._indices) + + def __len__(self): + """ + Return the length of ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeGroup(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: elt = a*c^-3*b^-2*a + sage: elt.length() + 7 + sage: len(elt) + 7 + + sage: F = IndexedFreeAbelianGroup(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: elt = a*c^-3*b^-2*a + sage: elt.length() + 7 + sage: len(elt) + 7 + """ + return sum(abs(exp) for gen,exp in self._sorted_items()) + + length = __len__ + + def order(self): + r""" + Return the number of elements of ``self``, which is `\infty` unless + this is the trivial group. + + EXAMPLES:: + + sage: G = IndexedFreeGroup(ZZ) + sage: G.order() + +Infinity + sage: G = IndexedFreeGroup('abc') + sage: G.order() + +Infinity + sage: G = IndexedFreeGroup([]) + sage: G.order() + 1 + """ + if self.ngens() == 0: + return Integer(1) + return infinity + + def is_finite(self): + """ + Return ``True`` if ``self`` is finite. + + EXAMPLES:: + + sage: G = IndexedFreeGroup(ZZ) + sage: G.is_finite() + False + sage: G = IndexedFreeGroup('abc') + sage: G.is_finite() + False + sage: G = IndexedFreeGroup([]) + sage: G.is_finite() + True + """ + return self.ngens() == 0 + + rank = IndexedFreeMonoid.ngens + + class Element(IndexedFreeMonoid.Element): + def _mul_(self, y): + """ + Multiply ``self`` by ``y``. + + EXAMPLES:: + + sage: F = IndexedFreeGroup(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: a*b^2*e*d + F[0]*F[1]^2*F[4]*F[3] + sage: (a*b^2*d^2) * (d^-4*b*e) + F[0]*F[1]^2*F[3]^-2*F[1]*F[4] + sage: (a*b^-2*d^2) * (d^-2*b^2*a^-1) + 1 + """ + if len(self._monomial) == 0: + return y + if len(y._monomial) == 0: + return self + + ret = list(self._monomial) + rhs = list(y._monomial) + while len(ret) > 0 and len(rhs) > 0 and ret[-1][0] == rhs[0][0]: + rhs[0] = (rhs[0][0], rhs[0][1] + ret.pop()[1]) + if rhs[0][1] == 0: + rhs.pop(0) + ret += rhs + return self.__class__(self.parent(), tuple(ret)) + + def __invert__(self): + """ + Return the inverse of ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeGroup(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: x = a*b^2*e^-1*d; ~x + F[3]^-1*F[4]*F[1]^-2*F[0]^-1 + sage: x * ~x + 1 + """ + return self.__class__(self.parent(), tuple((x[0], -x[1]) for x in reversed(self._monomial))) + + def __pow__(self, n): + """ + Raise ``self`` to the power of ``n``. + + EXAMPLES:: + + sage: F = IndexedFreeGroup(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: x = a*b^2*e*a^-1; x + F[0]*F[1]^2*F[4]*F[0]^-1 + sage: x^3 + F[0]*F[1]^2*F[4]*F[1]^2*F[4]*F[1]^2*F[4]*F[0]^-1 + sage: x^0 + 1 + sage: x^-3 + F[0]*F[4]^-1*F[1]^-2*F[4]^-1*F[1]^-2*F[4]^-1*F[1]^-2*F[0]^-1 + """ + if not isinstance(n, (int, long, Integer)): + raise TypeError("Argument n (= {}) must be an integer".format(n)) + if n == 0: + return self.parent().one() + if n == 1: + return self + if n == -1: + return ~self + if len(self._monomial) == 1: + gen,exp = self._monomial[0] + return self.__class__(self.parent(), ((gen, exp*n),)) + if n < 0: + self = ~self + n = -n + ret = self + for i in range(n-1): + ret *= self + return ret + +class IndexedFreeAbelianGroup(IndexedFreeAbelianMonoid, AbelianGroup): + """ + An indexed free abelian group. + + EXAMPLES:: + + sage: G = IndexedFreeAbelianGroup(ZZ) + sage: G + Free abelian group indexed by Integer Ring + sage: G = IndexedFreeAbelianGroup('abcde') + sage: G + Free abelian group indexed by {'a', 'b', 'c', 'd', 'e'} + """ + def __init__(self, indices, prefix, category=None, **kwds): + """ + Initialize ``self``. + + TESTS:: + + sage: G = IndexedFreeAbelianGroup(ZZ) + sage: TestSuite(G).run() + sage: G = IndexedFreeAbelianGroup('abc') + sage: TestSuite(G).run() + """ + AbelianGroup.__init__(self) + category = Groups().or_subcategory(category) + IndexedFreeAbelianMonoid.__init__(self, indices, prefix, category, **kwds) + + def _repr_(self): + """ + TESTS:: + + sage: IndexedFreeAbelianGroup(ZZ) + Free abelian group indexed by Integer Ring + """ + return 'Free abelian group indexed by {}'.format(self._indices) + + def order(self): + r""" + Return the number of elements of ``self``, which is `\infty` unless + this is the trivial group. + + EXAMPLES:: + + sage: G = IndexedFreeAbelianGroup(ZZ) + sage: G.order() + +Infinity + sage: G = IndexedFreeAbelianGroup('abc') + sage: G.order() + +Infinity + sage: G = IndexedFreeAbelianGroup([]) + sage: G.order() + 1 + """ + if self.ngens() == 0: + return Integer(1) + return infinity + + def is_finite(self): + """ + Return ``True`` if ``self`` is finite. + + EXAMPLES:: + + sage: G = IndexedFreeAbelianGroup(ZZ) + sage: G.is_finite() + False + sage: G = IndexedFreeAbelianGroup('abc') + sage: G.is_finite() + False + sage: G = IndexedFreeAbelianGroup([]) + sage: G.is_finite() + True + """ + return self.ngens() == 0 + + rank = IndexedFreeAbelianMonoid.ngens + + class Element(IndexedFreeAbelianMonoid.Element): + def _mul_(self, y): + """ + Multiply ``self`` by ``y``. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianGroup(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: a*b^2*e^-1*d + F[0]*F[1]^2*F[3]*F[4]^-1 + sage: (a*b^2*d^2) * (d^-4*b^-2*e) + F[0]*F[3]^-2*F[4] + sage: (a*b^-2*d^2) * (d^-2*b^2*a^-1) + 1 + """ + ret = copy(self._monomial) + for k,v in y._monomial.iteritems(): + ret[k] = ret.get(k, 0) + v + if ret[k] == 0: + del ret[k] + return self.__class__(self.parent(), ret) + + def __invert__(self): + """ + Return the inverse of ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianGroup(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: x = a*b^2*e^-1*d; ~x + F[0]^-1*F[1]^-2*F[3]^-1*F[4] + sage: x * ~x + 1 + """ + return self.__pow__(-1) + + def __pow__(self, n): + """ + Raise ``self`` to the power of ``n``. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianGroup(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: x = a*b^2*e^-1*d; x + F[0]*F[1]^2*F[3]*F[4]^-1 + sage: x^3 + F[0]^3*F[1]^6*F[3]^3*F[4]^-3 + sage: x^0 + 1 + sage: x^-3 + F[0]^-3*F[1]^-6*F[3]^-3*F[4]^3 + """ + if not isinstance(n, (int, long, Integer)): + raise TypeError("Argument n (= {}) must be an integer".format(n)) + if n == 1: + return self + if n == 0: + return self.parent().one() + return self.__class__(self.parent(), {k:v*n for k,v in self._monomial.iteritems()}) + diff --git a/src/sage/misc/indexed_generators.py b/src/sage/misc/indexed_generators.py new file mode 100644 index 00000000000..db154448489 --- /dev/null +++ b/src/sage/misc/indexed_generators.py @@ -0,0 +1,399 @@ +""" +Indexed Generators +""" +#***************************************************************************** +# Copyright (C) 2013 Travis Scrimshaw , +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.rings.all import Integer + +class IndexedGenerators: + r""" + Abstract base class for parents whose elements consist of generators + indexed by an arbitrary set. + + Options controlling the printing of elements: + + - ``prefix`` -- string, prefix used for printing elements of this + module (optional, default 'x'). With the default, a monomial + indexed by 'a' would be printed as ``x['a']``. + + - ``latex_prefix`` -- string or ``None``, prefix used in the `\LaTeX` + representation of elements (optional, default ``None``). If this is + anything except the empty string, it prints the index as a + subscript. If this is None, it uses the setting for ``prefix``, + so if ``prefix`` is set to "B", then a monomial indexed by 'a' + would be printed as ``B_{a}``. If this is the empty string, then + don't print monomials as subscripts: the monomial indexed by 'a' + would be printed as ``a``, or as ``[a]`` if ``latex_bracket`` is + True. + + - ``bracket`` -- ``None``, bool, string, or list or tuple of + strings (optional, default None): if ``None``, use the value of the + attribute ``self._repr_option_bracket``, which has default value + True. (``self._repr_option_bracket`` is available for backwards + compatibility. Users should set ``bracket`` instead. If + ``bracket`` is set to anything except None, it overrides + the value of ``self._repr_option_bracket``.) If False, do not + include brackets when printing elements: a monomial indexed by + 'a' would be printed as ``B'a'``, and a monomial indexed by + (1,2,3) would be printed as ``B(1,2,3)``. If True, use "[" and + "]" as brackets. If it is one of "[", "(", or "{", use it and + its partner as brackets. If it is any other string, use it as + both brackets. If it is a list or tuple of strings, use the + first entry as the left bracket and the second entry as the + right bracket. + + - ``latex_bracket`` -- bool, string, or list or tuple of strings + (optional, default False): if ``False``, do not include brackets in + the LaTeX representation of elements. This option is only + relevant if ``latex_prefix`` is the empty string; otherwise, + brackets are not used regardless. If ``True``, use "\left[" and + "\right]" as brackets. If this is one of "[", "(", "\\{", "|", + or "||", use it and its partner, prepended with "\left" and + "\right", as brackets. If this is any other string, use it as + both brackets. If this is a list or tuple of strings, use the + first entry as the left bracket and the second entry as the + right bracket. + + - ``scalar_mult`` -- string to use for scalar multiplication in + the print representation (optional, default "*") + + - ``latex_scalar_mult`` -- string or ``None`` (optional, default ``None``), + string to use for scalar multiplication in the latex + representation. If None, use the empty string if ``scalar_mult`` + is set to "*", otherwise use the value of ``scalar_mult``. + + - ``tensor_symbol`` -- string or ``None`` (optional, default ``None``), + string to use for tensor product in the print representation. If + None, use the ``sage.categories.tensor.symbol``. + + - ``monomial_cmp`` -- a comparison function (optional, default ``cmp``), + to use for sorting elements in the output of elements + + .. NOTE:: + + These print options may also be accessed and modified using the + :meth:`print_options` method, after the parent has been defined. + """ + def __init__(self, indices, prefix="x", **kwds): + """ + Initialize ``self``. + """ + self._indices = indices + + # printing options for elements (set when initializing self). + # This includes self._repr_option_bracket (kept for backwards + # compatibility, declared to be True by default, needs to be + # overridden explicitly). + self._print_options = {'prefix': prefix, + 'bracket': None, + 'latex_bracket': False, + 'latex_prefix': None, + 'scalar_mult': "*", + 'latex_scalar_mult': None, + 'tensor_symbol': None, + 'monomial_cmp': cmp} + # 'bracket': its default value here is None, meaning that + # the value of self._repr_option_bracket is used; the default + # value of that attribute is True -- see immediately before + # the method _repr_term. If 'bracket' is any value + # except None, then it overrides the value of + # self._repr_option_bracket. Future users might consider + # using 'bracket' instead of _repr_option_bracket. + self.print_options(**kwds) + + def indices(self): + """ + Return the indices of ``self``. + + EXAMPLES:: + + sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c']) + sage: F.indices() + {'a', 'b', 'c'} + """ + return self._indices + + def prefix(self): + """ + Return the prefix used when displaying elements of self. + + EXAMPLES:: + + sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c']) + sage: F.prefix() + 'B' + + :: + + sage: X = SchubertPolynomialRing(QQ) + sage: X.prefix() + 'X' + """ + return self._print_options['prefix'] + + def print_options(self, **kwds): + """ + Return the current print options, or set an option. + + INPUT: all of the input is optional; if present, it should be + in the form of keyword pairs, such as + ``latex_bracket='('``. The allowable keywords are: + + - ``prefix`` + - ``latex_prefix`` + - ``bracket`` + - ``latex_bracket`` + - ``scalar_mult`` + - ``latex_scalar_mult`` + - ``tensor_symbol`` + - ``monomial_cmp`` + + See the documentation for :class:`CombinatorialFreeModule` for + descriptions of the effects of setting each of these options. + + OUTPUT: if the user provides any input, set the appropriate + option(s) and return nothing. Otherwise, return the + dictionary of settings for print and LaTeX representations. + + EXAMPLES:: + + sage: F = CombinatorialFreeModule(ZZ, [1,2,3], prefix='x') + sage: F.print_options() + {...'prefix': 'x'...} + sage: F.print_options(bracket='(') + sage: F.print_options() + {...'bracket': '('...} + + TESTS:: + + sage: sorted(F.print_options().items()) + [('bracket', '('), ('latex_bracket', False), ('latex_prefix', None), ('latex_scalar_mult', None), ('monomial_cmp', ), ('prefix', 'x'), ('scalar_mult', '*'), ('tensor_symbol', None)] + sage: F.print_options(bracket='[') # reset + """ + # don't just use kwds.get(...) because I want to distinguish + # between an argument like "option=None" and the option not + # being there altogether. + if kwds: + for option in kwds: + if option in ['prefix', 'latex_prefix', 'bracket', 'latex_bracket', + 'scalar_mult', 'latex_scalar_mult', 'tensor_symbol', + 'monomial_cmp' + ]: + self._print_options[option] = kwds[option] + else: + raise ValueError('{} is not a valid print option.'.format(option)) + return + return self._print_options + + _repr_option_bracket = True + + def _repr_generator(self, m): + """ + Return a string representing the generator indexed by ``m``. + + The output can be customized by setting any of the following + options when initializing the module: + + - ``prefix`` + - ``bracket`` + - ``scalar_mult`` + + Alternatively, one can use the :meth:`print_options` method + to achieve the same effect. To modify the bracket setting, + one can also set ``self._repr_option_bracket`` as long as one + has *not* set the ``bracket`` option: if the + ``bracket`` option is anything but ``None``, it overrides + the value of ``self._repr_option_bracket``. + + See the documentation for :class:`CombinatorialFreeModule` for + details on the initialization options. + + EXAMPLES:: + + sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c']) + sage: e = F.basis() + sage: e['a'] + 2*e['b'] # indirect doctest + B['a'] + 2*B['b'] + + sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix="F") + sage: e = F.basis() + sage: e['a'] + 2*e['b'] # indirect doctest + F['a'] + 2*F['b'] + + sage: QS3 = CombinatorialFreeModule(QQ, Permutations(3), prefix="") + sage: original_print_options = QS3.print_options() + sage: a = 2*QS3([1,2,3])+4*QS3([3,2,1]) + sage: a # indirect doctest + 2*[[1, 2, 3]] + 4*[[3, 2, 1]] + + sage: QS3.print_options(bracket = False) + sage: a # indirect doctest + 2*[1, 2, 3] + 4*[3, 2, 1] + + sage: QS3.print_options(prefix='') + sage: a # indirect doctest + 2*[1, 2, 3] + 4*[3, 2, 1] + + sage: QS3.print_options(bracket="|", scalar_mult=" *@* ") + sage: a # indirect doctest + 2 *@* |[1, 2, 3]| + 4 *@* |[3, 2, 1]| + + sage: QS3.print_options(**original_print_options) # reset + + TESTS:: + + sage: F = CombinatorialFreeModule(QQ, [('a', 'b'), ('c','d')]) + sage: e = F.basis() + sage: e[('a','b')] + 2*e[('c','d')] # indirect doctest + B[('a', 'b')] + 2*B[('c', 'd')] + """ + bracket = self._print_options.get('bracket', None) + bracket_d = {"{": "}", "[": "]", "(": ")"} + if bracket is None: + bracket = self._repr_option_bracket + if bracket is True: + left = "[" + right = "]" + elif bracket is False: + left = "" + right = "" + elif isinstance(bracket, (tuple, list)): + left = bracket[0] + right = bracket[1] + elif bracket in bracket_d: + left = bracket + right = bracket_d[bracket] + else: + left = bracket + right = bracket + return self.prefix() + left + repr(m) + right # mind the (m), to accept a tuple for m + + def _ascii_art_generator(self, m): + r""" + Return an ascii art representing the generator indexed by ``m``. + + TESTS:: + + sage: R = NonCommutativeSymmetricFunctions(QQ).R() + sage: ascii_art(R[1,2,2,4]) + R + **** + ** + ** + * + sage: Partitions.global_options(diagram_str="#", convention="french") + sage: ascii_art(R[1,2,2,4]) + R + # + ## + ## + #### + """ + from sage.misc.ascii_art import AsciiArt, ascii_art + pref = AsciiArt([self.prefix()]) + r = pref * (AsciiArt([" "**Integer(len(pref))]) + ascii_art(m)) + r._baseline = r._h - 1 + return r + + def _latex_generator(self, m): + r""" + Return a string for the `\LaTeX` code for the generator + indexed by ``m``. + + The output can be customized by setting any of the following + options when initializing the module: + + - ``prefix`` + - ``latex_prefix`` + - ``latex_bracket`` + + (Alternatively, one can use the :meth:`print_options` method + to achieve the same effect.) + + See the documentation for :class:`CombinatorialFreeModule` for + details on the initialization options. + + EXAMPLES:: + + sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c']) + sage: e = F.basis() + sage: latex(e['a'] + 2*e['b']) # indirect doctest + B_{a} + 2B_{b} + + sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix="C") + sage: e = F.basis() + sage: latex(e['a'] + 2*e['b']) # indirect doctest + C_{a} + 2C_{b} + + sage: QS3 = CombinatorialFreeModule(QQ, Permutations(3), prefix="", scalar_mult="*") + sage: original_print_options = QS3.print_options() + sage: a = 2*QS3([1,2,3])+4*QS3([3,2,1]) + sage: latex(a) # indirect doctest + 2[1, 2, 3] + 4[3, 2, 1] + sage: QS3.print_options(latex_bracket=True) + sage: latex(a) # indirect doctest + 2\left[ [1, 2, 3] \right] + 4\left[ [3, 2, 1] \right] + sage: QS3.print_options(latex_bracket="(") + sage: latex(a) # indirect doctest + 2\left( [1, 2, 3] \right) + 4\left( [3, 2, 1] \right) + sage: QS3.print_options(latex_bracket=('\\myleftbracket', '\\myrightbracket')) + sage: latex(a) # indirect doctest + 2\myleftbracket [1, 2, 3] \myrightbracket + 4\myleftbracket [3, 2, 1] \myrightbracket + sage: QS3.print_options(**original_print_options) # reset + + TESTS:: + + sage: F = CombinatorialFreeModule(QQ, [('a', 'b'), (0,1,2)]) + sage: e = F.basis() + sage: latex(e[('a','b')]) # indirect doctest + B_{('a', 'b')} + sage: latex(2*e[(0,1,2)]) # indirect doctest + 2B_{\left(0, 1, 2\right)} + sage: F = CombinatorialFreeModule(QQ, [('a', 'b'), (0,1,2)], prefix="") + sage: e = F.basis() + sage: latex(2*e[(0,1,2)]) # indirect doctest + 2\left(0, 1, 2\right) + """ + from sage.misc.latex import latex + + s = latex(m) + if s.find('\\text{\\textt') != -1: + # m contains "non-LaTeXed" strings, use string representation + s = str(m) + + # dictionary with left-right pairs of "brackets". put pairs + # in here accept \\left and \\right as prefixes. + bracket_d = {"{": "\\}", "[": "]", "(": ")", "\\{": "\\}", + "|": "|", "||": "||"} + bracket = self._print_options.get('latex_bracket', False) + if bracket is True: + left = "\\left[" + right = "\\right]" + elif bracket is False: + left = "" + right = "" + elif isinstance(bracket, (tuple, list)): + left = bracket[0] + right = bracket[1] + elif bracket in bracket_d: + left = bracket + right = bracket_d[bracket] + if left == "{": + left = "\\{" + left = "\\left" + left + right = "\\right" + right + else: + left = bracket + right = bracket + prefix = self._print_options.get('latex_prefix') + if prefix is None: + prefix = self._print_options.get('prefix') + if prefix == "": + return left + s + right + return "%s_{%s}" % (prefix, s) + diff --git a/src/sage/monoids/all.py b/src/sage/monoids/all.py index ae42a9e83f4..8a666f58f90 100644 --- a/src/sage/monoids/all.py +++ b/src/sage/monoids/all.py @@ -21,5 +21,5 @@ from free_abelian_monoid_element import is_FreeAbelianMonoidElement - +from indexed_monoid import IndexedFreeMonoid, IndexedFreeAbelianMonoid diff --git a/src/sage/monoids/indexed_monoid.py b/src/sage/monoids/indexed_monoid.py new file mode 100644 index 00000000000..3a315406524 --- /dev/null +++ b/src/sage/monoids/indexed_monoid.py @@ -0,0 +1,867 @@ +""" +Indexed Monoids + +AUTHORS: + +- Travis Scrimshaw (2013-10-15) +""" + +#***************************************************************************** +# Copyright (C) 2013 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from copy import copy +from sage.misc.abstract_method import abstract_method +from sage.misc.cachefunc import cached_method +from sage.misc.indexed_generators import IndexedGenerators +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.element import MonoidElement + +from sage.categories.monoids import Monoids +from sage.categories.poor_man_map import PoorManMap +from sage.rings.integer import Integer +from sage.rings.infinity import infinity +from sage.sets.finite_enumerated_set import FiniteEnumeratedSet +from sage.sets.family import Family + +class IndexedMonoidElement(MonoidElement): + """ + An element of an indexed monoid. + """ + def __init__(self, F, x): + """ + Create the element ``x`` of an indexed free abelian monoid ``F``. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F(1) + 1 + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: x = a^2 * b^3 * a^2 * b^4; x + F[0]^4*F[1]^7 + sage: TestSuite(x).run() + + sage: F = IndexedFreeMonoid('abcde') + sage: a,b,c,d,e = F.gens() + sage: a in F + True + sage: a*b in F + True + sage: TestSuite(a*d^2*e*c*a).run() + """ + MonoidElement.__init__(self, F) + self._monomial = x + + @abstract_method + def _sorted_items(self): + """ + Return the items (i.e terms) of ``self``, sorted for printing. + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: x = a*b^2*e*d + sage: x._sorted_items() + ((0, 1), (1, 2), (4, 1), (3, 1)) + + .. SEEALSO:: + + :meth:`_repr_`, :meth:`_latex_`, :meth:`print_options` + """ + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: a*b^2*e*d + F[0]*F[1]^2*F[3]*F[4] + """ + if not self._monomial: + return '1' + + monomial = self._sorted_items() + P = self.parent() + + scalar_mult = P._print_options['scalar_mult'] + + exp = lambda v: '^{}'.format(v) if v != 1 else '' + return scalar_mult.join(P._repr_generator(g) + exp(v) for g,v in monomial) + + def _ascii_art_(self): + r""" + Return an ASCII art representation of ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: ascii_art(a*e*d) + F *F *F + 0 3 4 + sage: ascii_art(a*b^2*e*d) + 2 + F *F *F *F + 0 1 3 4 + """ + from sage.misc.ascii_art import AsciiArt, ascii_art, empty_ascii_art + + if not self._monomial: + return AsciiArt(["1"]) + + monomial = self._sorted_items() + P = self.parent() + scalar_mult = P._print_options['scalar_mult'] + + if all(x[1] == 1 for x in monomial): + ascii_art_gen = lambda m: P._ascii_art_generator(m[0]) + else: + pref = AsciiArt([P.prefix()]) + def ascii_art_gen(m): + if m[1] != 1: + r = (AsciiArt([" "**Integer(len(pref))]) + ascii_art(m[1])) + else: + r = empty_ascii_art + r = r * P._ascii_art_generator(m[0]) + r._baseline = r._h - 2 + return r + b = ascii_art_gen(monomial[0]) + for x in monomial[1:]: + b = b + AsciiArt([scalar_mult]) + ascii_art_gen(x) + return b + + def _latex_(self): + r""" + Return a `\LaTeX` representation of ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: latex(a*b^2*e*d) + F_{0} F_{1}^{2} F_{3} F_{4} + """ + if not self._monomial: + return '1' + + monomial = self._sorted_items() + P = self.parent() + + scalar_mult = P._print_options['latex_scalar_mult'] + if scalar_mult is None: + scalar_mult = P._print_options['scalar_mult'] + if scalar_mult == "*": + scalar_mult = " " + + exp = lambda v: '^{{{}}}'.format(v) if v != 1 else '' + return scalar_mult.join(P._latex_generator(g) + exp(v) for g,v in monomial) + + def __iter__(self): + """ + Iterate over ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: list(b*a*c^3*b) + [(F[1], 1), (F[0], 1), (F[2], 3), (F[1], 1)] + + :: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: list(b*c^3*a) + [(F[0], 1), (F[1], 1), (F[2], 3)] + """ + return ((self.parent().gen(index), exp) for (index,exp) in self._sorted_items()) + + def __eq__(self, y): + """ + Check equality. + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: a == a + True + sage: a*e == a*e + True + sage: a*b*c^3*b*d == (a*b*c)*(c^2*b*d) + True + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: a == a + True + sage: a*e == e*a + True + sage: a*b*c^3*b*d == a*d*(b^2*c^2)*c + True + """ + if not isinstance(y, IndexedMonoidElement): + return y == 1 and not self._monomial + return y.parent() is self.parent() and y._monomial == self._monomial + + def __ne__(self, y): + """ + Check inequality. + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: a != b + True + sage: a*b != b*a + True + sage: a*b*c^3*b*d != (a*b*c)*(c^2*b*d) + False + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: a != b + True + sage: a*b != a*a + True + sage: a*b*c^3*b*d != a*d*(b^2*c^2)*c + False + """ + return not self.__eq__(y) + + def __len__(self): + """ + Return the length of ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: elt = a*c^3*b^2*a + sage: elt.length() + 7 + sage: len(elt) + 7 + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: elt = a*c^3*b^2*a + sage: elt.length() + 7 + sage: len(elt) + 7 + """ + return sum(exp for gen,exp in self._sorted_items()) + + length = __len__ + + def support(self): + """ + Return a list of the objects indexing ``self`` with + non-zero exponents. + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: (b*a*c^3*b).support() + [0, 1, 2] + + :: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: (a*c^3).support() + [0, 2] + """ + supp = set([key for key, exp in self._sorted_items() if exp != 0]) + return sorted(supp) + + def leading_support(self): + """ + Return the support of the leading generator of ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: (b*a*c^3*a).leading_support() + 1 + + :: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: (b*c^3*a).leading_support() + 0 + """ + if not self: + return None + return self._sorted_items()[0][0] + + def trailing_support(self): + """ + Return the support of the trailing generator of ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: (b*a*c^3*a).trailing_support() + 0 + + :: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: (b*c^3*a).trailing_support() + 2 + """ + if not self: + return None + return self._sorted_items()[-1][0] + +class IndexedFreeMonoidElement(IndexedMonoidElement): + """ + An element of an indexed free abelian monoid. + """ + def _sorted_items(self): + """ + Returns the items (i.e terms) of ``self``, sorted for printing + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: x = a*b^2*e*d + sage: x._sorted_items() + ((0, 1), (1, 2), (4, 1), (3, 1)) + sage: F.print_options(monomial_cmp = lambda x,y: -cmp(x,y)) + sage: x._sorted_items() + ((0, 1), (1, 2), (4, 1), (3, 1)) + sage: F.print_options(monomial_cmp=cmp) # reset to original state + + .. SEEALSO:: + + :meth:`_repr_`, :meth:`_latex_`, :meth:`print_options` + """ + return self._monomial + + def _mul_(self, y): + """ + Multiply ``self`` by ``y``. + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: a*b^2*e*d + F[0]*F[1]^2*F[4]*F[3] + sage: (a*b^2*d^2) * (d^4*b*e) + F[0]*F[1]^2*F[3]^6*F[1]*F[4] + """ + if len(self._monomial) == 0: + return y + if len(y._monomial) == 0: + return self + + ret = list(self._monomial) + rhs = list(y._monomial) + if ret[-1][0] == rhs[0][0]: + rhs[0] = (rhs[0][0], rhs[0][1] + ret.pop()[1]) + ret += rhs + return self.__class__(self.parent(), tuple(ret)) + + def __pow__(self, n): + """ + Raise ``self`` to the power of ``n``. + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: x = a*b^2*e*d*a; x + F[0]*F[1]^2*F[4]*F[3]*F[0] + sage: x^3 + F[0]*F[1]^2*F[4]*F[3]*F[0]^2*F[1]^2*F[4]*F[3]*F[0]^2*F[1]^2*F[4]*F[3]*F[0] + sage: x^0 + 1 + """ + if not isinstance(n, (int, long, Integer)): + raise TypeError("Argument n (= {}) must be an integer".format(n)) + if n < 0: + raise ValueError("Argument n (= {}) must be positive".format(n)) + if n == 1: + return self + if n == 0: + return self.parent().one() + if len(self._monomial) == 1: + gen,exp = self._monomial[0] + return self.__class__(self.parent(), ((gen, exp*n),)) + ret = self + for i in range(n-1): + ret *= self + return ret + +class IndexedFreeAbelianMonoidElement(IndexedMonoidElement): + """ + An element of an indexed free abelian monoid. + """ + def _sorted_items(self): + """ + Return the items (i.e syllables) of ``self``, sorted for printing. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: x = a*b^2*e*d + sage: x._sorted_items() + [(0, 1), (1, 2), (3, 1), (4, 1)] + sage: F.print_options(monomial_cmp = lambda x,y: -cmp(x,y)) + sage: x._sorted_items() + [(4, 1), (3, 1), (1, 2), (0, 1)] + sage: F.print_options(monomial_cmp=cmp) # reset to original state + + .. SEEALSO:: + + :meth:`_repr_`, :meth:`_latex_`, :meth:`print_options` + """ + print_options = self.parent().print_options() + v = self._monomial.items() + try: + v.sort(cmp = print_options['monomial_cmp']) + except StandardError: # Sorting the output is a plus, but if we can't, no big deal + pass + return v + + def _mul_(self, y): + """ + Multiply ``self`` by ``y``. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: a*b^2*e*d + F[0]*F[1]^2*F[3]*F[4] + """ + ret = copy(self._monomial) + for k,v in y._monomial.iteritems(): + ret[k] = ret.get(k, 0) + v + return self.__class__(self.parent(), ret) + + def __pow__(self, n): + """ + Raise ``self`` to the power of ``n``. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: x = a*b^2*e*d; x + F[0]*F[1]^2*F[3]*F[4] + sage: x^3 + F[0]^3*F[1]^6*F[3]^3*F[4]^3 + sage: x^0 + 1 + """ + if not isinstance(n, (int, long, Integer)): + raise TypeError("Argument n (= {}) must be an integer".format(n)) + if n < 0: + raise ValueError("Argument n (= {}) must be positive".format(n)) + if n == 1: + return self + if n == 0: + return self.parent().one() + return self.__class__(self.parent(), {k:v*n for k,v in self._monomial.iteritems()}) + + def dict(self): + """ + Return ``self`` as a dictionary. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: (a*c^3).dict() + {0: 1, 2: 3} + """ + return copy(self._monomial) + + def cancel(self, elt): + """ + Cancel the element ``elt`` out of ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: elt = a*b*c^3*d^2; elt + F[0]*F[1]*F[2]^3*F[3]^2 + sage: elt.cancel(a) + F[1]*F[2]^3*F[3]^2 + sage: elt.cancel(c) + F[0]*F[1]*F[2]^2*F[3]^2 + sage: elt.cancel(a*b*d^2) + F[2]^3 + """ + d = copy(self._monomial) + for k,v in elt._monomial.iteritems(): + d[k] -= v + for k,v in d.items(): + if v < 0: + raise ValueError("invalid cancellation") + if v == 0: + del d[k] + return self.__class__(self.parent(), d) + +class IndexedMonoid(Parent, IndexedGenerators, UniqueRepresentation): + """ + Base class for monoids with an indexed set of generators. + + INPUT: + + - ``indices`` -- the indices for the generators + + For the optional arguments that control the printing, see + :class:`~sage.misc.indexed_generators.IndexedGenerators`. + """ + @staticmethod + def __classcall__(cls, indices, prefix="F", **kwds): + """ + TESTS:: + + sage: F = IndexedFreeAbelianMonoid(['a','b','c']) + sage: G = IndexedFreeAbelianMonoid(('a','b','c')) + sage: H = IndexedFreeAbelianMonoid('abc') + sage: F is G and F is H + True + + sage: F = IndexedFreeAbelianMonoid(['a','b','c'], latex_bracket=['LEFT', 'RIGHT']) + sage: F.print_options()['latex_bracket'] + ('LEFT', 'RIGHT') + sage: F is G + False + """ + if isinstance(indices, str): + indices = FiniteEnumeratedSet(list(indices)) + elif isinstance(indices, (list, tuple)): + indices = FiniteEnumeratedSet(indices) + + # bracket or latex_bracket might be lists, so convert + # them to tuples so that they're hashable. + bracket = kwds.get('bracket', None) + if isinstance(bracket, list): + kwds['bracket'] = tuple(bracket) + latex_bracket = kwds.get('latex_bracket', None) + if isinstance(latex_bracket, list): + kwds['latex_bracket'] = tuple(latex_bracket) + return super(IndexedMonoid, cls).__classcall__(cls, indices, prefix, **kwds) + + def __init__(self, indices, prefix, category=None, **kwds): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: TestSuite(F).run() + sage: F = IndexedFreeAbelianMonoid('abcde') + sage: TestSuite(F).run() + """ + self._indices = indices + category = Monoids().or_subcategory(category) + Parent.__init__(self, category=category) + + # ignore the optional 'key' since it only affects CachedRepresentation + kwds.pop('key', None) + IndexedGenerators.__init__(self, indices, prefix, **kwds) + + def _element_constructor_(self, x=None): + """ + Create an element of this abelian monoid from ``x``. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F(F.gen(2)) + F[2] + sage: F(-5) + F[-5] + sage: F(1) + 1 + sage: F([[1, 3], [-2, 12]]) + F[-2]^12*F[1]^3 + """ + if x is None or x == 1: + return self.one() + if isinstance(x, IndexedFreeAbelianMonoidElement) and x.parent() is self: + return x + if x in self._indices: + return self.gens()[x] + return self.element_class(self, x) + + def _an_element_(self): + """ + Return an element of ``self``. + + EXAMPLES:: + + sage: G = IndexedFreeAbelianMonoid(ZZ) + sage: G.an_element() + F[1] + sage: G = IndexedFreeMonoid('abc') + sage: G.an_element() + F['a']*F['b']*F['c'] + """ + if self._indices.cardinality() != infinity: + return self.prod(self.gens()) + return self.gen(self._indices.an_element()) + + def __contains__(self, x): + r""" + Return ``True`` if `x` is an element of ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F.gen(2)*F.gen(3) in F + True + + Note that a monoid on `\NN` generators is not considered a + submonoid of one on `\ZZ` generators:: + + sage: IndexedFreeAbelianMonoid(NN).gen(2) in F + False + """ + return isinstance(x, self.element_class) and x.parent() is self + + def gens(self): + """ + Return the generators of ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F.gens() + Lazy family (Generator map from Integer Ring to + Free abelian monoid indexed by Integer Ring(i))_{i in Integer Ring} + sage: F = IndexedFreeAbelianMonoid('abcde') + sage: F.gens() + Finite family {'a': F['a'], 'c': F['c'], 'b': F['b'], 'e': F['e'], 'd': F['d']} + """ + if self._indices.cardinality() == infinity: + gen = PoorManMap(self.gen, domain=self._indices, codomain=self, name="Generator map") + return Family(self._indices, gen) + return Family(self._indices, self.gen) + + def ngens(self): + """ + The number of free generators of ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F.ngens() + +Infinity + sage: F = IndexedFreeAbelianMonoid('abcde') + sage: F.ngens() + 5 + """ + return self._indices.cardinality() + +class IndexedFreeMonoid(IndexedMonoid): + """ + Free monoid with an indexed set of generators. + + INPUT: + + - ``indices`` -- the indices for the generators + + For the optional arguments that control the printing, see + :class:`~sage.misc.indexed_generators.IndexedGenerators`. + + .. NOTE:: + + If there is ambiguity with `1`, we construct the identity in + the monoid instead of the generator:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: F(1) + 1 + sage: F.gen(1) + F[1] + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: F.gen(15)^3 * F.gen(2) * F.gen(15) + F[15]^3*F[2]*F[15] + + Now we examine some of the printing options:: + + sage: F = IndexedFreeMonoid(ZZ, prefix='X', bracket=['|','>']) + sage: F.gen(2) * F.gen(12) + X|2>*X|12> + sage: F = IndexedFreeAbelianMonoid(Partitions(), prefix='A', bracket=False, scalar_mult='%') + sage: F.gen([3,1,1]) * F.gen([2,2]) + A[2, 2]%A[3, 1, 1] + """ + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: IndexedFreeMonoid(ZZ) + Free monoid indexed by Integer Ring + """ + return "Free monoid indexed by {}".format(self._indices) + + Element = IndexedFreeMonoidElement + + @cached_method + def one(self): + """ + Return the identity element of ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: F.one() + 1 + """ + return self.element_class(self, ()) + + def gen(self, x): + """ + The generator indexed by ``x`` of ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: F.gen(0) + F[0] + sage: F.gen(2) + F[2] + """ + if x not in self._indices: + raise IndexError("{} is not in the index set".format(x)) + try: + return self.element_class(self, ((self._indices(x),1),)) + except TypeError: # Backup (if it is a string) + return self.element_class(self, ((x,1),)) + +class IndexedFreeAbelianMonoid(IndexedMonoid): + """ + Free abelian monoid with an indexed set of generators. + + INPUT: + + - ``indices`` -- the indices for the generators + + For the optional arguments that control the printing, see + :class:`~sage.misc.indexed_generators.IndexedGenerators`. + + .. NOTE:: + + If there is ambiguity with `1`, we construct the identity in + the monoid instead of the generator:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F(1) + 1 + sage: F.gen(1) + F[1] + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F.gen(15)^3 * F.gen(2) * F.gen(15) + F[2]*F[15]^4 + + Now we examine some of the printing options:: + + sage: F = IndexedFreeAbelianMonoid(ZZ, prefix='X', bracket=['|','>']) + sage: F.gen(2) * F.gen(12) + X|2>*X|12> + sage: F = IndexedFreeAbelianMonoid(Partitions(), prefix='A', bracket=False, scalar_mult='%') + sage: F.gen([3,1,1]) * F.gen([2,2]) + A[2, 2]%A[3, 1, 1] + """ + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: IndexedFreeAbelianMonoid(ZZ) + Free abelian monoid indexed by Integer Ring + """ + return "Free abelian monoid indexed by {}".format(self._indices) + + def _element_constructor_(self, x=None): + """ + Create an element of this abelian monoid from ``x``. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F(F.gen(2)) + F[2] + sage: F(-5) + F[-5] + sage: F(1) + 1 + sage: F([[1, 3], [-2, 12]]) + F[-2]^12*F[1]^3 + """ + if isinstance(x, (list, tuple)): + x = dict(x) + return IndexedMonoid._element_constructor_(self, x) + + Element = IndexedFreeAbelianMonoidElement + + @cached_method + def one(self): + """ + Return the identity element of ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F.one() + 1 + """ + return self.element_class(self, {}) + + def gen(self, x): + """ + The generator indexed by ``x`` of ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F.gen(0) + F[0] + sage: F.gen(2) + F[2] + """ + if x not in self._indices: + raise IndexError("{} is not in the index set".format(x)) + try: + return self.element_class(self, {self._indices(x):1}) + except TypeError: # Backup (if it is a string) + return self.element_class(self, {x:1}) + From 32fa6c14411947b49bf32c3b6be176575bade538 Mon Sep 17 00:00:00 2001 From: Hao Chen Date: Sun, 10 Nov 2013 16:29:00 +0000 Subject: [PATCH 004/546] trac 15382 Macaulay Resultant of Multivariate Polynomials --- src/sage/rings/polynomial/all.py | 6 + .../rings/polynomial/macaulay_resultant.py | 347 ++++++++++++++++++ 2 files changed, 353 insertions(+) create mode 100644 src/sage/rings/polynomial/macaulay_resultant.py diff --git a/src/sage/rings/polynomial/all.py b/src/sage/rings/polynomial/all.py index ebd1d56dc92..6d9282cbc67 100644 --- a/src/sage/rings/polynomial/all.py +++ b/src/sage/rings/polynomial/all.py @@ -17,6 +17,8 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.misc.lazy_import import lazy_import + # Quotient of polynomial ring from sage.rings.polynomial.polynomial_quotient_ring import PolynomialQuotientRing, is_PolynomialQuotientRing from sage.rings.polynomial.polynomial_quotient_ring_element import PolynomialQuotientRingElement @@ -47,3 +49,7 @@ # Evaluation of cyclotomic polynomials from sage.rings.polynomial.cyclotomic import cyclotomic_value + +# Macaulay resultant for multivariable polynomials +lazy_import('sage.rings.polynomial.macaulay_resultant', 'macaulay_resultant') +lazy_import('sage.rings.polynomial.macaulay_resultant', 'macaulay_general_resultant') \ No newline at end of file diff --git a/src/sage/rings/polynomial/macaulay_resultant.py b/src/sage/rings/polynomial/macaulay_resultant.py new file mode 100644 index 00000000000..3edf76d8e1e --- /dev/null +++ b/src/sage/rings/polynomial/macaulay_resultant.py @@ -0,0 +1,347 @@ +""" +Macaulay Resultant of Multivariate Polynomials + +This is an implementation of the Macaulay Resultant. It computes +the resultant of universal polynomials as well as polynomials +with constant coefficients. This is a project done in +sage days 55. It's based on the implementation in Maple by +Manfred Minimair, which in turn is based on the following: + +-Using Algebraic Geometry by Cox, Little, O'Shea +-Canny, J., "Generalised characteristic polynomials", p.241--250, + J. Symbolic Comput. Vol. 9, No. 3, 1990 +-The algebraic theory of modular systems by Macaulay + + +AUTHORS: + +- Hao Chen +- Solomon Vishkautsan + +""" + +from sage.combinat.integer_vector_weighted import WeightedIntegerVectors +from sage.misc.misc_c import prod +from sage.matrix.constructor import matrix +from sage.rings.integer_ring import ZZ +from sage.rings.arith import binomial +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + +def getS(mon_deg_tuple,dlist): + r""" + In the MR algorithm the list of all monomials of the total degree is partitioned into sets S_i. + This function returns the index i for the set S_i for the inputted given monomial. + + INPUT: + + - `mon_deg_tuple` -- a list representing a monomial of a degree d + - `dlist` -- a list of degrees d_i of the polynomials in question, where + d = sum(dlist) - len(dlist) + 1 + + OUTPUT: + + - the index i such that the input monomial lives in S_i + + EXAMPLES:: + + sage: from sage.rings.polynomial.macaulay_resultant import getS + sage: getS([1,1,0],[2,1,1]) # the monomial xy where the total degree = 2 + 1 + + sage: getS([29,21,8],[10,20,30]) + 0 + + sage: getS(range(0,9)+[10],range(1,11)) + 9 + """ + for i in xrange(len(dlist)): + if mon_deg_tuple[i] - dlist[i] >= 0: + return i + + +def monomials(d,R): + r""" + returns all the monomials of degree d with variables a list of + generators of the ring R + + INPUT: + + -`d` -- a positive integer + -`R` -- a polynoimal ring + + OUTPUT: + + - a list of all monomials of degree d. + + EXAMPLES:: + + sage: from sage.rings.polynomial.macaulay_resultant import monomials + sage: monomials(3, PolynomialRing(QQ,3,'x')) + [x0^3, x0^2*x1, x0^2*x2, x0*x1^2, x0*x1*x2, x0*x2^2, x1^3, x1^2*x2, x1*x2^2, x2^3] + + It's OK if the coefficients of R live in some polynomial ring:: + + sage: U = PolynomialRing(QQ,20,'u') + sage: monomials(2, PolynomialRing(U,5,'x')) + [x0^2, x0*x1, x0*x2, x0*x3, x0*x4, x1^2, x1*x2, x1*x3, x1*x4, x2^2, x2*x3, x2*x4, x3^2, x3*x4, x4^2] + + """ + xlist = R.gens() + n = len(xlist) -1 + one_list = [1 for i in xrange(0,n+1)] + degs = WeightedIntegerVectors(d, one_list) + return [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) + for deg in degs] + +def is_reduced(mon,dlist,xlist): + r""" + A monomial in the variables x_0,...,x_n is called reduced with respect to the list of degrees d_0,...,d_n + if the degree of x_i in the monomial is >= d_i for exactly one i. This function checks this property for an inputted monomial. + + INPUT: + + - mon -- a monomial in the variables listed in xlist + - dlist -- a list of degrees with respect to which we check reducedness + - xlist -- a list of variables in some Polynomial ring. + + OUTPUT: + + - True/False + + EXAMPLES:: + + sage: from sage.rings.polynomial.macaulay_resultant import is_reduced + sage: R. = PolynomialRing(QQ,3) + sage: is_reduced(x^2*y^3*z,[2,3,3],[x,y,z]) + False + + sage: R. = PolynomialRing(QQ,3) + sage: is_reduced(x*y^3*z^2,[2,3,3],[x,y,z]) + True + """ + deg = [mon.degree(xi) for xi in xlist] + diff = [deg[i] - dlist[i] for i in xrange(0,len(dlist))] + return len([1 for d in diff if d >= 0]) == 1 + +def construct_universal_polynomial(dlist): + r""" + Given a list of degrees, this function returns a list of len(dlist) polynomials with len(dlist) variables, + with generic coefficients. This is useful for generating polynomials for tests, + and for getting the general resultant for the given degrees. + + INPUT: + + - dlist -- a list of degrees. + + OUTPUT: + + - a list of polynomials of the given degrees with general coefficients. + - a polynomial ring over ZZ generated by the coefficients of the output polynomials. + + EXAMPLES:: + + sage: from sage.rings.polynomial.macaulay_resultant import construct_universal_polynomial + sage: construct_universal_polynomial([1,1,2]) + ([u0*x0 + u1*x1 + u2*x2, u3*x0 + u4*x1 + u5*x2, u6*x0^2 + u7*x0*x1 + u9*x1^2 + u8*x0*x2 + u10*x1*x2 + u11*x2^2], + Multivariate Polynomial Ring in u0, u1, u2, u3, u4, u5, u6, u7, u8, u9, u10, u11 over Integer Ring) + """ + + n = len(dlist) - 1 + number_of_coeffs = sum([binomial(n+di,di) for di in dlist]) + U = PolynomialRing(ZZ,'u',number_of_coeffs) + d = sum(dlist) - len(dlist) + 1 + flist = [] + R = PolynomialRing(U,'x',n+1) + #print R + ulist = U.gens() + for d in dlist: + # construct a universal polynomial of degree d + # suppose we already have mon_d + mon_d = monomials(d,R) + #print mon_d + f = sum([mon_d[i]*ulist[i] for i in xrange(0,len(mon_d))]) + flist.append (f) + #print f + ulist = ulist[len(mon_d):] + return flist, U + +def macaulay_resultant(flist): + r""" + This is an implementation of the Macaulay Resultant. It computes + the resultant of universal polynomials as well as polynomials + with constant coefficients. This is a project done in + sage days 55. It's based on the implementation in Maple by + Manfred Minimair, which in turn is based on the following: + + -Using Algebraic Geometry by Cox, Little, O'Shea + -Canny, J., "Generalised characteristic polynomials", p.241--250, + J. Symbolic Comput. Vol. 9, No. 3, 1990 + -The algebraic theory of modular systems by Macaulay + + It calculates the Macaulay resultant for a list of Polynomials, + up to sign! + + AUTHORS: + + - Hao Chen + - Solomon Vishkautsan + + + INPUT: + + - flist -- a list of n homogeneous polynomials in n variables + + OUTPUT: + + - the resultant + + EXAMPLES: + + The polynomials need to be all homogeneous:: + + sage: from sage.rings.polynomial.macaulay_resultant import * + sage: R. = PolynomialRing(QQ,3) + sage: macaulay_resultant([y, x+z, z+x^3]) + Traceback (most recent call last): + ... + AssertionError: resultant for non-homogeneous polynomials is not supported + + + The number of polynomials has to match the number of variables:: + + sage: R. = PolynomialRing(QQ,3) + sage: macaulay_resultant([y,x+z]) + Traceback (most recent call last): + ... + AssertionError: number of polynomials(= 2) must equal to number of variables (= 3) + + + + The following example recreates Proposition 2.10 in Ch.3 of Using Algebraic Geometry:: + + + sage: flist,_ = construct_universal_polynomial([1,1,2]) + sage: macaulay_resultant(flist) + u2^2*u4^2*u6 - 2*u1*u2*u4*u5*u6 + u1^2*u5^2*u6 - u2^2*u3*u4*u7 + u1*u2*u3*u5*u7 + u0*u2*u4*u5*u7 - u0*u1*u5^2*u7 + u1*u2*u3*u4*u8 - u0*u2*u4^2*u8 - u1^2*u3*u5*u8 + u0*u1*u4*u5*u8 + u2^2*u3^2*u9 - 2*u0*u2*u3*u5*u9 + u0^2*u5^2*u9 - u1*u2*u3^2*u10 + u0*u2*u3*u4*u10 + u0*u1*u3*u5*u10 - u0^2*u4*u5*u10 + u1^2*u3^2*u11 - 2*u0*u1*u3*u4*u11 + u0^2*u4^2*u11 + + The following example degenerates into the determinant of a 3*3 matrix:: + + sage: flist,_ = construct_universal_polynomial([1,1,1]) + sage: macaulay_resultant(flist) + -u2*u4*u6 + u1*u5*u6 + u2*u3*u7 - u0*u5*u7 - u1*u3*u8 + u0*u4*u8 + + The following example is by Patrick Ingram(arxiv:1310.4114):: + + sage: U = PolynomialRing(ZZ,'y',2); y0,y1 = U.gens() + sage: R = PolynomialRing(U,'x',3); x0,x1,x2 = R.gens() + sage: f0 = y0*x2^2 - x0^2 + 2*x1*x2 + sage: f1 = y1*x2^2 - x1^2 + 2*x0*x2 + sage: f2 = x0*x1 - x2^2 + sage: flist = [f0,f1,f2] + sage: macaulay_resultant([f0,f1,f2]) + y0^2*y1^2 - 4*y0^3 - 4*y1^3 + 18*y0*y1 - 27 + + a simple example with constant rational coefficients:: + + sage: R. = PolynomialRing(QQ,4) + sage: macaulay_resultant([w,z,y,x]) + 1 + + an example where the resultant vanishes:: + + sage: R. = PolynomialRing(QQ,3) + sage: macaulay_resultant([x+y,y^2,x]) + 0 + + an example of bad reduction at a prime p = 5:: + + sage: R. = PolynomialRing(QQ,3) + sage: macaulay_resultant([y,x^3+25*y^2*x,5*z]) + 125 + + an example when the coefficients live in a finite field:: + + sage: F = FiniteField(11) + sage: R. = PolynomialRing(F,4) + sage: macaulay_resultant([z,x^3,5*y,w]) + 4 + + + example when the denominator in the algorithm vanishes(in this case + the resultant is the constant term of the quotient of + char polynomials of numerator/denominator):: + + sage: R. = PolynomialRing(QQ,3) + sage: macaulay_resultant([y, x+z, z^2]) + -1 + + when there are only 2 functions, macaulay resultant degenerates to the traditional resultant:: + + sage: R. = PolynomialRing(QQ,1) + sage: f = x^2+1; g = x^5+1 + sage: f.resultant(g) == macaulay_resultant([f.homogenize(),g.homogenize()]) + True + + """ + assert len(flist) > 0, 'you have to input least 1 polynomial in the list' + assert all([f.is_homogeneous() for f in flist]), 'resultant for non-homogeneous polynomials is not supported' + R = flist[0].parent() + dlist = [f.degree() for f in flist] + xlist = R.gens() + assert len(xlist) == len(dlist), 'number of polynomials(= %d) must equal to number of variables (= %d)' % (len(dlist),len(xlist)) + n = len(dlist) - 1 + d = sum(dlist) - len(dlist) + 1 + one_list = [1 for i in xrange(0,len(dlist))] + degs = WeightedIntegerVectors(d, one_list) + mon_d = [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) for deg in degs] + M = len(mon_d) + mons_to_keep = [] + for j in xrange(0,M): + if not is_reduced(mon_d[j],dlist,xlist): + mons_to_keep.append(j) + newflist = [] + for mon in mon_d: + degs_mon = [mon.degree(x) for x in xlist] + si_mon = getS(degs_mon, dlist) + degs_mon[si_mon] -= dlist[si_mon] + quo = prod([xlist[k]**(degs_mon[k]) for k in xrange(0,n+1)]) + newflist.append(flist[si_mon]*quo) + result = [] + for f in newflist: + result.append([f.monomial_coefficient(mon) for mon in mon_d]) + numer_matrix = matrix(result) + denom_matrix = numer_matrix.matrix_from_rows_and_columns(mons_to_keep,mons_to_keep) + if denom_matrix.dimensions()[0] == 0: + return numer_matrix.det() + denom_det = denom_matrix.det() + if denom_det != 0: + return numer_matrix.det()/denom_det + poly_num = numer_matrix.characteristic_polynomial('T') + poly_denom = denom_matrix.characteristic_polynomial('T') + poly_quo = poly_num.quo_rem(poly_denom)[0] + return poly_quo(0) + + +def macaulay_general_resultant(dlist): + r""" + this is just a wrapper function of macaulay_resultant, where + it takes a list of degrees as input and returns the resultant of + a list of generic polynomials with coefficients in a polynomial ring. + + INPUT: + + - dlist -- a list of degrees + + OUTPUT: + + - the general resultant + + EXAMPLES:: + + sage: from sage.rings.polynomial.macaulay_resultant import macaulay_general_resultant + sage: macaulay_general_resultant([1,1,2]) + u2^2*u4^2*u6 - 2*u1*u2*u4*u5*u6 + u1^2*u5^2*u6 - u2^2*u3*u4*u7 + u1*u2*u3*u5*u7 + u0*u2*u4*u5*u7 - u0*u1*u5^2*u7 + u1*u2*u3*u4*u8 - u0*u2*u4^2*u8 - u1^2*u3*u5*u8 + u0*u1*u4*u5*u8 + u2^2*u3^2*u9 - 2*u0*u2*u3*u5*u9 + u0^2*u5^2*u9 - u1*u2*u3^2*u10 + u0*u2*u3*u4*u10 + u0*u1*u3*u5*u10 - u0^2*u4*u5*u10 + u1^2*u3^2*u11 - 2*u0*u1*u3*u4*u11 + u0^2*u4^2*u11 + """ + assert all([d >=0 for d in dlist]), 'degrees must be non-negative' + flist, U = construct_universal_polynomial(dlist) + return U(macaulay_resultant(flist)) From 96b81c371d4b2018e308db8d3870e4cd051d995a Mon Sep 17 00:00:00 2001 From: Solomon Vishkautsan Date: Sun, 1 Dec 2013 14:01:40 +0200 Subject: [PATCH 005/546] improve performance of macaulay_resultant --- .../rings/polynomial/macaulay_resultant.py | 54 ++++++++++++------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/src/sage/rings/polynomial/macaulay_resultant.py b/src/sage/rings/polynomial/macaulay_resultant.py index 3edf76d8e1e..cdb97105a0c 100644 --- a/src/sage/rings/polynomial/macaulay_resultant.py +++ b/src/sage/rings/polynomial/macaulay_resultant.py @@ -93,7 +93,7 @@ def monomials(d,R): return [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) for deg in degs] -def is_reduced(mon,dlist,xlist): +def is_reduced(mon_degs,dlist,xlist): r""" A monomial in the variables x_0,...,x_n is called reduced with respect to the list of degrees d_0,...,d_n if the degree of x_i in the monomial is >= d_i for exactly one i. This function checks this property for an inputted monomial. @@ -119,8 +119,10 @@ def is_reduced(mon,dlist,xlist): sage: is_reduced(x*y^3*z^2,[2,3,3],[x,y,z]) True """ - deg = [mon.degree(xi) for xi in xlist] - diff = [deg[i] - dlist[i] for i in xrange(0,len(dlist))] + #TODO fix comments to reflect change in input parameters + #RRR deg = [mon.degree(xi) for xi in xlist] + + diff = [mon_degs[i] - dlist[i] for i in xrange(0,len(dlist))] return len([1 for d in diff if d >= 0]) == 1 def construct_universal_polynomial(dlist): @@ -152,6 +154,7 @@ def construct_universal_polynomial(dlist): d = sum(dlist) - len(dlist) + 1 flist = [] R = PolynomialRing(U,'x',n+1) + #TODO remove ugly prints #print R ulist = U.gens() for d in dlist: @@ -219,7 +222,6 @@ def macaulay_resultant(flist): The following example recreates Proposition 2.10 in Ch.3 of Using Algebraic Geometry:: - sage: flist,_ = construct_universal_polynomial([1,1,2]) sage: macaulay_resultant(flist) u2^2*u4^2*u6 - 2*u1*u2*u4*u5*u6 + u1^2*u5^2*u6 - u2^2*u3*u4*u7 + u1*u2*u3*u5*u7 + u0*u2*u4*u5*u7 - u0*u1*u5^2*u7 + u1*u2*u3*u4*u8 - u0*u2*u4^2*u8 - u1^2*u3*u5*u8 + u0*u1*u4*u5*u8 + u2^2*u3^2*u9 - 2*u0*u2*u3*u5*u9 + u0^2*u5^2*u9 - u1*u2*u3^2*u10 + u0*u2*u3*u4*u10 + u0*u1*u3*u5*u10 - u0^2*u4*u5*u10 + u1^2*u3^2*u11 - 2*u0*u1*u3*u4*u11 + u0^2*u4^2*u11 @@ -283,32 +285,46 @@ def macaulay_resultant(flist): True """ + #TODO add test that checks that the output of the function is a polynomial assert len(flist) > 0, 'you have to input least 1 polynomial in the list' assert all([f.is_homogeneous() for f in flist]), 'resultant for non-homogeneous polynomials is not supported' R = flist[0].parent() dlist = [f.degree() for f in flist] xlist = R.gens() - assert len(xlist) == len(dlist), 'number of polynomials(= %d) must equal to number of variables (= %d)' % (len(dlist),len(xlist)) + assert len(xlist) == len(dlist), 'number of polynomials(= %d) must equal number of variables (= %d)' % (len(dlist),len(xlist)) n = len(dlist) - 1 d = sum(dlist) - len(dlist) + 1 one_list = [1 for i in xrange(0,len(dlist))] - degs = WeightedIntegerVectors(d, one_list) - mon_d = [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) for deg in degs] - M = len(mon_d) + mons = WeightedIntegerVectors(d, one_list) # returns a list of integer vectors representing the list of all monomials of degree d + #RRR mon_d = [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) for deg in degs] + #RRR M = len(mon_d) + mons_num = len(mons) mons_to_keep = [] - for j in xrange(0,M): - if not is_reduced(mon_d[j],dlist,xlist): - mons_to_keep.append(j) newflist = [] - for mon in mon_d: - degs_mon = [mon.degree(x) for x in xlist] - si_mon = getS(degs_mon, dlist) - degs_mon[si_mon] -= dlist[si_mon] - quo = prod([xlist[k]**(degs_mon[k]) for k in xrange(0,n+1)]) - newflist.append(flist[si_mon]*quo) result = [] - for f in newflist: - result.append([f.monomial_coefficient(mon) for mon in mon_d]) + + for j in xrange(0,mons_num): + if not is_reduced(mons[j],dlist,xlist): + mons_to_keep.append(j) + si_mon = getS(mons[j], dlist) + # Monomial is in S_i under the partition, now we reduce the i'th degree of the monomial + new_mon = list(mons[j]) + new_mon[si_mon] -= dlist[si_mon] + quo = prod([xlist[k]**(new_mon[k]) for k in xrange(0,n+1)]) # this produces the actual monomial + new_f = flist[si_mon]*quo + print(new_f) + # we strip the coefficients of the new polynomial: + result.append([new_f.monomial_coefficient(prod([xlist[k]**(mon[k]) for k in xrange(0,n+1)])) for mon in mons]) + + # for mon in mon_d: + # degs_mon = [mon.degree(x) for x in xlist] + + # degs_mon[si_mon] -= dlist[si_mon] + # quo = prod([xlist[k]**(degs_mon[k]) for k in xrange(0,n+1)]) + # newflist.append(flist[si_mon]*quo) + + #for f in newflist: + # result.append([f.monomial_coefficient(mon) for mon in mon_d]) numer_matrix = matrix(result) denom_matrix = numer_matrix.matrix_from_rows_and_columns(mons_to_keep,mons_to_keep) if denom_matrix.dimensions()[0] == 0: From d48a13b76376b60e1f2385f6951750faf27eebfa Mon Sep 17 00:00:00 2001 From: Solomon Vishkautsan Date: Sun, 1 Dec 2013 21:49:52 +0200 Subject: [PATCH 006/546] some fixes to performance issues --- src/sage/rings/polynomial/macaulay_resultant.py | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/src/sage/rings/polynomial/macaulay_resultant.py b/src/sage/rings/polynomial/macaulay_resultant.py index cdb97105a0c..f8155746ab0 100644 --- a/src/sage/rings/polynomial/macaulay_resultant.py +++ b/src/sage/rings/polynomial/macaulay_resultant.py @@ -296,8 +296,7 @@ def macaulay_resultant(flist): d = sum(dlist) - len(dlist) + 1 one_list = [1 for i in xrange(0,len(dlist))] mons = WeightedIntegerVectors(d, one_list) # returns a list of integer vectors representing the list of all monomials of degree d - #RRR mon_d = [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) for deg in degs] - #RRR M = len(mon_d) + mon_d = [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) for deg in mons] mons_num = len(mons) mons_to_keep = [] newflist = [] @@ -312,19 +311,9 @@ def macaulay_resultant(flist): new_mon[si_mon] -= dlist[si_mon] quo = prod([xlist[k]**(new_mon[k]) for k in xrange(0,n+1)]) # this produces the actual monomial new_f = flist[si_mon]*quo - print(new_f) # we strip the coefficients of the new polynomial: - result.append([new_f.monomial_coefficient(prod([xlist[k]**(mon[k]) for k in xrange(0,n+1)])) for mon in mons]) - - # for mon in mon_d: - # degs_mon = [mon.degree(x) for x in xlist] - - # degs_mon[si_mon] -= dlist[si_mon] - # quo = prod([xlist[k]**(degs_mon[k]) for k in xrange(0,n+1)]) - # newflist.append(flist[si_mon]*quo) - - #for f in newflist: - # result.append([f.monomial_coefficient(mon) for mon in mon_d]) + result.append([new_f.monomial_coefficient(mon) for mon in mon_d]) + numer_matrix = matrix(result) denom_matrix = numer_matrix.matrix_from_rows_and_columns(mons_to_keep,mons_to_keep) if denom_matrix.dimensions()[0] == 0: From c903dc85370548d7c632f144b28d570f530caaf3 Mon Sep 17 00:00:00 2001 From: Solomon Vishkautsan Date: Tue, 3 Dec 2013 09:49:56 +0200 Subject: [PATCH 007/546] minor changes --- .../rings/polynomial/macaulay_resultant.py | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/sage/rings/polynomial/macaulay_resultant.py b/src/sage/rings/polynomial/macaulay_resultant.py index f8155746ab0..3fef0d90ab3 100644 --- a/src/sage/rings/polynomial/macaulay_resultant.py +++ b/src/sage/rings/polynomial/macaulay_resultant.py @@ -93,7 +93,7 @@ def monomials(d,R): return [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) for deg in degs] -def is_reduced(mon_degs,dlist,xlist): +def is_reduced(mon_degs,dlist): r""" A monomial in the variables x_0,...,x_n is called reduced with respect to the list of degrees d_0,...,d_n if the degree of x_i in the monomial is >= d_i for exactly one i. This function checks this property for an inputted monomial. @@ -112,16 +112,15 @@ def is_reduced(mon_degs,dlist,xlist): sage: from sage.rings.polynomial.macaulay_resultant import is_reduced sage: R. = PolynomialRing(QQ,3) - sage: is_reduced(x^2*y^3*z,[2,3,3],[x,y,z]) + sage: is_reduced([2,3,1],[2,3,3]) # the monomial x^2*y^3*z is not reduced w.r.t. degrees vector [2,3,3] False sage: R. = PolynomialRing(QQ,3) - sage: is_reduced(x*y^3*z^2,[2,3,3],[x,y,z]) + sage: is_reduced([1,3,2],[2,3,3]) # the monomial x*y^3*z^2 is not reduced w.r.t. degrees vector [2,3,3] True """ #TODO fix comments to reflect change in input parameters #RRR deg = [mon.degree(xi) for xi in xlist] - diff = [mon_degs[i] - dlist[i] for i in xrange(0,len(dlist))] return len([1 for d in diff if d >= 0]) == 1 @@ -200,25 +199,24 @@ def macaulay_resultant(flist): EXAMPLES: - The polynomials need to be all homogeneous:: - - sage: from sage.rings.polynomial.macaulay_resultant import * - sage: R. = PolynomialRing(QQ,3) - sage: macaulay_resultant([y, x+z, z+x^3]) - Traceback (most recent call last): - ... - AssertionError: resultant for non-homogeneous polynomials is not supported - The number of polynomials has to match the number of variables:: sage: R. = PolynomialRing(QQ,3) sage: macaulay_resultant([y,x+z]) + ... Traceback (most recent call last): ... AssertionError: number of polynomials(= 2) must equal to number of variables (= 3) + The polynomials need to be all homogeneous:: + sage: from sage.rings.polynomial.macaulay_resultant import * + sage: R. = PolynomialRing(QQ,3) + sage: macaulay_resultant([y, x+z, z+x^3]) + Traceback (most recent call last): + ... + AssertionError: resultant for non-homogeneous polynomials is not supported The following example recreates Proposition 2.10 in Ch.3 of Using Algebraic Geometry:: @@ -283,7 +281,6 @@ def macaulay_resultant(flist): sage: f = x^2+1; g = x^5+1 sage: f.resultant(g) == macaulay_resultant([f.homogenize(),g.homogenize()]) True - """ #TODO add test that checks that the output of the function is a polynomial assert len(flist) > 0, 'you have to input least 1 polynomial in the list' @@ -291,11 +288,11 @@ def macaulay_resultant(flist): R = flist[0].parent() dlist = [f.degree() for f in flist] xlist = R.gens() - assert len(xlist) == len(dlist), 'number of polynomials(= %d) must equal number of variables (= %d)' % (len(dlist),len(xlist)) + assert len(xlist) == len(dlist), 'number of polynomials(= %d) must equal number of variables (= %d)'%(len(dlist),len(xlist)) n = len(dlist) - 1 d = sum(dlist) - len(dlist) + 1 one_list = [1 for i in xrange(0,len(dlist))] - mons = WeightedIntegerVectors(d, one_list) # returns a list of integer vectors representing the list of all monomials of degree d + mons = WeightedIntegerVectors(d, one_list) # returns a list of integer vectors representing the list of all monomials of degree d mon_d = [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) for deg in mons] mons_num = len(mons) mons_to_keep = [] @@ -303,7 +300,7 @@ def macaulay_resultant(flist): result = [] for j in xrange(0,mons_num): - if not is_reduced(mons[j],dlist,xlist): + if not is_reduced(mons[j],dlist): mons_to_keep.append(j) si_mon = getS(mons[j], dlist) # Monomial is in S_i under the partition, now we reduce the i'th degree of the monomial From 94bcf0ec174e1ba5f697d4bf2e446d8b57874a32 Mon Sep 17 00:00:00 2001 From: Solomon Vishkautsan Date: Tue, 3 Dec 2013 16:09:39 +0200 Subject: [PATCH 008/546] finished implementing minor performance improvements. There is still a failed test to take care of. --- src/sage/rings/polynomial/macaulay_resultant.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/polynomial/macaulay_resultant.py b/src/sage/rings/polynomial/macaulay_resultant.py index 3fef0d90ab3..41fd5134f41 100644 --- a/src/sage/rings/polynomial/macaulay_resultant.py +++ b/src/sage/rings/polynomial/macaulay_resultant.py @@ -204,7 +204,6 @@ def macaulay_resultant(flist): sage: R. = PolynomialRing(QQ,3) sage: macaulay_resultant([y,x+z]) - ... Traceback (most recent call last): ... AssertionError: number of polynomials(= 2) must equal to number of variables (= 3) @@ -293,7 +292,7 @@ def macaulay_resultant(flist): d = sum(dlist) - len(dlist) + 1 one_list = [1 for i in xrange(0,len(dlist))] mons = WeightedIntegerVectors(d, one_list) # returns a list of integer vectors representing the list of all monomials of degree d - mon_d = [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) for deg in mons] + mon_d = [prod([xlist[i]**(deg[i]) for i in xrange(0,len(deg))]) for deg in mons] mons_num = len(mons) mons_to_keep = [] newflist = [] @@ -303,9 +302,9 @@ def macaulay_resultant(flist): if not is_reduced(mons[j],dlist): mons_to_keep.append(j) si_mon = getS(mons[j], dlist) - # Monomial is in S_i under the partition, now we reduce the i'th degree of the monomial - new_mon = list(mons[j]) - new_mon[si_mon] -= dlist[si_mon] + # Monomial is in S_i under the partition, now we reduce the i'th degree of the monomial + new_mon = list(mons[j]) + new_mon[si_mon] -= dlist[si_mon] quo = prod([xlist[k]**(new_mon[k]) for k in xrange(0,n+1)]) # this produces the actual monomial new_f = flist[si_mon]*quo # we strip the coefficients of the new polynomial: From 9dca526f5ab91f7d2777ba4f4e945a869b169bb9 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 3 Dec 2013 11:40:30 -0800 Subject: [PATCH 009/546] Added comparison operations. --- src/sage/groups/indexed_group.py | 83 ++++++++++++++++++++---------- src/sage/monoids/indexed_monoid.py | 68 ++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 28 deletions(-) diff --git a/src/sage/groups/indexed_group.py b/src/sage/groups/indexed_group.py index 2d383ffdbe3..78a8970897c 100644 --- a/src/sage/groups/indexed_group.py +++ b/src/sage/groups/indexed_group.py @@ -22,7 +22,8 @@ from copy import copy from sage.categories.groups import Groups from sage.groups.group import Group, AbelianGroup -from sage.monoids.indexed_monoid import IndexedFreeMonoid, IndexedFreeAbelianMonoid +from sage.monoids.indexed_monoid import IndexedMonoidElement, IndexedFreeMonoid, \ + IndexedFreeAbelianMonoid from sage.misc.cachefunc import cached_method from sage.rings.integer import Integer from sage.rings.infinity import infinity @@ -64,32 +65,6 @@ def _repr_(self): """ return 'Free group indexed by {}'.format(self._indices) - def __len__(self): - """ - Return the length of ``self``. - - EXAMPLES:: - - sage: F = IndexedFreeGroup(ZZ) - sage: a,b,c,d,e = [F.gen(i) for i in range(5)] - sage: elt = a*c^-3*b^-2*a - sage: elt.length() - 7 - sage: len(elt) - 7 - - sage: F = IndexedFreeAbelianGroup(ZZ) - sage: a,b,c,d,e = [F.gen(i) for i in range(5)] - sage: elt = a*c^-3*b^-2*a - sage: elt.length() - 7 - sage: len(elt) - 7 - """ - return sum(abs(exp) for gen,exp in self._sorted_items()) - - length = __len__ - def order(self): r""" Return the number of elements of ``self``, which is `\infty` unless @@ -132,6 +107,58 @@ def is_finite(self): rank = IndexedFreeMonoid.ngens class Element(IndexedFreeMonoid.Element): + def __lt__(self, y): + """ + Check less than. + + EXAMPLES:: + + sage: F = IndexedFreeGroup(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: a < b + True + sage: a^-1*b < b^-1*a + True + sage: a*b < a*a^-1 + False + sage: a^-1*a < a^2 + True + sage: a^2*b < a*b^-1*a*b + True + """ + if not isinstance(y, IndexedMonoidElement): + return False + sign = lambda x: 1 if x > 0 else -1 # It is never 0 + lhs = sum([[ (x[0], sign(x[1])) ]*abs(x[1]) for x in self._sorted_items()], []) + rhs = sum([[ (x[0], sign(x[1])) ]*abs(x[1]) for x in y._sorted_items()], []) + return lhs < rhs + + def __len__(self): + """ + Return the length of ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeGroup(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: elt = a*c^-3*b^-2*a + sage: elt.length() + 7 + sage: len(elt) + 7 + + sage: F = IndexedFreeAbelianGroup(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: elt = a*c^-3*b^-2*a + sage: elt.length() + 7 + sage: len(elt) + 7 + """ + return sum(abs(exp) for gen,exp in self._sorted_items()) + + length = __len__ + def _mul_(self, y): """ Multiply ``self`` by ``y``. @@ -290,7 +317,7 @@ def is_finite(self): rank = IndexedFreeAbelianMonoid.ngens - class Element(IndexedFreeAbelianMonoid.Element): + class Element(IndexedFreeAbelianMonoid.Element, IndexedFreeGroup.Element): def _mul_(self, y): """ Multiply ``self`` by ``y``. diff --git a/src/sage/monoids/indexed_monoid.py b/src/sage/monoids/indexed_monoid.py index 3a315406524..74ac915543a 100644 --- a/src/sage/monoids/indexed_monoid.py +++ b/src/sage/monoids/indexed_monoid.py @@ -239,6 +239,74 @@ def __ne__(self, y): """ return not self.__eq__(y) + def __lt__(self, y): + """ + Check less than. + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: a < b + True + sage: a*b < b*a + True + sage: a*b < a*a + False + sage: a^2*b < a*b*b + True + """ + if not isinstance(y, IndexedMonoidElement): + return False + lhs = sum([[x[0]]*x[1] for x in self._sorted_items()], []) + rhs = sum([[x[0]]*x[1] for x in y._sorted_items()], []) + return lhs < rhs + + def __gt__(self, y): + """ + Check less than. + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: b > a + True + sage: a*b > b*a + False + sage: a*b > a*a + True + """ + if not isinstance(y, IndexedMonoidElement): + return False + return y.__lt__(self) + + def __le__(self, y): + """ + Check less than or equals. + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: a*b <= b*a + True + """ + return self.__eq__(y) or self.__lt__(y) + + def __ge__(self, y): + """ + Check greater than or equals. + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: a*b <= b*a + True + """ + return self.__eq__(y) or self.__gt__(y) + def __len__(self): """ Return the length of ``self``. From 60d531c214fa16074a20526aba534b9fb243586d Mon Sep 17 00:00:00 2001 From: Solomon Vishkautsan Date: Thu, 5 Dec 2013 10:10:32 +0200 Subject: [PATCH 010/546] removed all usage of actual monomials, only using degree-lists now. --- src/sage/rings/polynomial/macaulay_resultant.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/macaulay_resultant.py b/src/sage/rings/polynomial/macaulay_resultant.py index 41fd5134f41..a71de0e75c6 100644 --- a/src/sage/rings/polynomial/macaulay_resultant.py +++ b/src/sage/rings/polynomial/macaulay_resultant.py @@ -206,7 +206,7 @@ def macaulay_resultant(flist): sage: macaulay_resultant([y,x+z]) Traceback (most recent call last): ... - AssertionError: number of polynomials(= 2) must equal to number of variables (= 3) + AssertionError: number of polynomials(= 2) must equal number of variables (= 3) The polynomials need to be all homogeneous:: @@ -292,7 +292,7 @@ def macaulay_resultant(flist): d = sum(dlist) - len(dlist) + 1 one_list = [1 for i in xrange(0,len(dlist))] mons = WeightedIntegerVectors(d, one_list) # returns a list of integer vectors representing the list of all monomials of degree d - mon_d = [prod([xlist[i]**(deg[i]) for i in xrange(0,len(deg))]) for deg in mons] + #mon_d = [prod([xlist[i]**(deg[i]) for i in xrange(0,len(deg))]) for deg in mons] mons_num = len(mons) mons_to_keep = [] newflist = [] @@ -308,7 +308,7 @@ def macaulay_resultant(flist): quo = prod([xlist[k]**(new_mon[k]) for k in xrange(0,n+1)]) # this produces the actual monomial new_f = flist[si_mon]*quo # we strip the coefficients of the new polynomial: - result.append([new_f.monomial_coefficient(mon) for mon in mon_d]) + result.append([new_f[mon] for mon in mons]) numer_matrix = matrix(result) denom_matrix = numer_matrix.matrix_from_rows_and_columns(mons_to_keep,mons_to_keep) From a9da35924d555a5d79e0d5d36587adebcdeffff6 Mon Sep 17 00:00:00 2001 From: Solomon Vishkautsan Date: Thu, 5 Dec 2013 11:25:02 +0200 Subject: [PATCH 011/546] minor changes to comments --- src/sage/rings/polynomial/macaulay_resultant.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/macaulay_resultant.py b/src/sage/rings/polynomial/macaulay_resultant.py index a71de0e75c6..4cf7197642c 100644 --- a/src/sage/rings/polynomial/macaulay_resultant.py +++ b/src/sage/rings/polynomial/macaulay_resultant.py @@ -291,7 +291,7 @@ def macaulay_resultant(flist): n = len(dlist) - 1 d = sum(dlist) - len(dlist) + 1 one_list = [1 for i in xrange(0,len(dlist))] - mons = WeightedIntegerVectors(d, one_list) # returns a list of integer vectors representing the list of all monomials of degree d + mons = WeightedIntegerVectors(d, one_list) # list of exponent-vectors(/lists) of monomials of degree d #mon_d = [prod([xlist[i]**(deg[i]) for i in xrange(0,len(deg))]) for deg in mons] mons_num = len(mons) mons_to_keep = [] @@ -305,18 +305,20 @@ def macaulay_resultant(flist): # Monomial is in S_i under the partition, now we reduce the i'th degree of the monomial new_mon = list(mons[j]) new_mon[si_mon] -= dlist[si_mon] - quo = prod([xlist[k]**(new_mon[k]) for k in xrange(0,n+1)]) # this produces the actual monomial + quo = prod([xlist[k]**(new_mon[k]) for k in xrange(0,n+1)]) # this produces the actual reduced monomial new_f = flist[si_mon]*quo # we strip the coefficients of the new polynomial: result.append([new_f[mon] for mon in mons]) numer_matrix = matrix(result) denom_matrix = numer_matrix.matrix_from_rows_and_columns(mons_to_keep,mons_to_keep) - if denom_matrix.dimensions()[0] == 0: + if denom_matrix.dimensions()[0] == 0: # here we choose the determinant of an empty matrix to be 1 return numer_matrix.det() denom_det = denom_matrix.det() if denom_det != 0: return numer_matrix.det()/denom_det + # if we get to this point, the determinant of the denominator was 0, and we get the resultant + # by taking the free coefficient of the quotient of two characteristic polynomials poly_num = numer_matrix.characteristic_polynomial('T') poly_denom = denom_matrix.characteristic_polynomial('T') poly_quo = poly_num.quo_rem(poly_denom)[0] From 3eb3b07feb507827bd87c5540b03348853b27978 Mon Sep 17 00:00:00 2001 From: Christian Stump Date: Thu, 24 Jan 2013 11:38:35 +0000 Subject: [PATCH 012/546] trac 14004 Implementation of nonnesting partitions for Weyl groups --- src/sage/categories/finite_posets.py | 7 +-- .../root_system/root_lattice_realizations.py | 51 +++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/sage/categories/finite_posets.py b/src/sage/categories/finite_posets.py index 46d4dbdd425..92778ee1f3b 100644 --- a/src/sage/categories/finite_posets.py +++ b/src/sage/categories/finite_posets.py @@ -883,7 +883,7 @@ def toggling_orbit_iter(self, vs, oideal, element_constructor=set, stop=True, ch next = self.order_ideal_toggles(next, vs) yield element_constructor(next) - def order_ideals_lattice(self, as_ideals=True): + def order_ideals_lattice(self, as_ideals=True, facade=False): r""" Return the lattice of order ideals of a poset ``self``, ordered by inclusion. @@ -940,7 +940,8 @@ def order_ideals_lattice(self, as_ideals=True): from sage.misc.misc import attrcall from sage.sets.set import Set ideals = [Set( self.order_ideal(antichain) ) for antichain in self.antichains()] - return LatticePoset((ideals,attrcall("issubset"))) + return LatticePoset((ideals,attrcall("issubset")), + facade=facade) else: from sage.misc.cachefunc import cached_function antichains = [tuple(a) for a in self.antichains()] @@ -949,7 +950,7 @@ def is_above(a,xb): return any(self.is_lequal(xa,xb) for xa in a) def cmp(a,b): return all(is_above(a,xb) for xb in b) - return LatticePoset((antichains,cmp)) + return LatticePoset((antichains,cmp), facade=facade) @abstract_method(optional = True) def antichains(self): diff --git a/src/sage/combinat/root_system/root_lattice_realizations.py b/src/sage/combinat/root_system/root_lattice_realizations.py index 17574fdc432..513482e9440 100644 --- a/src/sage/combinat/root_system/root_lattice_realizations.py +++ b/src/sage/combinat/root_system/root_lattice_realizations.py @@ -702,6 +702,57 @@ def root_poset(self, restricted=False, facade=False): rels.append((root,root_cover)) return Poset((pos_roots,rels),cover_relations=True,facade=facade) + def nonnesting_partition_lattice(self,facade=False): + r""" + Return the lattice of nonnesting partitions + + EXAMPLES:: + """ + return self.root_poset(facade=facade).order_ideals_lattice(facade=facade) + + def generalized_nonnesting_partition_lattice(self,m,facade=False): + r""" + Return the lattice of m-nonnesting partitions + + EXAMPLES:: + """ + from sage.combinat.multichoose_nk import MultichooseNK + Phi_plus = self.positive_roots() + L = self.nonnesting_partition_lattice(facade=True) + chains = [chain for chain in L.chains().list() if len(chain) <= m] + multichains = [] + for chain in chains: + for multilist in MultichooseNK(len(chain), m): + if len(set(multilist)) == len(chain): + multichains.append(tuple([chain[i] for i in multilist])) + def is_saturated_chain(chain): + for i in range(1, m + 1): + for j in range(1, m - i + 1): + for alpha in chain[i - 1]: + for beta in chain[j - 1]: + gamma = alpha + beta + if gamma in Phi_plus and gamma not in chain[i+j-1]: + return False + cochain = [[beta for beta in Phi_plus if beta not in ideal] + for ideal in chain] + for i in range(1, m + 1): + for j in range(1, m + 1): + for alpha in cochain[i - 1]: + for beta in cochain[j - 1]: + gamma = alpha + beta + if gamma in Phi_plus and gamma not in cochain[min(m-1,i+j-1)]: + return False + return True + + def is_componentwise_subset(chain1, chain2): + return all(chain1[i].issubset(chain2[i]) + for i in range(len(chain1))) + from sage.combinat.posets.lattices import LatticePoset + saturated_chains = [multichain for multichain in multichains + if is_saturated_chain(multichain)] + return LatticePoset((saturated_chains, is_componentwise_subset), + facade=facade) + def almost_positive_roots(self): r""" Returns the almost positive roots of ``self`` From f17af7a764f8e879da0df5085a507dbebaf2c3c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 7 Jan 2014 21:11:30 +0100 Subject: [PATCH 013/546] trac #14004 add documentation, still needs work --- .../root_system/root_lattice_realizations.py | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/root_system/root_lattice_realizations.py b/src/sage/combinat/root_system/root_lattice_realizations.py index 513482e9440..8d4a6aff297 100644 --- a/src/sage/combinat/root_system/root_lattice_realizations.py +++ b/src/sage/combinat/root_system/root_lattice_realizations.py @@ -702,19 +702,51 @@ def root_poset(self, restricted=False, facade=False): rels.append((root,root_cover)) return Poset((pos_roots,rels),cover_relations=True,facade=facade) - def nonnesting_partition_lattice(self,facade=False): + def nonnesting_partition_lattice(self, facade=False): r""" Return the lattice of nonnesting partitions + This is the lattice of order ideals of the root poset. + + .. SEEALSO:: + + :meth:`generalized_nonnesting_partition_lattice`, :meth:`root_poset` + EXAMPLES:: + + sage: R = RootSystem(['A', 3]) + sage: RS = R.root_lattice() + sage: P = RS.nonnesting_partition_lattice(); P + Finite lattice containing 14 elements + sage: P.coxeter_transformation()**10 == 1 + True + + REFERENCES: """ return self.root_poset(facade=facade).order_ideals_lattice(facade=facade) - def generalized_nonnesting_partition_lattice(self,m,facade=False): + def generalized_nonnesting_partition_lattice(self, m, facade=False): r""" Return the lattice of m-nonnesting partitions + INPUT: + + - `m` -- integer + + .. SEEALSO:: + + :meth:`nonnesting_partition_lattice` + EXAMPLES:: + + sage: R = RootSystem(['A', 2]) + sage: RS = R.root_lattice() + sage: P = RS.generalized_nonnesting_partition_lattice(2); P + Finite lattice containing 12 elements + sage: P.coxeter_transformation()**20 == 1 + True + + REFERENCES: """ from sage.combinat.multichoose_nk import MultichooseNK Phi_plus = self.positive_roots() @@ -740,7 +772,7 @@ def is_saturated_chain(chain): for alpha in cochain[i - 1]: for beta in cochain[j - 1]: gamma = alpha + beta - if gamma in Phi_plus and gamma not in cochain[min(m-1,i+j-1)]: + if gamma in Phi_plus and gamma not in cochain[min(m - 1, i + j - 1)]: return False return True From 1bbbb30b8a2c8af8899b92e71342a8ab2f93023e Mon Sep 17 00:00:00 2001 From: Martin Albrecht Date: Mon, 13 Jan 2014 18:28:28 +0000 Subject: [PATCH 014/546] prevent increased degrees in eliminate_linear_variables() --- .../polynomial/multi_polynomial_sequence.py | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index 47fb3877601..c1ebf1e48cc 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -927,7 +927,7 @@ class PolynomialSequence_gf2(PolynomialSequence_generic): """ Polynomial Sequences over `\mathbb{F}_2`. """ - def eliminate_linear_variables(self, maxlength=Infinity, skip=None, return_reductors=False): + def eliminate_linear_variables(self, maxlength=Infinity, skip=None, return_reductors=False, use_polybori=False): """ Return a new system where linear leading variables are eliminated if the tail of the polynomial has length at most @@ -949,6 +949,11 @@ def eliminate_linear_variables(self, maxlength=Infinity, skip=None, return_reduc with linear leading terms which were used for reduction is also returned (default: ``False``). + - ```use_polybori`` - if ``True`` then ``polybori.ll.eliminate`` is + called. While this is typically faster what is implemented here, it + is less flexible (``skip` is not supported) and may increase the + degree (default: ``False``) + OUTPUT: When ``return_reductors==True``, then a pair of sequences of @@ -1008,6 +1013,25 @@ def eliminate_linear_variables(self, maxlength=Infinity, skip=None, return_reduc sage: S.eliminate_linear_variables(return_reductors=True) ([], [x + y, z + 1]) + We test a case which would increase the degree with ``polybori=True``:: + + sage: B. = BooleanPolynomialRing() + sage: f = a*d + a + b*d + c*d + 1 + sage: Sequence([f, a + b*c + c+d + 1]).eliminate_linear_variables() + [a*d + a + b*d + c*d + 1, a + b*c + c + d + 1] + + sage: B. = BooleanPolynomialRing() + sage: f = a*d + a + b*d + c*d + 1 + sage: Sequence([f, a + b*c + c+d + 1]).eliminate_linear_variables(use_polybori=True) + [b*c*d + b*c + b*d + c + d] + + This used to produce a SIGSEGV:: + + sage: R. = BooleanPolynomialRing() + sage: S = Sequence([x*y*z+x*y+z*y+x*z, x+y+z+1, x+y+z]) + sage: S.eliminate_linear_variables() + [1] + .. NOTE:: This is called "massaging" in [CBJ07]_. @@ -1019,6 +1043,7 @@ def eliminate_linear_variables(self, maxlength=Infinity, skip=None, return_reduc Multivariate Polynomials over GF(2) via SAT-Solvers*. Cryptology ePrint Archive: Report 2007/024. available at http://eprint.iacr.org/2007/024 + """ from sage.rings.polynomial.pbori import BooleanPolynomialRing from polybori import gauss_on_polys @@ -1032,7 +1057,7 @@ def eliminate_linear_variables(self, maxlength=Infinity, skip=None, return_reduc F = self reductors = [] - if skip is None and maxlength==Infinity: + if use_polybori and skip is None and maxlength==Infinity: # faster solution based on polybori.ll.eliminate while True: (this_step_reductors, _, higher) = eliminate(F) @@ -1063,6 +1088,11 @@ def eliminate_linear_variables(self, maxlength=Infinity, skip=None, return_reduc break linear = gauss_on_polys(linear) + if 1 in linear: + if return_reductors: + return PolynomialSequence(R, [R(1)]), PolynomialSequence(R, []) + else: + return PolynomialSequence(R, [R(1)]) rb = ll_encode(linear) reductors.extend(linear) From a278afa89bbac2a30cd8e9a982ed3c6463169019 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 24 Jan 2014 08:32:52 -0800 Subject: [PATCH 015/546] Added cardinality methods. --- src/sage/monoids/free_monoid.py | 14 ++++++++++++++ src/sage/monoids/indexed_monoid.py | 24 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/sage/monoids/free_monoid.py b/src/sage/monoids/free_monoid.py index 1aa6709ac75..e169d570298 100644 --- a/src/sage/monoids/free_monoid.py +++ b/src/sage/monoids/free_monoid.py @@ -250,3 +250,17 @@ def one_element(self): 1 """ return self(1) + + def cardinality(self): + r""" + Return the cardinality of ``self``, which is `\infty`. + + EXAMPLES:: + + sage: F = FreeMonoid(2005, 'a') + sage: F.cardinality() + +Infinity + """ + from sage.rings.infinity import infinity + return infinity + diff --git a/src/sage/monoids/indexed_monoid.py b/src/sage/monoids/indexed_monoid.py index 74ac915543a..bc271610756 100644 --- a/src/sage/monoids/indexed_monoid.py +++ b/src/sage/monoids/indexed_monoid.py @@ -831,6 +831,18 @@ def gen(self, x): except TypeError: # Backup (if it is a string) return self.element_class(self, ((x,1),)) + def cardinality(self): + r""" + Return the cardinality of ``self``, which is `\infty`. + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: F.cardinality() + +Infinity + """ + return infinity + class IndexedFreeAbelianMonoid(IndexedMonoid): """ Free abelian monoid with an indexed set of generators. @@ -933,3 +945,15 @@ def gen(self, x): except TypeError: # Backup (if it is a string) return self.element_class(self, {x:1}) + def cardinality(self): + r""" + Return the cardinality of ``self``, which is `\infty`. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F.cardinality() + +Infinity + """ + return infinity + From f681606006b81695f68deadfc913fd2069045edc Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 24 Jan 2014 08:33:49 -0800 Subject: [PATCH 016/546] Added cardinality to free abelian monoid for consistancy. --- src/sage/monoids/free_abelian_monoid.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/sage/monoids/free_abelian_monoid.py b/src/sage/monoids/free_abelian_monoid.py index 8bf138fe362..ea8fb3f53b1 100644 --- a/src/sage/monoids/free_abelian_monoid.py +++ b/src/sage/monoids/free_abelian_monoid.py @@ -215,3 +215,16 @@ def ngens(self): """ return self.__ngens + def cardinality(self): + r""" + Return the cardinality of ``self``, which is `\infty`. + + EXAMPLES:: + + sage: F = FreeAbelianMonoid(3000, 'a') + sage: F.cardinality() + +Infinity + """ + from sage.rings.infinity import infinity + return infinity + From b8a859a329efd606ade2ddbca38976e16d338f7d Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Fri, 19 Oct 2012 03:03:09 +0000 Subject: [PATCH 017/546] Trac #13620: initialization of padic polynomial from empty dict --- .../padics/polynomial_padic_capped_relative_dense.py | 9 ++++++++- .../rings/polynomial/padics/polynomial_padic_flat.py | 12 ++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py b/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py index f03ff90c07a..0db682ebb05 100644 --- a/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py +++ b/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py @@ -43,6 +43,13 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct = False, sage: T. = ZZ[] sage: R(t + 2) (1 + O(13^7))*t + (2 + O(13^7)) + + Check that :trac:`13620` has been fixed:: + + sage: f = R.zero() + sage: R(f.dict()) + 0 + """ Polynomial.__init__(self, parent, is_gen=is_gen) parentbr = parent.base_ring() @@ -97,7 +104,7 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct = False, check = False elif isinstance(x, dict): zero = parentbr.zero_element() - n = max(x.keys()) + n = max(x.keys()) if len(x) else 0 v = [zero for _ in xrange(n + 1)] for i, z in x.iteritems(): v[i] = z diff --git a/src/sage/rings/polynomial/padics/polynomial_padic_flat.py b/src/sage/rings/polynomial/padics/polynomial_padic_flat.py index 99ce0e45184..46108c1ce78 100644 --- a/src/sage/rings/polynomial/padics/polynomial_padic_flat.py +++ b/src/sage/rings/polynomial/padics/polynomial_padic_flat.py @@ -18,7 +18,15 @@ class Polynomial_padic_flat(Polynomial_generic_dense, Polynomial_padic): def __init__(self, parent, x=None, check=True, is_gen=False, construct=False, absprec=None): """ - Initialization function for the class Polynomial_padic_flat. + TESTS: + + Check that :trac:`13620` has been fixed:: + + sage: K = ZpFM(3) + sage: R. = K[] + sage: R(R.zero()) + 0 + """ if x is None: Polynomial_generic_dense.__init__(self, parent, x, check, is_gen, construct) @@ -41,7 +49,7 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct=False, ab if check: m = infinity zero = R(0) - n = max(x.keys()) + n = max(x.keys()) if len(x) else 0 v = [zero for _ in xrange(n+1)] for i, z in x.iteritems(): v[i] = R(z) From d4606cc69aad90e8693190dafce1d4948db0c46c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 6 Feb 2014 11:15:41 -0800 Subject: [PATCH 018/546] Added more robustness to element creation. --- src/sage/monoids/indexed_monoid.py | 61 ++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/src/sage/monoids/indexed_monoid.py b/src/sage/monoids/indexed_monoid.py index bc271610756..62bf984d610 100644 --- a/src/sage/monoids/indexed_monoid.py +++ b/src/sage/monoids/indexed_monoid.py @@ -35,9 +35,9 @@ class IndexedMonoidElement(MonoidElement): def __init__(self, F, x): """ Create the element ``x`` of an indexed free abelian monoid ``F``. - + EXAMPLES:: - + sage: F = IndexedFreeAbelianMonoid(ZZ) sage: F(1) 1 @@ -399,13 +399,50 @@ def trailing_support(self): return None return self._sorted_items()[-1][0] + def to_word_list(self): + """ + Return ``self`` as a word represented as a list whose entries + are indices of ``self``. + + EXAMPLES:: + + sage: F = IndexedFreeMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: (b*a*c^3*a).to_word_list() + [1, 0, 2, 2, 2, 0] + + :: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: (b*c^3*a).to_word_list() + [0, 1, 2, 2, 2] + """ + return [k for k,e in self._sorted_items() for dummy in range(e)] + class IndexedFreeMonoidElement(IndexedMonoidElement): """ An element of an indexed free abelian monoid. """ + def __init__(self, F, x): + """ + Create the element ``x`` of an indexed free abelian monoid ``F``. + + EXAMPLES:: + + sage: F = IndexedFreeMonoid('abcde') + sage: x = F( [(1, 2), (0, 1), (3, 2), (0, 1)] ) + sage: y = F( ((1, 2), (0, 1), [3, 2], [0, 1]) ) + sage: z = F( reversed([(0, 1), (3, 2), (0, 1), (1, 2)]) ) + sage: x == y and y == z + True + sage: TestSuite(x).run() + """ + IndexedMonoidElement.__init__(self, F, tuple(map(tuple, x))) + def _sorted_items(self): """ - Returns the items (i.e terms) of ``self``, sorted for printing + Return the items (i.e terms) of ``self``, sorted for printing. EXAMPLES:: @@ -485,9 +522,25 @@ class IndexedFreeAbelianMonoidElement(IndexedMonoidElement): """ An element of an indexed free abelian monoid. """ + def __init__(self, F, x): + """ + Create the element ``x`` of an indexed free abelian monoid ``F``. + + EXAMPLES:: + + sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: x = F([(0, 1), (2, 2), (-1, 2)]) + sage: y = F({0:1, 2:2, -1:2}) + sage: z = F(reversed([(0, 1), (2, 2), (-1, 2)])) + sage: x == y and y == z + True + sage: TestSuite(x).run() + """ + IndexedMonoidElement.__init__(self, F, dict(x)) + def _sorted_items(self): """ - Return the items (i.e syllables) of ``self``, sorted for printing. + Return the items (i.e terms) of ``self``, sorted for printing. EXAMPLES:: From 56703ebf108d92562eee067ba90f156a1bfaa50c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 11 Feb 2014 13:30:27 -0800 Subject: [PATCH 019/546] Made Indexed* have entry points through Free*. --- src/sage/groups/all.py | 1 - src/sage/groups/free_group.py | 21 ++- src/sage/groups/indexed_group.py | 146 ++++++++++++------ src/sage/monoids/all.py | 2 - src/sage/monoids/free_abelian_monoid.py | 49 +++++- src/sage/monoids/free_monoid.py | 58 ++++++- src/sage/monoids/indexed_monoid.py | 191 ++++++++++++------------ 7 files changed, 308 insertions(+), 160 deletions(-) diff --git a/src/sage/groups/all.py b/src/sage/groups/all.py index 69520ad4e69..8f473c530e5 100644 --- a/src/sage/groups/all.py +++ b/src/sage/groups/all.py @@ -17,7 +17,6 @@ lazy_import('sage.groups.free_group', 'FreeGroup') lazy_import('sage.groups.braid', 'BraidGroup') -lazy_import('sage.groups.indexed_group', ['IndexedFreeGroup', 'IndexedFreeAbelianGroup']) lazy_import('sage.groups.affine_gps.affine_group', 'AffineGroup') lazy_import('sage.groups.affine_gps.euclidean_group', 'EuclideanGroup') diff --git a/src/sage/groups/free_group.py b/src/sage/groups/free_group.py index 8eccf53adaf..3fcf51d7d2c 100644 --- a/src/sage/groups/free_group.py +++ b/src/sage/groups/free_group.py @@ -433,9 +433,9 @@ def __call__(self, *values): return prod( replace[gen] ** power for gen, power in self.syllables() ) -def FreeGroup(n=None, names='x'): +def FreeGroup(n=None, names='x', index_set=None, abelian=False, **kwds): """ - Construct a Free Group + Construct a Free Group. INPUT: @@ -445,6 +445,9 @@ def FreeGroup(n=None, names='x'): - ``names`` -- string or list/tuple/iterable of strings (default: ``'x'``). The generator names or name prefix. + - ``index_set`` -- (optional) an index set for the generators; if + specified then the optional keyword ``abelian`` can be used + EXAMPLES:: sage: G. = FreeGroup(); G @@ -466,6 +469,13 @@ def FreeGroup(n=None, names='x'): sage: FreeGroup() Free Group on generators {x} + We give two examples with the index set:: + + sage: FreeGroup(index_set=ZZ) + Free group indexed by Integer Ring + sage: FreeGroup(index_set=ZZ, abelian=True) + Free abelian group indexed by Integer Ring + TESTS:: sage: G1 = FreeGroup(2, 'a,b') @@ -490,6 +500,13 @@ def FreeGroup(n=None, names='x'): n = len(names) from sage.structure.parent import normalize_names names = tuple(normalize_names(n, names)) + if index_set is not None: + if abelian: + from sage.groups.indexed_group import IndexedFreeAbelianGroup + return IndexedFreeAbelianGroup(index_set, names=names, **kwds) + + from sage.groups.indexed_group import IndexedFreeGroup + return IndexedFreeGroup(index_set, names=names, **kwds) return FreeGroup_class(names) diff --git a/src/sage/groups/indexed_group.py b/src/sage/groups/indexed_group.py index 78a8970897c..6de82b1d9b3 100644 --- a/src/sage/groups/indexed_group.py +++ b/src/sage/groups/indexed_group.py @@ -34,10 +34,10 @@ class IndexedFreeGroup(IndexedFreeMonoid, Group): EXAMPLES:: - sage: G = IndexedFreeGroup(ZZ) + sage: G = FreeGroup(index_set=ZZ) sage: G Free group indexed by Integer Ring - sage: G = IndexedFreeGroup('abcde') + sage: G = FreeGroup(index_set='abcde') sage: G Free group indexed by {'a', 'b', 'c', 'd', 'e'} """ @@ -47,9 +47,9 @@ def __init__(self, indices, prefix, category=None, **kwds): TESTS:: - sage: G = IndexedFreeGroup(ZZ) + sage: G = FreeGroup(index_set=ZZ) sage: TestSuite(G).run() - sage: G = IndexedFreeGroup('abc') + sage: G = FreeGroup(index_set='abc') sage: TestSuite(G).run() """ Group.__init__(self) @@ -60,7 +60,7 @@ def _repr_(self): """ TESTS:: - sage: IndexedFreeGroup(ZZ) + sage: FreeGroup(index_set=ZZ) Free group indexed by Integer Ring """ return 'Free group indexed by {}'.format(self._indices) @@ -72,17 +72,17 @@ def order(self): EXAMPLES:: - sage: G = IndexedFreeGroup(ZZ) + sage: G = FreeGroup(index_set=ZZ) sage: G.order() +Infinity - sage: G = IndexedFreeGroup('abc') + sage: G = FreeGroup(index_set='abc') sage: G.order() +Infinity - sage: G = IndexedFreeGroup([]) + sage: G = FreeGroup(index_set=[]) sage: G.order() 1 """ - if self.ngens() == 0: + if self.is_finite(): return Integer(1) return infinity @@ -92,19 +92,36 @@ def is_finite(self): EXAMPLES:: - sage: G = IndexedFreeGroup(ZZ) + sage: G = FreeGroup(index_set=ZZ) sage: G.is_finite() False - sage: G = IndexedFreeGroup('abc') + sage: G = FreeGroup(index_set='abc') sage: G.is_finite() False - sage: G = IndexedFreeGroup([]) + sage: G = FreeGroup(index_set=[]) sage: G.is_finite() True """ - return self.ngens() == 0 + return self.rank() == 0 - rank = IndexedFreeMonoid.ngens + def rank(self): + """ + Return the rank of ``self``, which is the number of + generators of ``self``. + + EXAMPLES:: + + sage: G = FreeGroup(index_set=ZZ) + sage: G.rank() + +Infinity + sage: G = FreeGroup(index_set='abc') + sage: G.rank() + 3 + sage: G = FreeGroup(index_set=[]) + sage: G.rank() + 0 + """ + return self.gens().cardinality() class Element(IndexedFreeMonoid.Element): def __lt__(self, y): @@ -113,7 +130,7 @@ def __lt__(self, y): EXAMPLES:: - sage: F = IndexedFreeGroup(ZZ) + sage: F = FreeGroup(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: a < b True @@ -128,10 +145,7 @@ def __lt__(self, y): """ if not isinstance(y, IndexedMonoidElement): return False - sign = lambda x: 1 if x > 0 else -1 # It is never 0 - lhs = sum([[ (x[0], sign(x[1])) ]*abs(x[1]) for x in self._sorted_items()], []) - rhs = sum([[ (x[0], sign(x[1])) ]*abs(x[1]) for x in y._sorted_items()], []) - return lhs < rhs + return self.to_word_list() < y.to_word_list() def __len__(self): """ @@ -139,7 +153,7 @@ def __len__(self): EXAMPLES:: - sage: F = IndexedFreeGroup(ZZ) + sage: F = FreeGroup(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: elt = a*c^-3*b^-2*a sage: elt.length() @@ -147,7 +161,7 @@ def __len__(self): sage: len(elt) 7 - sage: F = IndexedFreeAbelianGroup(ZZ) + sage: F = FreeGroup(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: elt = a*c^-3*b^-2*a sage: elt.length() @@ -159,13 +173,13 @@ def __len__(self): length = __len__ - def _mul_(self, y): + def _mul_(self, other): """ - Multiply ``self`` by ``y``. + Multiply ``self`` by ``other``. EXAMPLES:: - sage: F = IndexedFreeGroup(ZZ) + sage: F = FreeGroup(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: a*b^2*e*d F[0]*F[1]^2*F[4]*F[3] @@ -175,12 +189,12 @@ def _mul_(self, y): 1 """ if len(self._monomial) == 0: - return y - if len(y._monomial) == 0: + return other + if len(other._monomial) == 0: return self ret = list(self._monomial) - rhs = list(y._monomial) + rhs = list(other._monomial) while len(ret) > 0 and len(rhs) > 0 and ret[-1][0] == rhs[0][0]: rhs[0] = (rhs[0][0], rhs[0][1] + ret.pop()[1]) if rhs[0][1] == 0: @@ -194,7 +208,7 @@ def __invert__(self): EXAMPLES:: - sage: F = IndexedFreeGroup(ZZ) + sage: F = FreeGroup(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: x = a*b^2*e^-1*d; ~x F[3]^-1*F[4]*F[1]^-2*F[0]^-1 @@ -209,7 +223,7 @@ def __pow__(self, n): EXAMPLES:: - sage: F = IndexedFreeGroup(ZZ) + sage: F = FreeGroup(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: x = a*b^2*e*a^-1; x F[0]*F[1]^2*F[4]*F[0]^-1 @@ -239,16 +253,33 @@ def __pow__(self, n): ret *= self return ret + def to_word_list(self): + """ + Return ``self`` as a word represented as a list whose entries + are the pairs ``(i, s)`` where ``i`` is the index and ``s`` is + the sign. + + EXAMPLES:: + + sage: F = FreeGroup(index_set=ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: x = a*b^2*e*a^-1 + sage: x.to_word_list() + [(0, 1), (1, 1), (1, 1), (4, 1), (0, -1)] + """ + sign = lambda x: 1 if x > 0 else -1 # It is never 0 + return [ (k, sign(e)) for k,e in self._sorted_items() for dummy in range(abs(e))] + class IndexedFreeAbelianGroup(IndexedFreeAbelianMonoid, AbelianGroup): """ An indexed free abelian group. EXAMPLES:: - sage: G = IndexedFreeAbelianGroup(ZZ) + sage: G = FreeGroup(index_set=ZZ, abelian=True) sage: G Free abelian group indexed by Integer Ring - sage: G = IndexedFreeAbelianGroup('abcde') + sage: G = FreeGroup(index_set='abcde', abelian=True) sage: G Free abelian group indexed by {'a', 'b', 'c', 'd', 'e'} """ @@ -258,9 +289,9 @@ def __init__(self, indices, prefix, category=None, **kwds): TESTS:: - sage: G = IndexedFreeAbelianGroup(ZZ) + sage: G = FreeGroup(index_set=ZZ, abelian=True) sage: TestSuite(G).run() - sage: G = IndexedFreeAbelianGroup('abc') + sage: G = FreeGroup(index_set='abc', abelian=True) sage: TestSuite(G).run() """ AbelianGroup.__init__(self) @@ -271,7 +302,7 @@ def _repr_(self): """ TESTS:: - sage: IndexedFreeAbelianGroup(ZZ) + sage: FreeGroup(index_set=ZZ, abelian=True) Free abelian group indexed by Integer Ring """ return 'Free abelian group indexed by {}'.format(self._indices) @@ -283,17 +314,17 @@ def order(self): EXAMPLES:: - sage: G = IndexedFreeAbelianGroup(ZZ) + sage: G = FreeGroup(index_set=ZZ, abelian=True) sage: G.order() +Infinity - sage: G = IndexedFreeAbelianGroup('abc') + sage: G = FreeGroup(index_set='abc', abelian=True) sage: G.order() +Infinity - sage: G = IndexedFreeAbelianGroup([]) + sage: G = FreeGroup(index_set=[], abelian=True) sage: G.order() 1 """ - if self.ngens() == 0: + if self.is_finite(): return Integer(1) return infinity @@ -303,28 +334,45 @@ def is_finite(self): EXAMPLES:: - sage: G = IndexedFreeAbelianGroup(ZZ) + sage: G = FreeGroup(index_set=ZZ, abelian=True) sage: G.is_finite() False - sage: G = IndexedFreeAbelianGroup('abc') + sage: G = FreeGroup(index_set='abc', abelian=True) sage: G.is_finite() False - sage: G = IndexedFreeAbelianGroup([]) + sage: G = FreeGroup(index_set=[], abelian=True) sage: G.is_finite() True """ - return self.ngens() == 0 + return self.rank() == 0 + + def rank(self): + """ + Return the rank of ``self``, which is the number of + generators of ``self``. + + EXAMPLES:: - rank = IndexedFreeAbelianMonoid.ngens + sage: G = FreeGroup(index_set=ZZ, abelian=True) + sage: G.rank() + +Infinity + sage: G = FreeGroup(index_set='abc', abelian=True) + sage: G.rank() + 3 + sage: G = FreeGroup(index_set=[], abelian=True) + sage: G.rank() + 0 + """ + return self.gens().cardinality() class Element(IndexedFreeAbelianMonoid.Element, IndexedFreeGroup.Element): - def _mul_(self, y): + def _mul_(self, other): """ - Multiply ``self`` by ``y``. + Multiply ``self`` by ``other``. EXAMPLES:: - sage: F = IndexedFreeAbelianGroup(ZZ) + sage: F = FreeGroup(index_set=ZZ, abelian=True) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: a*b^2*e^-1*d F[0]*F[1]^2*F[3]*F[4]^-1 @@ -334,7 +382,7 @@ def _mul_(self, y): 1 """ ret = copy(self._monomial) - for k,v in y._monomial.iteritems(): + for k,v in other._monomial.iteritems(): ret[k] = ret.get(k, 0) + v if ret[k] == 0: del ret[k] @@ -346,7 +394,7 @@ def __invert__(self): EXAMPLES:: - sage: F = IndexedFreeAbelianGroup(ZZ) + sage: F = FreeGroup(index_set=ZZ, abelian=True) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: x = a*b^2*e^-1*d; ~x F[0]^-1*F[1]^-2*F[3]^-1*F[4] @@ -361,7 +409,7 @@ def __pow__(self, n): EXAMPLES:: - sage: F = IndexedFreeAbelianGroup(ZZ) + sage: F = FreeGroup(index_set=ZZ, abelian=True) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: x = a*b^2*e^-1*d; x F[0]*F[1]^2*F[3]*F[4]^-1 diff --git a/src/sage/monoids/all.py b/src/sage/monoids/all.py index 8a666f58f90..0f5c6f6a9dc 100644 --- a/src/sage/monoids/all.py +++ b/src/sage/monoids/all.py @@ -21,5 +21,3 @@ from free_abelian_monoid_element import is_FreeAbelianMonoidElement -from indexed_monoid import IndexedFreeMonoid, IndexedFreeAbelianMonoid - diff --git a/src/sage/monoids/free_abelian_monoid.py b/src/sage/monoids/free_abelian_monoid.py index ea8fb3f53b1..755c52aa25e 100644 --- a/src/sage/monoids/free_abelian_monoid.py +++ b/src/sage/monoids/free_abelian_monoid.py @@ -101,8 +101,55 @@ def create_key(self, n, names): def create_object(self, version, key): return FreeAbelianMonoid_class(*key) -FreeAbelianMonoid = FreeAbelianMonoidFactory("FreeAbelianMonoid") +FreeAbelianMonoid_factory = FreeAbelianMonoidFactory("sage.monoids.free_abelian_monoid.FreeAbelianMonoid_factory") +def FreeAbelianMonoid(n=None, names=None, index_set=None, **kwds): + """ + Return a free abelian monoid on `n` generators or with the generators + indexed by a set `I`. + + We construct free abelian monoids by specifing either: + + - the number of generators and/or the names of the generators + - the indexing set for the generators (this ignores the other two inputs) + + INPUT: + + - ``n`` -- integer + + - ``names`` -- names of generators + + - ``index_set`` -- an indexing set for the generators + + OUTPUT: + + A free abelian monoid. + + EXAMPLES:: + + sage: F. = FreeAbelianMonoid(); F + Free abelian monoid on 5 generators (a, b, c, d, e) + sage: FreeAbelianMonoid(index_set=ZZ) + Free abelian monoid indexed by Integer Ring + """ + if isinstance(n, str): # Swap args (this works if names is None as well) + names, n = n, names + + if n is None and names is not None: + if isinstance(names, str): + n = names.count(',') + else: + n = len(names) + + if index_set is not None: + if names is not None: + names = normalize_names(int(n), names) + from sage.monoids.indexed_monoid import IndexedFreeAbelianMonoid + return IndexedFreeAbelianMonoid(index_set, names=names, **kwds) + + if names is None: + raise ValueError("names must be specified") + return FreeAbelianMonoid_factory(n, names) def is_FreeAbelianMonoid(x): """ diff --git a/src/sage/monoids/free_monoid.py b/src/sage/monoids/free_monoid.py index e169d570298..676c81ce0ca 100644 --- a/src/sage/monoids/free_monoid.py +++ b/src/sage/monoids/free_monoid.py @@ -43,17 +43,15 @@ class FreeMonoidFactory(UniqueFactory): """ - Returns a free monoid on `n` generators. + Create the free monoid in `n` generators. INPUT: - - ``n`` - integer - ``names`` - names of generators - - OUTPUT: free abelian monoid + OUTPUT: free monoid EXAMPLES:: @@ -70,12 +68,58 @@ def create_key(self, n, names): n = int(n) names = normalize_names(n, names) return (n, names) - - def create_object(self, version, key): + def create_object(self, version, key, **kwds): return FreeMonoid_class(*key) -FreeMonoid = FreeMonoidFactory("FreeMonoid") +FreeMonoid_factory = FreeMonoidFactory("sage.monoids.free_monoid.FreeMonoid_factory") + +def FreeMonoid(n=None, names=None, index_set=None, **kwds): + r""" + Return a free monoid on `n` generators or with the generators indexed by + a set `I`. + + We construct free monoids by specifing either: + + - the number of generators and/or the names of the generators + - the indexing set for the generators + + INPUT: + + - ``n`` -- integer + - ``names`` -- names of generators + + - ``index_set`` -- an indexing set for the generators + + OUTPUT: + + A free monoid. + + EXAMPLES:: + + sage: F. = FreeMonoid(); F + Free monoid on 5 generators (a, b, c, d, e) + sage: FreeMonoid(index_set=ZZ) + Free monoid indexed by Integer Ring + """ + if isinstance(n, str): # Swap args (this works if names is None as well) + names, n = n, names + + if n is None and names is not None: + if isinstance(names, str): + n = names.count(',') + else: + n = len(names) + + if index_set is not None: + if names is not None: + names = normalize_names(n, names) + from sage.monoids.indexed_monoid import IndexedFreeMonoid + return IndexedFreeMonoid(index_set, names=names, **kwds) + + if names is None: + raise ValueError("names must be specified") + return FreeMonoid_factory(n, names) def is_FreeMonoid(x): """ diff --git a/src/sage/monoids/indexed_monoid.py b/src/sage/monoids/indexed_monoid.py index 62bf984d610..e921620759c 100644 --- a/src/sage/monoids/indexed_monoid.py +++ b/src/sage/monoids/indexed_monoid.py @@ -31,6 +31,19 @@ class IndexedMonoidElement(MonoidElement): """ An element of an indexed monoid. + + This is an abstract class which uses the (abstract) method + :meth:`_sorted_items` for all of it's functions. So to implement an + element of an indexed monoid, one just needs to implement + :meth:`_sorted_items`, which returns an list of pairs ``(i, p)`` where + ``i`` is the index and ``p`` is the corresponding power, sorted in some + order. For example, in the free monoid there is no such choice, but for + the free abelian monoid, one could want lex order or have the highest + powers first. + + Indexed monoid elements are ordered lexicographically based upon the + order of word from :meth:`_sorted_items` and the order of the indexing + set. """ def __init__(self, F, x): """ @@ -38,7 +51,7 @@ def __init__(self, F, x): EXAMPLES:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: F(1) 1 sage: a,b,c,d,e = [F.gen(i) for i in range(5)] @@ -46,7 +59,7 @@ def __init__(self, F, x): F[0]^4*F[1]^7 sage: TestSuite(x).run() - sage: F = IndexedFreeMonoid('abcde') + sage: F = FreeMonoid(index_set='abcde') sage: a,b,c,d,e = F.gens() sage: a in F True @@ -64,7 +77,7 @@ def _sorted_items(self): EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: x = a*b^2*e*d sage: x._sorted_items() @@ -81,7 +94,7 @@ def _repr_(self): EXAMPLES:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: a*b^2*e*d F[0]*F[1]^2*F[3]*F[4] @@ -103,7 +116,7 @@ def _ascii_art_(self): EXAMPLES:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: ascii_art(a*e*d) F *F *F @@ -145,7 +158,7 @@ def _latex_(self): EXAMPLES:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: latex(a*b^2*e*d) F_{0} F_{1}^{2} F_{3} F_{4} @@ -171,14 +184,14 @@ def __iter__(self): EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: list(b*a*c^3*b) [(F[1], 1), (F[0], 1), (F[2], 3), (F[1], 1)] :: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: list(b*c^3*a) [(F[0], 1), (F[1], 1), (F[2], 3)] @@ -191,7 +204,7 @@ def __eq__(self, y): EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: a == a True @@ -200,7 +213,7 @@ def __eq__(self, y): sage: a*b*c^3*b*d == (a*b*c)*(c^2*b*d) True - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: a == a True @@ -219,7 +232,7 @@ def __ne__(self, y): EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: a != b True @@ -228,7 +241,7 @@ def __ne__(self, y): sage: a*b*c^3*b*d != (a*b*c)*(c^2*b*d) False - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: a != b True @@ -245,7 +258,7 @@ def __lt__(self, y): EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: a < b True @@ -258,9 +271,7 @@ def __lt__(self, y): """ if not isinstance(y, IndexedMonoidElement): return False - lhs = sum([[x[0]]*x[1] for x in self._sorted_items()], []) - rhs = sum([[x[0]]*x[1] for x in y._sorted_items()], []) - return lhs < rhs + return self.to_word_list() < y.to_word_list() def __gt__(self, y): """ @@ -268,7 +279,7 @@ def __gt__(self, y): EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: b > a True @@ -287,7 +298,7 @@ def __le__(self, y): EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: a*b <= b*a True @@ -300,7 +311,7 @@ def __ge__(self, y): EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: a*b <= b*a True @@ -313,7 +324,7 @@ def __len__(self): EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: elt = a*c^3*b^2*a sage: elt.length() @@ -321,7 +332,7 @@ def __len__(self): sage: len(elt) 7 - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: elt = a*c^3*b^2*a sage: elt.length() @@ -340,14 +351,14 @@ def support(self): EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: (b*a*c^3*b).support() [0, 1, 2] :: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: (a*c^3).support() [0, 2] @@ -361,14 +372,14 @@ def leading_support(self): EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: (b*a*c^3*a).leading_support() 1 :: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: (b*c^3*a).leading_support() 0 @@ -383,14 +394,14 @@ def trailing_support(self): EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: (b*a*c^3*a).trailing_support() 0 :: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: (b*c^3*a).trailing_support() 2 @@ -406,14 +417,14 @@ def to_word_list(self): EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: (b*a*c^3*a).to_word_list() [1, 0, 2, 2, 2, 0] :: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: (b*c^3*a).to_word_list() [0, 1, 2, 2, 2] @@ -430,7 +441,7 @@ def __init__(self, F, x): EXAMPLES:: - sage: F = IndexedFreeMonoid('abcde') + sage: F = FreeMonoid(index_set='abcde') sage: x = F( [(1, 2), (0, 1), (3, 2), (0, 1)] ) sage: y = F( ((1, 2), (0, 1), [3, 2], [0, 1]) ) sage: z = F( reversed([(0, 1), (3, 2), (0, 1), (1, 2)]) ) @@ -446,7 +457,7 @@ def _sorted_items(self): EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: x = a*b^2*e*d sage: x._sorted_items() @@ -462,13 +473,13 @@ def _sorted_items(self): """ return self._monomial - def _mul_(self, y): + def _mul_(self, other): """ - Multiply ``self`` by ``y``. + Multiply ``self`` by ``other``. EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: a*b^2*e*d F[0]*F[1]^2*F[4]*F[3] @@ -476,12 +487,12 @@ def _mul_(self, y): F[0]*F[1]^2*F[3]^6*F[1]*F[4] """ if len(self._monomial) == 0: - return y - if len(y._monomial) == 0: + return other + if len(other._monomial) == 0: return self ret = list(self._monomial) - rhs = list(y._monomial) + rhs = list(other._monomial) if ret[-1][0] == rhs[0][0]: rhs[0] = (rhs[0][0], rhs[0][1] + ret.pop()[1]) ret += rhs @@ -493,7 +504,7 @@ def __pow__(self, n): EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: x = a*b^2*e*d*a; x F[0]*F[1]^2*F[4]*F[3]*F[0] @@ -528,7 +539,7 @@ def __init__(self, F, x): EXAMPLES:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: x = F([(0, 1), (2, 2), (-1, 2)]) sage: y = F({0:1, 2:2, -1:2}) sage: z = F(reversed([(0, 1), (2, 2), (-1, 2)])) @@ -544,7 +555,7 @@ def _sorted_items(self): EXAMPLES:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: x = a*b^2*e*d sage: x._sorted_items() @@ -566,19 +577,19 @@ def _sorted_items(self): pass return v - def _mul_(self, y): + def _mul_(self, other): """ - Multiply ``self`` by ``y``. + Multiply ``self`` by ``other``. EXAMPLES:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: a*b^2*e*d F[0]*F[1]^2*F[3]*F[4] """ ret = copy(self._monomial) - for k,v in y._monomial.iteritems(): + for k,v in other._monomial.iteritems(): ret[k] = ret.get(k, 0) + v return self.__class__(self.parent(), ret) @@ -588,7 +599,7 @@ def __pow__(self, n): EXAMPLES:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: x = a*b^2*e*d; x F[0]*F[1]^2*F[3]*F[4] @@ -613,7 +624,7 @@ def dict(self): EXAMPLES:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: (a*c^3).dict() {0: 1, 2: 3} @@ -626,7 +637,7 @@ def cancel(self, elt): EXAMPLES:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: elt = a*b*c^3*d^2; elt F[0]*F[1]*F[2]^3*F[3]^2 @@ -663,13 +674,13 @@ def __classcall__(cls, indices, prefix="F", **kwds): """ TESTS:: - sage: F = IndexedFreeAbelianMonoid(['a','b','c']) - sage: G = IndexedFreeAbelianMonoid(('a','b','c')) - sage: H = IndexedFreeAbelianMonoid('abc') + sage: F = FreeAbelianMonoid(index_set=['a','b','c']) + sage: G = FreeAbelianMonoid(index_set=('a','b','c')) + sage: H = FreeAbelianMonoid(index_set='abc') sage: F is G and F is H True - sage: F = IndexedFreeAbelianMonoid(['a','b','c'], latex_bracket=['LEFT', 'RIGHT']) + sage: F = FreeAbelianMonoid(index_set=['a','b','c'], latex_bracket=['LEFT', 'RIGHT']) sage: F.print_options()['latex_bracket'] ('LEFT', 'RIGHT') sage: F is G @@ -690,20 +701,25 @@ def __classcall__(cls, indices, prefix="F", **kwds): kwds['latex_bracket'] = tuple(latex_bracket) return super(IndexedMonoid, cls).__classcall__(cls, indices, prefix, **kwds) - def __init__(self, indices, prefix, category=None, **kwds): + def __init__(self, indices, prefix, category=None, names=None, **kwds): """ Initialize ``self``. EXAMPLES:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) + sage: TestSuite(F).run() + sage: F = FreeMonoid(index_set='abcde') + sage: TestSuite(F).run() + + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: TestSuite(F).run() - sage: F = IndexedFreeAbelianMonoid('abcde') + sage: F = FreeAbelianMonoid(index_set='abcde') sage: TestSuite(F).run() """ self._indices = indices category = Monoids().or_subcategory(category) - Parent.__init__(self, category=category) + Parent.__init__(self, names=names, category=category) # ignore the optional 'key' since it only affects CachedRepresentation kwds.pop('key', None) @@ -715,7 +731,7 @@ def _element_constructor_(self, x=None): EXAMPLES:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: F(F.gen(2)) F[2] sage: F(-5) @@ -739,10 +755,10 @@ def _an_element_(self): EXAMPLES:: - sage: G = IndexedFreeAbelianMonoid(ZZ) + sage: G = FreeAbelianMonoid(index_set=ZZ) sage: G.an_element() F[1] - sage: G = IndexedFreeMonoid('abc') + sage: G = FreeMonoid(index_set='abc') sage: G.an_element() F['a']*F['b']*F['c'] """ @@ -756,14 +772,14 @@ def __contains__(self, x): EXAMPLES:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: F.gen(2)*F.gen(3) in F True Note that a monoid on `\NN` generators is not considered a submonoid of one on `\ZZ` generators:: - sage: IndexedFreeAbelianMonoid(NN).gen(2) in F + sage: FreeAbelianMonoid(index_set=NN).gen(2) in F False """ return isinstance(x, self.element_class) and x.parent() is self @@ -774,11 +790,11 @@ def gens(self): EXAMPLES:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: F.gens() Lazy family (Generator map from Integer Ring to Free abelian monoid indexed by Integer Ring(i))_{i in Integer Ring} - sage: F = IndexedFreeAbelianMonoid('abcde') + sage: F = FreeAbelianMonoid(index_set='abcde') sage: F.gens() Finite family {'a': F['a'], 'c': F['c'], 'b': F['b'], 'e': F['e'], 'd': F['d']} """ @@ -787,21 +803,6 @@ def gens(self): return Family(self._indices, gen) return Family(self._indices, self.gen) - def ngens(self): - """ - The number of free generators of ``self``. - - EXAMPLES:: - - sage: F = IndexedFreeAbelianMonoid(ZZ) - sage: F.ngens() - +Infinity - sage: F = IndexedFreeAbelianMonoid('abcde') - sage: F.ngens() - 5 - """ - return self._indices.cardinality() - class IndexedFreeMonoid(IndexedMonoid): """ Free monoid with an indexed set of generators. @@ -818,7 +819,7 @@ class IndexedFreeMonoid(IndexedMonoid): If there is ambiguity with `1`, we construct the identity in the monoid instead of the generator:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: F(1) 1 sage: F.gen(1) @@ -826,18 +827,15 @@ class IndexedFreeMonoid(IndexedMonoid): EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: F.gen(15)^3 * F.gen(2) * F.gen(15) F[15]^3*F[2]*F[15] Now we examine some of the printing options:: - sage: F = IndexedFreeMonoid(ZZ, prefix='X', bracket=['|','>']) + sage: F = FreeMonoid(index_set=ZZ, prefix='X', bracket=['|','>']) sage: F.gen(2) * F.gen(12) X|2>*X|12> - sage: F = IndexedFreeAbelianMonoid(Partitions(), prefix='A', bracket=False, scalar_mult='%') - sage: F.gen([3,1,1]) * F.gen([2,2]) - A[2, 2]%A[3, 1, 1] """ def _repr_(self): """ @@ -845,7 +843,7 @@ def _repr_(self): EXAMPLES:: - sage: IndexedFreeMonoid(ZZ) + sage: FreeMonoid(index_set=ZZ) Free monoid indexed by Integer Ring """ return "Free monoid indexed by {}".format(self._indices) @@ -859,7 +857,7 @@ def one(self): EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: F.one() 1 """ @@ -871,7 +869,7 @@ def gen(self, x): EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: F.gen(0) F[0] sage: F.gen(2) @@ -890,7 +888,7 @@ def cardinality(self): EXAMPLES:: - sage: F = IndexedFreeMonoid(ZZ) + sage: F = FreeMonoid(index_set=ZZ) sage: F.cardinality() +Infinity """ @@ -912,7 +910,7 @@ class IndexedFreeAbelianMonoid(IndexedMonoid): If there is ambiguity with `1`, we construct the identity in the monoid instead of the generator:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: F(1) 1 sage: F.gen(1) @@ -920,16 +918,13 @@ class IndexedFreeAbelianMonoid(IndexedMonoid): EXAMPLES:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: F.gen(15)^3 * F.gen(2) * F.gen(15) F[2]*F[15]^4 Now we examine some of the printing options:: - sage: F = IndexedFreeAbelianMonoid(ZZ, prefix='X', bracket=['|','>']) - sage: F.gen(2) * F.gen(12) - X|2>*X|12> - sage: F = IndexedFreeAbelianMonoid(Partitions(), prefix='A', bracket=False, scalar_mult='%') + sage: F = FreeAbelianMonoid(index_set=Partitions(), prefix='A', bracket=False, scalar_mult='%') sage: F.gen([3,1,1]) * F.gen([2,2]) A[2, 2]%A[3, 1, 1] """ @@ -939,7 +934,7 @@ def _repr_(self): EXAMPLES:: - sage: IndexedFreeAbelianMonoid(ZZ) + sage: FreeAbelianMonoid(index_set=ZZ) Free abelian monoid indexed by Integer Ring """ return "Free abelian monoid indexed by {}".format(self._indices) @@ -950,7 +945,7 @@ def _element_constructor_(self, x=None): EXAMPLES:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: F(F.gen(2)) F[2] sage: F(-5) @@ -973,7 +968,7 @@ def one(self): EXAMPLES:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: F.one() 1 """ @@ -985,7 +980,7 @@ def gen(self, x): EXAMPLES:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: F.gen(0) F[0] sage: F.gen(2) @@ -1004,7 +999,7 @@ def cardinality(self): EXAMPLES:: - sage: F = IndexedFreeAbelianMonoid(ZZ) + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: F.cardinality() +Infinity """ From 163df6e7f6b29e53c0844995389f52c198cffcc1 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 11 Feb 2014 13:36:43 -0800 Subject: [PATCH 020/546] Changed more _basis_keys to _indices, deprecated the former. --- src/sage/combinat/combinatorial_algebra.py | 2 +- src/sage/combinat/free_module.py | 21 +++++++------------ .../combinat/ncsf_qsym/generic_basis_code.py | 6 +++--- src/sage/combinat/ncsf_qsym/ncsf.py | 20 +++++++++--------- src/sage/combinat/ncsf_qsym/qsym.py | 14 ++++++------- src/sage/combinat/symmetric_group_algebra.py | 6 +++--- 6 files changed, 31 insertions(+), 38 deletions(-) diff --git a/src/sage/combinat/combinatorial_algebra.py b/src/sage/combinat/combinatorial_algebra.py index c03ce567a5e..16a64ed2cc8 100644 --- a/src/sage/combinat/combinatorial_algebra.py +++ b/src/sage/combinat/combinatorial_algebra.py @@ -194,7 +194,7 @@ def __init__(self, R, cc = None, element_class = None, category = None): # for backward compatibility if cc is None: if hasattr(self, "_basis_keys"): - cc = self._basis_keys + cc = self._indices elif hasattr(self, "_indices"): cc = self._indices assert(cc is not None) diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index 5fba59d417b..e566f2c8adc 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -1564,28 +1564,21 @@ def _an_element_impl(self): """ return self.element_class(self, {}) - def combinatorial_class(self): + @lazy_attribute + def _basis_keys(self): """ - Returns the combinatorial class that indexes the basis elements. - - Deprecated: use self.basis().keys() instead. + Deprecated: use ``self._indices`` instead. EXAMPLES:: sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c']) - sage: F.combinatorial_class() - doctest:...: DeprecationWarning: "FM.combinatorial_class()" is deprecated. Use "F.basis().keys()" instead ! - See http://trac.sagemath.org/6136 for details. + sage: F._basis_keys + doctest:...: DeprecationWarning: "FM._basis_keys" is deprecated. Use "F._indices" instead! + See http://trac.sagemath.org/15289 for details. {'a', 'b', 'c'} - - :: - - sage: s = SymmetricFunctions(QQ).schur() - sage: s.combinatorial_class() - Partitions """ from sage.misc.superseded import deprecation - deprecation(6136, '"FM.combinatorial_class()" is deprecated. Use "F.basis().keys()" instead !') + deprecation(15289, '"FM._basis_keys" is deprecated. Use "F._indices" instead!') return self._indices def dimension(self): diff --git a/src/sage/combinat/ncsf_qsym/generic_basis_code.py b/src/sage/combinat/ncsf_qsym/generic_basis_code.py index 378f48fd54d..47b8064606b 100644 --- a/src/sage/combinat/ncsf_qsym/generic_basis_code.py +++ b/src/sage/combinat/ncsf_qsym/generic_basis_code.py @@ -114,9 +114,9 @@ def __getitem__(self, c, *rest): assert len(rest) == 0 else: if len(rest) > 0 or isinstance(c, (int, Integer)): - c = self._basis_keys([c] + list(rest)) + c = self._indices([c] + list(rest)) else: - c = self._basis_keys(list(c)) + c = self._indices(list(c)) return self.monomial(c) # could go to Algebras(...).Graded().Connected() or Modules(...).Graded().Connected() @@ -294,7 +294,7 @@ def sum_of_partition_rearrangements(self, par): sage: elementary.sum_of_partition_rearrangements(Partition([])) L[] """ - return self.sum_of_monomials( self._basis_keys(comp) for comp in Permutations(par) ) + return self.sum_of_monomials( self._indices(comp) for comp in Permutations(par) ) def _comp_to_par(self, comp): """ diff --git a/src/sage/combinat/ncsf_qsym/ncsf.py b/src/sage/combinat/ncsf_qsym/ncsf.py index 8a48f910d88..e028984db1c 100644 --- a/src/sage/combinat/ncsf_qsym/ncsf.py +++ b/src/sage/combinat/ncsf_qsym/ncsf.py @@ -686,7 +686,7 @@ def verschiebung(self, n): # componentwise, then convert back. parent = self.parent() S = parent.realization_of().S() - C = parent._basis_keys + C = parent._indices dct = {C(map(lambda i: i // n, I)): coeff for (I, coeff) in S(self).monomial_coefficients().items() if all(i % n == 0 for i in I)} @@ -1132,7 +1132,7 @@ def algebra_generators(self): """ from sage.sets.family import Family from sage.sets.positive_integers import PositiveIntegers - return Family(PositiveIntegers(), lambda i: self.monomial(self._basis_keys([i]))) + return Family(PositiveIntegers(), lambda i: self.monomial(self._indices([i]))) def product_on_basis(self, composition1, composition2): """ @@ -1377,7 +1377,7 @@ def coproduct_on_generators(self, i): """ if i<1: return "Not a positive integer: %s" % `i` - def C(i): return self._basis_keys([i]) if i else self._basis_keys([]) + def C(i): return self._indices([i]) if i else self._indices([]) T = self.tensor_square() return T.sum_of_monomials( (C(j), C(i-j)) for j in range(0,i+1) ) @@ -1620,8 +1620,8 @@ def product_on_basis(self, I, J): elif J == []: return self.monomial(I) else: - return self.monomial(self._basis_keys(I[:] + J[:])) + \ - self.monomial(self._basis_keys(I[:-1] + [I[-1]+J[0]] + J[1:])) + return self.monomial(self._indices(I[:] + J[:])) + \ + self.monomial(self._indices(I[:-1] + [I[-1]+J[0]] + J[1:])) def antipode_on_basis(self, composition): """ @@ -1815,7 +1815,7 @@ def verschiebung(self, n): True """ parent = self.parent() - C = parent._basis_keys + C = parent._indices def ribbon_mapper(I, coeff): # return \mathbf{V}_n ( coeff * R_I ) as pair # (composition, coefficient) @@ -2588,7 +2588,7 @@ def _from_complete_on_generators(self, n): """ # Equation (58) of NCSF I article one = self.base_ring().one() - I = self._basis_keys([n]) + I = self._indices([n]) # TODO: I being trivial, there is no refinement going on here, so # one can probably be a bit more explicit / fast return self.sum_of_terms((J, one/coeff_pi(J,I)) for J in Compositions(n)) @@ -2797,7 +2797,7 @@ def verschiebung(self, n): True """ parent = self.parent() - C = parent._basis_keys + C = parent._indices return parent.sum_of_terms([(C([i // n for i in I]), coeff * (n ** len(I))) for (I, coeff) in self @@ -3068,7 +3068,7 @@ def verschiebung(self, n): True """ parent = self.parent() - C = parent._basis_keys + C = parent._indices return parent.sum_of_terms([(C([i // n for i in I]), coeff * (n ** len(I))) for (I, coeff) in self @@ -3333,7 +3333,7 @@ def _from_psi_on_basis(self, I): for J in Compositions(I.size()): if I.is_finer(J): len_of_J = len(J) - p = [0] + self._basis_keys(I).refinement_splitting_lengths(J).partial_sums() + p = [0] + self._indices(I).refinement_splitting_lengths(J).partial_sums() sum_of_elements += prod( (len_of_J - k)**(p[k+1]-p[k]) for k in range(len_of_J) ) * M(J) return sum_of_elements diff --git a/src/sage/combinat/ncsf_qsym/qsym.py b/src/sage/combinat/ncsf_qsym/qsym.py index dcc5ce779e8..b8cefbe5e2b 100644 --- a/src/sage/combinat/ncsf_qsym/qsym.py +++ b/src/sage/combinat/ncsf_qsym/qsym.py @@ -1093,7 +1093,7 @@ def frobenius(self, n): # then convert back. parent = self.parent() M = parent.realization_of().M() - C = parent._basis_keys + C = parent._indices dct = {C(map(lambda i: n * i, I)): coeff for (I, coeff) in M(self).monomial_coefficients().items()} result_in_M_basis = M._from_dict(dct) @@ -1488,8 +1488,8 @@ def coproduct_on_basis(self, compo): sage: M.coproduct_on_basis(Composition([])) M[] # M[] """ - return self.tensor_square().sum_of_monomials((self._basis_keys(compo[:i]), - self._basis_keys(compo[i:])) + return self.tensor_square().sum_of_monomials((self._indices(compo[:i]), + self._indices(compo[i:])) for i in range(0,len(compo)+1)) def lambda_of_monomial(self, I, n): @@ -1597,7 +1597,7 @@ def lambda_of_monomial(self, I, n): QQ_result = QQM.zero() for lam in Partitions(n): coeff = QQ((-1) ** len(lam)) / lam.centralizer_size() - QQ_result += coeff * QQM.prod([QQM(self._basis_keys([k * i for i in I])) + QQ_result += coeff * QQM.prod([QQM(self._indices([k * i for i in I])) for k in lam]) QQ_result *= (-1) ** n # QQ_result is now \lambda^n(M_I) over QQ. @@ -2876,7 +2876,7 @@ def _precompute_cache(self, n, to_self_cache, from_self_cache, transition_matric # Handle the n == 0 case separately if n == 0: - part = self._basis_keys([]) + part = self._indices([]) to_self_cache[ part ] = { part: base_ring(1) } from_self_cache[ part ] = { part: base_ring(1) } transition_matrices[n] = matrix(base_ring, [[1]]) @@ -2904,7 +2904,7 @@ def _precompute_cache(self, n, to_self_cache, from_self_cache, transition_matric # M_coeffs will be M(self[I])._monomial_coefficients M_coeffs = {} - self_I_in_M_basis = M.prod([from_self_gen_function(self._basis_keys(list(J))) + self_I_in_M_basis = M.prod([from_self_gen_function(self._indices(list(J))) for J in Word(I).lyndon_factorization()]) j = 0 @@ -3128,5 +3128,5 @@ def product_on_basis(self, I, J): # either some i satisfies a_i > b_i and (a_j == b_j for all # j < i), or we have n > m and all i <= m satisfy a_i == b_i. new_factors = sorted(I_factors + J_factors, reverse=True) - return self.monomial(self._basis_keys(flatten(new_factors))) + return self.monomial(self._indices(flatten(new_factors))) diff --git a/src/sage/combinat/symmetric_group_algebra.py b/src/sage/combinat/symmetric_group_algebra.py index 418a5ac2a14..ddc9fe8c64e 100644 --- a/src/sage/combinat/symmetric_group_algebra.py +++ b/src/sage/combinat/symmetric_group_algebra.py @@ -791,7 +791,7 @@ def jucys_murphy(self, k): p = range(1, self.n+1) p[i-1] = k p[k-1] = i - res += self.monomial(self._basis_keys(p)) + res += self.monomial(self._indices(p)) return res @@ -1488,7 +1488,7 @@ def _coerce_start(self, x): if x == []: return self.one() if len(x) < self.n and x in permutation.Permutations(): - return self.monomial(self._basis_keys( list(x) + range(len(x)+1, self.n+1) )) + return self.monomial(self._indices( list(x) + range(len(x)+1, self.n+1) )) raise TypeError class HeckeAlgebraSymmetricGroup_t(HeckeAlgebraSymmetricGroup_generic): @@ -1535,7 +1535,7 @@ def t_action_on_basis(self, perm, i): # -- Darij, 19 Nov 2013 if perm[i-1] < perm[i]: - return self.monomial(self._basis_keys(perm_i)) + return self.monomial(self._indices(perm_i)) else: #Ti^2 = (q - q^(-1))*Ti - q1*q2 q = self.q() From 8db8e0a07605c801a9a9974e043312061ee375c1 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 11 Feb 2014 13:47:03 -0800 Subject: [PATCH 021/546] Changed _an_element_ to indexed_monoid.py. --- src/sage/monoids/indexed_monoid.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/sage/monoids/indexed_monoid.py b/src/sage/monoids/indexed_monoid.py index e921620759c..f86ce3810fc 100644 --- a/src/sage/monoids/indexed_monoid.py +++ b/src/sage/monoids/indexed_monoid.py @@ -757,14 +757,24 @@ def _an_element_(self): sage: G = FreeAbelianMonoid(index_set=ZZ) sage: G.an_element() - F[1] - sage: G = FreeMonoid(index_set='abc') + F[-1]^3*F[0]*F[1]^3 + sage: G = FreeMonoid(index_set='ab') sage: G.an_element() - F['a']*F['b']*F['c'] + F['a']^2*F['b']^2 """ - if self._indices.cardinality() != infinity: - return self.prod(self.gens()) - return self.gen(self._indices.an_element()) + x = self.one() + I = self._indices + try: + x *= self.gen(I.an_element()) + except Exception: + pass + try: + g = iter(self._indices) + for c in range(1,4): + x *= self.gen(g.next()) ** c + except Exception: + pass + return x def __contains__(self, x): r""" From 3bf4cab87623bf09068c079aaa9363c72f7feeff Mon Sep 17 00:00:00 2001 From: Jean-Pierre Flori Date: Wed, 19 Feb 2014 06:59:47 -0800 Subject: [PATCH 022/546] Build Singular with FLINT support. --- build/deps | 2 +- build/pkgs/singular/SPKG.txt | 412 +----------------------- build/pkgs/singular/package-version.txt | 2 +- build/pkgs/singular/patches/flint.patch | 22 ++ build/pkgs/singular/spkg-install | 2 +- src/module_list.py | 33 +- 6 files changed, 45 insertions(+), 428 deletions(-) create mode 100644 build/pkgs/singular/patches/flint.patch diff --git a/build/deps b/build/deps index 3809e9555d2..e2074c01cc0 100644 --- a/build/deps +++ b/build/deps @@ -330,7 +330,7 @@ $(INST)/$(SAGETEX): $(INST)/$(PYTHON) \ $(INST)/$(SETUPTOOLS): $(INST)/$(PYTHON) +$(PIPE) "$(SAGE_SPKG) $(SETUPTOOLS) 2>&1" "tee -a $(SAGE_LOGS)/$(SETUPTOOLS).log" -$(INST)/$(SINGULAR): $(INST)/$(MPIR) $(INST)/$(NTL) \ +$(INST)/$(SINGULAR): $(INST)/$(MPIR) $(INST)/$(NTL) $(INST)/$(FLINT) \ $(INST)/$(READLINE) $(INST)/$(MPFR) +$(PIPE) "$(SAGE_SPKG) $(SINGULAR) 2>&1" "tee -a $(SAGE_LOGS)/$(SINGULAR).log" diff --git a/build/pkgs/singular/SPKG.txt b/build/pkgs/singular/SPKG.txt index af447a80d1d..74164a4eb58 100644 --- a/build/pkgs/singular/SPKG.txt +++ b/build/pkgs/singular/SPKG.txt @@ -23,7 +23,12 @@ http://www.singular.uni-kl.de/ == Dependencies == -GNU patch, GMP/MPIR, NTL, Termcap, Readline and MPFR +* GNU patch +* readline +* GMP/MPIR +* MPFR +* NTL +* FLINT == Special Update/Build Instructions == @@ -45,6 +50,7 @@ See spkg-src. * osx_link.patch: #14415: Fixes linker problems on OS X PPC. * sanitize_gmp_header_hack.patch: Fix and simplify generation of `factory/cf_gmp.h` (cf. #14737). + * flint.patch: avoid underlinking FLINT in libsingular. Other notes * The option '--without-dynamic-kernel' is used on *all* @@ -61,412 +67,8 @@ Other notes * Due to http://www.singular.uni-kl.de:8002/trac/ticket/438, we currently always build Singular with its debug code. Change --with-debug to $WITH_DEBUG in the configure call if this is fixed. - * We build Singular without FLINT support because Singular requires - FLINT 2.x. If a sufficiently recent version of FLINT is ever added - to Sage, we should make Singular use it - (configure --with-flint="$SAGE_LOCAL"). * If the environment variable SAGE_DEBUG is set to "yes", then omalloc will be replaced by xalloc. The resulting Singular executable and libsingular library will be slower than with omalloc, but allow for easier debugging of memory corruptions. -== ChangeLog == - -=== singular-3-1-6.p0 (Jeroen Demeyer, 26 August 2013) === - * Remove the following patches, which were fixed upstream: - - NTL_negate.patch - - install_table.patch - - pullrequest215.patch - - sage_trac_12089.patch - - singular_15435.patch - - singular_part_of_changeset_baadc0f7.patch - - singular_trac_439.patch - - singular_trac_440.patch - - singular_trac_441.patch - - singular_trac_443.patch - * Removed cygwin-makefile.patch, which is hopefully not needed anymore. - * Rebased some other patches. - * Add configure_comma.patch. - -=== singular-3-1-5.p9 (Paul Zimmermann, Jeroen Demeyer, 26 August 2013) === - * #13770: Add upstream pull request 215 for multivariate factorisation. - * spkg-install: split apply_patches step in 2 steps: choose_patches and - apply_patches; add a few small fixes. - * Remove install-sh which is no longer needed. - -=== singular-3-1-5.p8 (Leif Leonhardy, Volker Braun, 10 July 2013) === - * #14737: Fix and simplify generation of `factory/cf_gmp.h` - (by adding `patches/sanitize_gmp_header_hack.patch`). - * Add spkg-src and track all files - -=== singular-3-1-5.p7 (Jeroen Demeyer, 9 April 2013) === - * #14429: On sparc processors, build with --with-align=8. - -=== singular-3-1-5.p6 (Jeroen Demeyer, 5 April 2013) === - * #14415: Add osx_link.patch to fix linker problems on OS X 10.4 PPC - with GCC 4.7.2. - -=== singular-3-1-5.p5 (Leif Leonhardy, March 18th 2013) === - * #14295: Singular fails to build with GCC 4.7.x on Solaris. - On Solaris, with `__cplusplus >= 199711L`, `floor()` and `log10()` are - overloaded functions which get pulled into the global namespace, - such that calling them with an `int` gets ambiguous. - Patch `kernel/bigintmat.cc` (`patches/sage_trac_14295.patch`) - to cast parameters to `floor()` and `log10()` from `int` to - `double`, making the calls unambiguous. - -=== singular-3-1-5.p4 (Jean-Pierre Flori, 12 February 2013) === - * Trac #14033: prevent Singular from always linking to ncurses on - Cygwin. - -=== singular-3-1-5.p3 (Volker Braun, 28 December 2012) === - * Trac #13876: Rename SINGULAR_XALLOC to SAGE_DEBUG - * Build fixes for the Sage library with SAGE_DEBUG=yes - -=== singular-3-1-5.p2 (Simon King, 03 December 2012) === - * Trac #13731 - * Optionally replace omalloc with xalloc (a thin compatibility layer - on top of malloc) so that debugging of memory corruptions becomes - easier. - * Include two upstream fixes for memory corruptions. - -=== singular-3-1-5.p1 (Simon King, 29 August 2012) === - * Trac #13237: add patch install_table.patch - - singular_15435.patch - - singular_part_of_changeset_baadc0f7.patch - * If SINGULAR_XALLOC is "yes", then omalloc is replaced - by xalloc (a thin compatibility layer on top of malloc), - so that debugging of memory corruptions becomes easier. - -=== singular-3-1-5.p0 (Jeroen Demeyer, Alexander Dreyer, 10 August 2012) === - * Trac #13237: Upgrade to version 3-1-5. - * Removed patches which are now upstreamed: - - patches/Singular.Makefile.in.shared.patch - - patches/Singular.configure.patch - - patches/factory.GNUmakefile.in.patch - - patches/factory_configure - - patches/make_parallel.patch - - patches/os_x_ppc.patch - * In spkg-install, simplify apply_patches(), automatically apply all - patches in patches/*.patch, move conditional patches to - patches/conditional - * Rename some patches such that they all have the extension '.patch' - * Put the two patches for SAGE_DEBUG (Singular.Makefile.in.debug.patch - and kernel.Makefile.in.debug.patch) into one file - conditional/sage_debug.patch. - * Add several patches (see above): - - NTL_negate.patch - - singular_trac_439.patch - - singular_trac_440.patch - - singular_trac_441.patch - - singular_trac_443.patch - - sage_trac_12089.patch - - slibdir.patch - * When building Singular, don't first make install and then - make install-nolns. Instead, only do the latter. - * Don't create the LIB->lib symlink, which is no longer needed. - * In spkg-install, remove distclean() step; merge clean_headers() and - part of the old distclean() into remove_old_version(). - * Only unset LD on Darwin. - * Remove the unsetting of TMPDIR (bug fixed upstream). - * Do not override user-set CFLAGS and CXXFLAGS. - * Echo all error messages to stderr instead of stdout. - * Fix various ./configure options and remove some unsupported options. - * Always configure --with-debug, as --without-debug doesn't work, see - http://www.singular.uni-kl.de:8002/trac/ticket/438 - * Don't create sage_singular symlink, which wasn't used anyway. - * Replace the $SAGE_LOCAL/bin/Singular script by a symlink. - * Remove workaround for GCC-4.0.x on Darwin (obsolete by the GCC spkg). - * Fix formatting of spkg-install (consistent indentation, no TABs). - -=== singular-3-1-3-3.p6 (Leif Leonhardy, March 17th 2012) === - * #12680: Fix hardcoded 'g++' (and two typos) in factory/GNUmakefile.in. - -=== singular-3-1-3-3.p5 (Jeroen Demeyer, 22 February 2012) === - * Trac #12562: Disable -pipe on SunOS, as the Sun assembler chokes on - large files passed through a pipe. - * Trac #12311: Don't specify an explicit path for testcc.sh. - -=== singular-3-1-3-3.p4 (Jeroen Demeyer, 19 January 2012) === - * Trac #12304: Regenerate factory/configure using autoconf-2.68 to fix - a build problem on OS X 10.4. - * Change spkg-changes to compress omalloc/Misc/dlmalloc/malloc.ps - using gzip -9. Redownloaded upstream sources and applied - spkg-changes. - -=== singular-3-1-3-3.p3 (Julien Puydt, 7 January 2012) === - * Trac #12110: Add patch to use the -shared switch by default on unknown - platforms -- ARM is unknown and doesn't compile without that switch ; - upstream has better support for the arch so it's not interesting to make - a better fix. - -=== singular-3-1-3-3.p2 (Jeroen Demeyer, 9 December 2011) === - * Trac #12137: Add patch make_parallel.patch to fix parallel building - by patching various Makefile.in's - * Use $MAKE instead of make - -=== singular-3-1-3-3.p1 (Jeroen Demeyer, 10 October 2011) === - * Trac #10903: Add a patch os_x_ppc.patch to fix building on OS X PPC. - * Make all patches apply at -p1 level and apply some patches for Cygwin - on all systems for consistency. - * Remove unused files in patches/: Singular.configure, Singular.configure.in, - Singular.configure.in.patch - * Exit when `patch` fails. We do not print an error message, patch is - verbose enough by itself. - -=== singular-3-1-3-3.p0 (Volker Braun, 2 October 2011) === - * Trac #10903: Solaris fixes - -=== singular-3-1-3-3 (Martin Albrecht, Burcin Erocal, Volker Braun, 28 September 2011) === - * Trac #10903: new upstream release - * SAGE_DEBUG now builds Singular with lots of checks - -=== singular-3-1-1-4.p13 (Karl-Dieter Crisman, 12 August 2011) === - * Trac #11550: For Cygwin, we need to include time.h in - Singular/Minor.h - -=== singular-3-1-1-4.p12 (Leif Leonhardy, Jeroen Demeyer, 10 August 2011) === - * Trac #11645: Fix installation of gftables on Solaris - by applying a patch to Singular/Makefile.in - -=== singular-3-1-1-4.p11 (Jeroen Demeyer, 9 August 2011) === - * Trac #11663: Make all files world-readable - * Use `cp -p` in spkg-install to preserve permissions - * In spkg-install, remove chmod for the Singular library - * Made the following files inside src/ non-executable: -Singular/LIB/phindex.lib -Singular/LIB/findifs.lib -Singular/LIB/dmodvar.lib -Singular/LIB/dmod.lib -Singular/LIB/jacobson.lib -Singular/LIB/bfun.lib -Singular/wrapper.cc -Singular/janet.cc -Singular/janet.h -doc/README_download.plural.texi -doc/INSTALL_unix.plural.texi -doc/singular.dic -doc/plural.doc -doc/letterplace.doc -doc/sca.doc -doc/NEWS.plural.texi -doc/INSTALL_win.plural.texi -doc/HOWTO.ispell -doc/COPYING.plural.texi -factory/cf_gcd_smallp.cc - -=== singular-3-1-1-4.p9 (Jeroen Demeyer, 3 May 2011) === -* #11278: When compiling with gcc version 4.0.x, build with -O3 - optimization instead of -O2 to fix build errors on older Mac OS X - systems. - -=== singular-3-1-1-4.p8 (Jeroen Demeyer, Alexander Dreyer, David Kirkby, 19th April 2011) === -* #11084. Drop optimisation level to -O2 since that is what upstream - mostly uses. -* Remove the special case for building on ia64 systems (the - optimization level used to be set to -O0 on such systems). - Testing on iras showed that it now works with -O2. -* When building with SAGE64 set, *append* -m64 to the various flags - instead of overriding. -* Merged changes from Sage's Trac #11084 into the package from #9497 - -=== singular-3-1-1-4.p6 (John Palmieri, 26th March 2011) === -* change "$RM" to "rm" in spkg-install - -=== singular-3-1-1-4.p5 (Martin Albrecht, 26th March 2011) === -* enable parallel make - -=== singular-3-1-1-4.p4 (Volker Braun, 4th February 2011) === -* compile fix for OSX on 64-bit kernels. -* removed dist/ directory. - -=== singular-3-1-1-4.p3 (Francois Bissey, Alexander Dreyer, 21th September 2010) === -* Patch by Francois Bissey: #9946: Added another dependency - -=== singular-3-1-1-4.p2 (Alexander Dreyer, 17th September 2010) === -* #9733, comment comment 41 ff: Added another dependency - -=== singular-3-1-1-4.p1 (Alexander Dreyer, 10th September 2010) === -* #9733: Restore parallel build, hopefully found all dependencies. - -=== singular-3-1-1-4.p0 (Mitesh Patel, 10th August 2010) === - * #8059: For now, restore building the package serially, to avoid - parallel build problems. - -=== singular-3-1-1-4 (Martin Albrecht, 15th July 2010) === - * new upstream release - * updated SPKG.txt - -=== singular-3-1-1-3 (Martin Albrecht, 10th July 2010) === - * new upstream release - * updated SPKG.txt - * reverting name to reflect actual Singular version format - -=== singular-3.1.0.4.p8 (David Kirkby, 30th June 2010) === - * #9397 "Resolve corrupted patches to permit Singular to build on Solaris x86/x64" - A previous patch made to add a Singular target x86-SunOS has been overwritten - by a later patch. I resolved these two issues, so the patch contained both changes - A patch file, and a diff made from it were removed, so the patches directory has two - less files than before. - * #9397 Set CC="$CC -m64" and CXX="$CXX-m64" to force the -m64 flag to fully propogate - thoughtout the Singular build process - otherwise it fails to do so. Setting - CFALGS/CXXFLAGS is insufficient. - -=== singular-3.1.0.4.p7 (Mitesh Patel, 8th June 2010) === - * #9185: Set an empty MAKEFLAGS variable before "make". On OS X, at - least, this fixes building multiple spkgs in parallel (cf. #8306). - -=== singular-3.1.0.4.p6 (David Kirkby and Willem Jan Palenstijn, 6th June 2010) === - * All changes relate to ticket #9160 - * Added sections to SPKG.txt which the Sage Developers Guide should be - present, but were not. These include Description, License, - PKG Maintainers, Upstream Contact, Dependencies and - Special Update/Build Instructions. - * Added a couple of comments in the new "Special Update/Build - Instructions" section about Solaris and the use of - --without-dynamic-kernel option and CONFIG_SHELL. - * Changed the name of package to singular-$version.$patchversion, - as is common Sage practice, and not have a date of any - update. Since this should be patch level 6, I've called it that. - * Touched the file src/Singular/libparse.cc - (Willem Jan Palenstijn) - * Touched the file src/factory/configure - * Removed the restriction that the OS needs to be OS X before - a 64-bit build is attempted. (This still does not fully build - 64-bit on OpenSolaris, though it is closer to doing so.) - * Move code associated with SAGE64 outside of the code for debugging. - * Ensured $SAGE_LOCAL/include is the first CPPFLAG, so the - Sage include files are included before others. - * Removed code which attempted to disable the dynamic kernel on - OS X in 64-bit mode, since the dynamic kernel was already disabled - on all platforms except Linux. It was pointless doing it twice. - -=== singular-3-1-0-4-20100214 (William Stein, February 14, 2010) === - * patch for Cygwin (trac 7338) - -=== singular-3-1-0-4-20100120 (Martin Albrecht, January 20th, 2010) === - * installing attrib.h from Singular - -=== singular-3-1-0-4-20090818.p2 (Martin Albrecht, November 18th, 2009) === - * installing lists.h from Singular (#7194) - -=== singular-3-1-0-4-20090818.p1 () === - * we forgot to update this :( - -=== singular-3-1-0-4-20090818.p0 (Georg S. Weber, September 24th, 2009) === - * added three missing /patches/*.diff files - * change -O2 to -O0 on ia64 (see trac #6360 and #6240), - original change: singular-3-1-0-2-20090618 (Craig Citro, June 18th, 2009) - * Make a copy of install-sh and put in the same directory as spkg-install, - SPKG.txt etc. For some reason, I believe one of the makefiles is looking - in the wrong directory for install-sh. There are about 5 identical copies - of this in the source. I'm not sure why there needs to be so many, and why - even with 5 or so copile, one can't be found. - Original change: singular-3-1-0-2-20090620.p0 (David Kirkby, July 19th, 2009) - * (don't include singular-3-1-0-2-20090620 patch, since no longer necessary: - Andrzej Giniewicz' fix GCC 4.4 compilation problem) - -=== singular-3-1-0-4-20090818 (Martin Albrecht, August 18th, 2009) === - * more includes, GCC 4.4 fixes - -=== singular-3-1-0-4-20090726 (Martin Albrecht, July 26th, 2009) === - * more includes in libsingular.h - -=== singular-3-1-0-4-20090611 (Martin Albrecht, June 11th, 2009) === - * new upstream release which makes some of our fixes redundant - -=== singular-3-1-0-2-20090512 (Martin Albrecht, May 12th, 2009) === - * new upstream release - -=== singular-3-0-4-4-20090511 (Martin Albrecht, May 11th, 2009) === - * fixed #5862 - * update SPKG.txt - -=== singular-3-0-4-4-20080711.p4 (Michael Abshoff, January 23rd, 2009) === - * Integrate two patches by Georg Weber (#4181 and #5344) - * update SPKG.txt - * delete MacOSX junk added in singular-3-0-4-4-20080711.p3 - -=== singular-3-0-4-4-20080711.p3 (Michael Abshoff, January 20th, 2009) === - * use "--with-malloc=system" for 64 bit OSX - -=== singular-3-0-4-4-20080711.p2 (Michael Abshoff, November 30th, 2008) === - * Fix header permission problem (#4668) - * delete old singular headers in $SAGE_LOCAL/include/singular - -=== singular-3-0-4-4-20080711.p1 (Michael Abshoff, September 1st, 2008) === - * Fix three Solaris issues: libsingular build options, tail POSIX issue and install missing on Solaris - -=== singular-3-0-4-4-20080711.p0 (Michael Abshoff, August 19th, 2008) === - * add 64 bit OSX support - * Fix mv-factoprization bug reported by Tom Boothby - -=== singular-3-0-4-4-20080711 (Martin Albrecht, July 11th, 2008) === - * new upstream release - -=== singular-3-0-4-2-20080405.p2 (Michael Abshoff, May 10th, 2008) === - * Default to "-O2" on Linux/Itanium (fixes #2983) - * Do not require flex to build Singular (fixes #3158) - -=== singular-3-0-4-2-20080405.p1 (Michael Abshoff, April 19th, 2008) === - * import Tim Abbott's Debian build fix (#2966) - -=== singular-3-0-4-2-20080405.p0 (Martin Albrecht) === - * Update Singular to the latest snapshot - -=== singular-3-0-4-1-20071209.p3 (Michael Abshoff) === - * fix memleak in kernel/longrat.cc - -* 20080105 William Stein -- Deleted LIB/surfex and modified LIB/all to not have surfex in it. -We should *not* ship and install precompiled java without thinking it through carefully and -understanding and testing it. - -* 20070823 Juan M. Bello Rivas - + dropped dependencies on flex and bison - -* 20070802 Martin Albrecht - + new upstream release (3-0-3) - + adapted new spkg structure (all changes in patches subdirectory) - -* 20070506 Martin Albrecht - + build script improvements - + changes to libsingular.h - -* 20070326 Martin Albrecht - + rewrote spkg-install - + added first alpha of libsingular.so - + removed stand-alone libfactory - -* 20070105 William Stein - + included Singular/Singular.rc.in which was missing for some - reason and is needed by the cygwin build. - -* 20060825 Martin Albrecht - + removed hannes-sprueche.txt (not funny anyway) - + removed ntl subdirectory (we ship NTL already) - + removed modules subdirectory (not needed) - + removed Tst subdirectory (not needed) - + wrote (very simple) spkg-install from scratch - -* 20060831 William Stein and Justin Walker -- fine tuning - for OS X. - -* 20060831 William Stein -- put a lot of the workarounds from the previous - Singular spkg-install script in here, since they are in fact needed - in order for Singular to work after you type, e.g., make clean, or if - you move the install directory. - -* Martin Albrecht -- add fix to libcf: -Yes, It's a (new?) bug in libcf I've fixed just yesterday but got no word from -the Singular people yet. They free a char ptr they are not supposed to free -if I understand the ostrstring docs correctly. I've attached the -one-line-fixed int_poly.cc which belongs in the factory subdirectory of -Singular. - - * Martin Albrecht -- 2006-09-17: -This looks to me as if I didn't remove all references to boost: -/usr/include/boost/dynamic_bitset/dynamic_bitset.hpp:1098: error: exception -I've attached an updated configure.in and configure (created with autoconf -2.60) with Boost checks disabled which should fix the problem. These belong -in the Singular subdirectory: - singular-3-0-2-2006-09-09/Singular diff --git a/build/pkgs/singular/package-version.txt b/build/pkgs/singular/package-version.txt index a20f5edf470..057d7ea1719 100644 --- a/build/pkgs/singular/package-version.txt +++ b/build/pkgs/singular/package-version.txt @@ -1 +1 @@ -3.1.6.p0 +3.1.6.p1 diff --git a/build/pkgs/singular/patches/flint.patch b/build/pkgs/singular/patches/flint.patch new file mode 100644 index 00000000000..db79d93157f --- /dev/null +++ b/build/pkgs/singular/patches/flint.patch @@ -0,0 +1,22 @@ +diff -druN latest.orig/Singular/configure latest/Singular/configure +--- latest.orig/Singular/configure 2012-12-19 13:01:16.000000000 -0800 ++++ latest/Singular/configure 2014-02-19 05:46:21.061190243 -0800 +@@ -8989,6 +8989,7 @@ + if test "x$flint_found" = "xyes"; then + includedir= "${FLINT_HOME}/include ${includedir}" + LDFLAGS="${LDFLAGS} ${FLINT_LIBS}" ++ SLDFLAGS="${SLDFLAGS} ${FLINT_LIBS}" + NEED_LIBS="-lflint -lmpfr ${NEED_LIBS}" + fi + +diff -druN latest.orig/Singular/configure.in latest/Singular/configure.in +--- latest.orig/Singular/configure.in 2012-12-19 13:01:16.000000000 -0800 ++++ latest/Singular/configure.in 2014-02-19 05:45:48.911187028 -0800 +@@ -1406,6 +1406,7 @@ + if test "x$flint_found" = "xyes"; then + includedir= "${FLINT_HOME}/include ${includedir}" + LDFLAGS="${LDFLAGS} ${FLINT_LIBS}" ++ SLDFLAGS="${SLDFLAGS} ${FLINT_LIBS}" + NEED_LIBS="-lflint -lmpfr ${NEED_LIBS}" + fi + diff --git a/build/pkgs/singular/spkg-install b/build/pkgs/singular/spkg-install index 3caa3829f88..69f613e69ce 100755 --- a/build/pkgs/singular/spkg-install +++ b/build/pkgs/singular/spkg-install @@ -143,10 +143,10 @@ config() --with-apint=gmp \ --with-malloc=system \ --with-NTL \ + --with-flint="$SAGE_LOCAL" \ --without-MP \ --without-lex \ --without-Boost \ - --without-flint \ --enable-Singular \ --enable-factory \ --enable-libfac \ diff --git a/src/module_list.py b/src/module_list.py index 26520f83ae9..64ece66e7ae 100755 --- a/src/module_list.py +++ b/src/module_list.py @@ -42,7 +42,8 @@ numpy_depends = [SAGE_LOCAL + '/lib/python/site-packages/numpy/core/include/numpy/_numpyconfig.h'] flint_depends = [SAGE_INC + '/flint/flint.h'] -singular_depends = [SAGE_INC + '/libsingular.h', SAGE_INC + '/givaro/givconfig.h'] +singular_depends = [SAGE_INC + '/libsingular.h'] +givaro_depends = [SAGE_INC + '/givaro/givconfig.h'] ######################################################### ### M4RI flags @@ -57,8 +58,7 @@ m4ri_extra_compile_args.extend( [flag.strip() for flag in m4ri_sse2_cflags.split(" ") if flag.strip()] ) break -singular_libs = ['m', 'readline', 'singular', 'givaro', 'ntl', 'gmpxx', 'gmp'] - +singular_libs = ['singular', 'flint', 'ntl', 'gmpxx', 'gmp', 'readline', 'm'] ######################################################### ### Givaro flags @@ -695,7 +695,7 @@ def uname_specific(name, value, alternative): 'stdc++', 'givaro', 'mpfr', 'gmp', 'gmpxx', BLAS, BLAS2], language = 'c++', extra_compile_args = givaro_extra_compile_args, - depends = [SAGE_INC + '/givaro/givconfig.h']), + depends = givaro_depends), Extension('sage.libs.lcalc.lcalc_Lfunction', sources = ['sage/libs/lcalc/lcalc_Lfunction.pyx'], @@ -755,10 +755,10 @@ def uname_specific(name, value, alternative): Extension('sage.libs.singular.singular', sources = ['sage/libs/singular/singular.pyx'], - libraries = singular_libs, + libraries = ['givaro'] + singular_libs, language="c++", include_dirs = [SAGE_INC + '/singular', SAGE_INC + '/factory'], - depends = singular_depends, + depends = singular_depends + givaro_depends, extra_compile_args = givaro_extra_compile_args), Extension('sage.libs.singular.polynomial', @@ -766,24 +766,21 @@ def uname_specific(name, value, alternative): libraries = singular_libs, language="c++", include_dirs = [SAGE_INC + '/singular', SAGE_INC + '/factory'], - depends = singular_depends, - extra_compile_args = givaro_extra_compile_args), + depends = singular_depends), Extension('sage.libs.singular.ring', sources = ['sage/libs/singular/ring.pyx'], libraries = singular_libs, language="c++", include_dirs = [SAGE_INC + '/singular', SAGE_INC + '/factory'], - depends = singular_depends, - extra_compile_args = givaro_extra_compile_args), + depends = singular_depends), Extension('sage.libs.singular.groebner_strategy', sources = ['sage/libs/singular/groebner_strategy.pyx'], libraries = singular_libs, language="c++", include_dirs = [SAGE_INC + '/singular', SAGE_INC + '/factory'], - depends = singular_depends, - extra_compile_args = givaro_extra_compile_args), + depends = singular_depends), Extension('sage.libs.singular.function', sources = ['sage/libs/singular/function.pyx'], @@ -798,8 +795,7 @@ def uname_specific(name, value, alternative): libraries = singular_libs, language="c++", include_dirs = [SAGE_INC + '/singular', SAGE_INC + '/factory'], - depends = singular_depends, - extra_compile_args = givaro_extra_compile_args), + depends = singular_depends), Extension('sage.libs.symmetrica.symmetrica', sources = ["sage/libs/symmetrica/%s"%s for s in ["symmetrica.pyx"]], @@ -1101,8 +1097,7 @@ def uname_specific(name, value, alternative): libraries = singular_libs, language="c++", include_dirs = [SAGE_INC + '/singular', SAGE_INC + '/factory'], - depends = singular_depends, - extra_compile_args = givaro_extra_compile_args), + depends = singular_depends), Extension('sage.matrix.matrix_rational_dense', sources = ['sage/matrix/matrix_rational_dense.pyx'], @@ -1778,8 +1773,7 @@ def uname_specific(name, value, alternative): libraries = singular_libs, language="c++", include_dirs = [SAGE_INC + '/singular', SAGE_INC + '/factory'], - depends = singular_depends, - extra_compile_args = givaro_extra_compile_args), + depends = singular_depends), Extension('sage.rings.polynomial.plural', sources = ['sage/rings/polynomial/plural.pyx'], @@ -1794,8 +1788,7 @@ def uname_specific(name, value, alternative): libraries = singular_libs, language="c++", include_dirs = [SAGE_INC + '/singular', SAGE_INC + '/factory'], - depends = singular_depends, - extra_compile_args = givaro_extra_compile_args), + depends = singular_depends), Extension('sage.rings.polynomial.multi_polynomial_ring_generic', sources = ['sage/rings/polynomial/multi_polynomial_ring_generic.pyx']), From e8c1403ad4a967a52e6fcbce08de9bb37c367ccd Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sun, 16 Mar 2014 15:18:23 +0100 Subject: [PATCH 023/546] Trac #13781: make all doctests use NTL implementation --- .../polynomial/polynomial_modn_dense_ntl.pyx | 123 +++++++++--------- 1 file changed, 62 insertions(+), 61 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx index 649b96fcda8..c3186d28ad4 100644 --- a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx @@ -65,7 +65,7 @@ cdef class Polynomial_dense_mod_n(Polynomial): EXAMPLES:: - sage: R. = PolynomialRing(Integers(16)) + sage: R. = PolynomialRing(Integers(16), implementation='ntl') sage: f = x^3 - x + 17 sage: f^2 x^6 + 14*x^4 + 2*x^3 + x^2 + 14*x + 1 @@ -73,14 +73,14 @@ cdef class Polynomial_dense_mod_n(Polynomial): sage: loads(f.dumps()) == f True - sage: R. = Integers(100)[] + sage: R. = PolynomialRing(Integers(100), implementation='ntl') sage: p = 3*x sage: q = 7*x sage: p+q 10*x - sage: R. = Integers(8)[] + sage: R. = PolynomialRing(Integers(8), implementation='ntl') sage: parent(p) - Univariate Polynomial Ring in x over Ring of integers modulo 100 + Univariate Polynomial Ring in x over Ring of integers modulo 100 (using NTL) sage: p + q 10*x sage: R({10:-1}) @@ -156,7 +156,7 @@ cdef class Polynomial_dense_mod_n(Polynomial): """ EXAMPLES:: - sage: t = PolynomialRing(IntegerModRing(17),"t").gen() + sage: t = PolynomialRing(IntegerModRing(17),"t", implementation='ntl').gen() sage: f = t^3 + 3*t - 17 sage: pari(f) Mod(1, 17)*t^3 + Mod(3, 17)*t @@ -185,7 +185,7 @@ cdef class Polynomial_dense_mod_n(Polynomial): EXAMPLES:: - sage: R. = Integers(100)[] + sage: R. = PolynomialRing(Integers(100), implementation='ntl') sage: from sage.rings.polynomial.polynomial_modn_dense_ntl import Polynomial_dense_mod_n sage: f = Polynomial_dense_mod_n(R,[5,10,13,1,4]); f 4*x^4 + x^3 + 13*x^2 + 10*x + 5 @@ -228,7 +228,7 @@ cdef class Polynomial_dense_mod_n(Polynomial): """ EXAMPLES:: - sage: x = PolynomialRing(Integers(100), 'x').0 + sage: x = PolynomialRing(Integers(100), 'x', implementation='ntl').0 sage: (x - 2)*(x^2 - 8*x + 16) x^3 + 90*x^2 + 32*x + 68 """ @@ -263,7 +263,7 @@ cdef class Polynomial_dense_mod_n(Polynomial): EXAMPLES:: - sage: R. = PolynomialRing(Integers(12345678901234567890)) + sage: R. = PolynomialRing(Integers(12345678901234567890), implementation='ntl') sage: p = x^2 + 2*x + 4 sage: p.shift(0) x^2 + 2*x + 4 @@ -313,7 +313,7 @@ cdef class Polynomial_dense_mod_n(Polynomial): EXAMPLES:: - sage: _. = Integers(100)[] + sage: _. = PolynomialRing(Integers(100), implementation='ntl') sage: f = x^3 + 3*x - 17 sage: f.list() [83, 3, 0, 1] @@ -335,7 +335,7 @@ cdef class Polynomial_dense_mod_n(Polynomial): EXAMPLES:: - sage: R. = PolynomialRing(Integers(100)) + sage: R. = PolynomialRing(Integers(100), implementation='ntl') sage: from sage.rings.polynomial.polynomial_modn_dense_ntl import Polynomial_dense_mod_n as poly_modn_dense sage: poly_modn_dense(R, ([1,-2,3])) 3*x^2 + 98*x + 1 @@ -385,7 +385,7 @@ cdef class Polynomial_dense_mod_n(Polynomial): sage: N = 10001 sage: K = Zmod(10001) - sage: P. = PolynomialRing(K) + sage: P. = PolynomialRing(K, implementation='ntl') sage: f = x^3 + 10*x^2 + 5000*x - 222 sage: f.small_roots() [4] @@ -422,7 +422,7 @@ def small_roots(self, X=None, beta=1.0, epsilon=None, **kwds): sage: N = 10001 sage: K = Zmod(10001) - sage: P. = PolynomialRing(K) + sage: P. = PolynomialRing(K, implementation='ntl') sage: f = x^3 + 10*x^2 + 5000*x - 222 This polynomial has no roots without modular reduction (i.e. over `\ZZ`):: @@ -482,7 +482,7 @@ def small_roots(self, X=None, beta=1.0, epsilon=None, **kwds): To recover `K` we consider the following polynomial modulo `N`:: - sage: P. = PolynomialRing(ZmodN) + sage: P. = PolynomialRing(ZmodN, implementation='ntl') sage: f = (2^Nbits - 2^Kbits + x)^e - C and recover its small roots:: @@ -509,7 +509,7 @@ def small_roots(self, X=None, beta=1.0, epsilon=None, **kwds): And try to recover `q` from it:: - sage: F. = PolynomialRing(Zmod(N)) + sage: F. = PolynomialRing(Zmod(N), implementation='ntl') sage: f = x - qbar We know that the error is `\le 2^{\text{hidden}}-1` and that the modulus @@ -595,7 +595,8 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): r""" EXAMPLES:: - sage: R = Integers(5**21) ; S. = R[] + sage: R = Integers(5**21) + sage: S. = PolynomialRing(R, implementation='ntl') sage: S(1/4) 357627868652344 """ @@ -641,7 +642,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = Integers(100)[] + sage: R. = PolynomialRing(Integers(100), implementation='ntl') sage: from sage.rings.polynomial.polynomial_modn_dense_ntl import Polynomial_dense_mod_n as poly_modn_dense sage: f = poly_modn_dense(R,[5,0,0,1]) sage: f.int_list() @@ -659,7 +660,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = Integers(100)[] + sage: R. = PolynomialRing(Integers(100), implementation='ntl') sage: from sage.rings.polynomial.polynomial_modn_dense_ntl import Polynomial_dense_modn_ntl_zz sage: f = Polynomial_dense_modn_ntl_zz(R,[2, 1])^7 sage: f[3] @@ -695,7 +696,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = Integers(100)[] + sage: R. = PolynomialRing(Integers(100), implementation='ntl') sage: (x+5) + (x^2 - 6) x^2 + x + 99 """ @@ -712,7 +713,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = Integers(100)[] + sage: R. = PolynomialRing(Integers(100), implementation='ntl') sage: (x+5) - (x^2 - 6) 99*x^2 + x + 11 """ @@ -729,7 +730,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = Integers(100)[] + sage: R. = PolynomialRing(Integers(100), implementation='ntl') sage: (x+5) * (x^2 - 1) x^3 + 5*x^2 + 99*x + 95 """ @@ -786,7 +787,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = Integers(100)[] + sage: R. = PolynomialRing(Integers(100), implementation='ntl') sage: (x+5) * 3 3*x + 15 """ @@ -802,7 +803,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = Integers(100)[] + sage: R. = PolynomialRing(Integers(100), implementation='ntl') sage: 3 * (x+5) 3*x + 15 """ @@ -818,11 +819,11 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = Integers(100)[] + sage: R. = PolynomialRing(Integers(100), implementation='ntl') sage: (x-1)^5 x^5 + 95*x^4 + 10*x^3 + 90*x^2 + 5*x + 99 - sage: R. = Integers(101)[] + sage: R. = PolynomialRing(Integers(101), implementation='ntl') sage: (x-1)^(-5) 1/(x^5 + 96*x^4 + 10*x^3 + 91*x^2 + 5*x + 100) @@ -887,7 +888,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = Integers(125)[] + sage: R. = PolynomialRing(Integers(125), implementation='ntl') sage: f = x^5+1; g = (x+1)^2 sage: q, r = f.quo_rem(g) sage: q @@ -914,7 +915,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = Integers(25)[] + sage: R. = PolynomialRing(Integers(25), implementation='ntl') sage: f = x^7 + 1; g = x^2 - 1 sage: q = f // g; q x^5 + x^3 + x @@ -937,7 +938,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ EXAMPLES:: - sage: R. = Integers(81)[] + sage: R. = PolynomialRing(Integers(81), implementation='ntl') sage: f = x^7 + x + 1; g = x^3 sage: r = f % g; r x + 1 @@ -963,7 +964,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = Integers(77)[] + sage: R. = PolynomialRing(Integers(77), implementation='ntl') sage: f = x^7 + x + 1 sage: f.shift(1) x^8 + x^2 + x @@ -987,7 +988,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ TEST:: - sage: R. = Integers(77)[] + sage: R. = PolynomialRing(Integers(77), implementation='ntl') sage: f = x^5 + 2*x + 1 sage: f << 3 x^8 + 2*x^4 + x^3 @@ -1000,7 +1001,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ TEST:: - sage: R. = Integers(77)[] + sage: R. = PolynomialRing(Integers(77), implementation='ntl') sage: f = x^5 + 2*x + 1 sage: f >> 3 x^2 @@ -1021,7 +1022,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = Integers(77)[] + sage: R. = PolynomialRing(Integers(77), implementation='ntl') sage: f = x^4 - x - 1 sage: f._derivative() 4*x^3 + 76 @@ -1054,7 +1055,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = Integers(77)[] + sage: R. = PolynomialRing(Integers(77), implementation='ntl') sage: f = x^4 - x - 1 sage: f.reverse() 76*x^4 + 76*x^3 + 1 @@ -1073,7 +1074,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = Integers(77)[] + sage: R. = PolynomialRing(Integers(77), implementation='ntl') sage: f = x^4 - x - 1 sage: not f False @@ -1089,7 +1090,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = Integers(10)[] + sage: R. = PolynomialRing(Integers(10), implementation='ntl') sage: x.valuation() 1 sage: f = x-3; f.valuation() @@ -1109,7 +1110,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ EXAMPLES:: - sage: R. = Integers(77)[] + sage: R. = PolynomialRing(Integers(77), implementation='ntl') sage: f = x^4 - x - 1 sage: f.degree() 4 @@ -1125,7 +1126,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = Integers(77)[] + sage: R. = PolynomialRing(Integers(77), implementation='ntl') sage: f = sum(x^n for n in range(10)); f x^9 + x^8 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1 sage: f.truncate(6) @@ -1143,7 +1144,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = Integers(100)[] + sage: R. = PolynomialRing(Integers(100), implementation='ntl') sage: f = x^3+7 sage: f(5) 32 @@ -1153,7 +1154,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): 32 sage: f(x) x^3 + 7 - sage: S. = Integers(5)[] + sage: S. = PolynomialRing(Integers(5), implementation='ntl') sage: f(y) y^3 + 2 """ @@ -1219,7 +1220,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = Integers(10^30)[] + sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') sage: from sage.rings.polynomial.polynomial_modn_dense_ntl import Polynomial_dense_modn_ntl_ZZ sage: f = Polynomial_dense_modn_ntl_ZZ(R,[2,1])^7 sage: f[3] @@ -1267,7 +1268,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = Integers(10^30)[] + sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') sage: (x+5) + (x^2 - 6) x^2 + x + 999999999999999999999999999999 """ @@ -1284,7 +1285,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = Integers(10^30)[] + sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') sage: (x+5) - (x^2 - 6) 999999999999999999999999999999*x^2 + x + 11 """ @@ -1301,7 +1302,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = Integers(10^30)[] + sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') sage: (x+5) * (x^2 - 1) x^3 + 5*x^2 + 999999999999999999999999999999*x + 999999999999999999999999999995 """ @@ -1358,7 +1359,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = Integers(10^30)[] + sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') sage: (x+5) * 3 3*x + 15 """ @@ -1375,7 +1376,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = Integers(10^30)[] + sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') sage: 3 * (x+5) 3*x + 15 """ @@ -1385,7 +1386,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = Integers(10^30)[] + sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') sage: (x+1)^5 x^5 + 5*x^4 + 10*x^3 + 10*x^2 + 5*x + 1 @@ -1448,7 +1449,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = Integers(10^30)[] + sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') sage: f = x^5+1; g = (x+1)^2 sage: q, r = f.quo_rem(g) sage: q @@ -1475,7 +1476,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = Integers(10^30)[] + sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') sage: f = x^7 + 1; g = x^2 - 1 sage: q = f // g; q x^5 + x^3 + x @@ -1498,7 +1499,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ EXAMPLES:: - sage: R. = Integers(9^30)[] + sage: R. = PolynomialRing(Integers(9^30), implementation='ntl') sage: f = x^7 + x + 1; g = x^3 - 1 sage: r = f % g; r 2*x + 1 @@ -1524,7 +1525,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = Integers(12^30)[] + sage: R. = PolynomialRing(Integers(12^30), implementation='ntl') sage: f = x^7 + x + 1 sage: f.shift(1) x^8 + x^2 + x @@ -1548,7 +1549,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ TEST:: - sage: R. = Integers(14^30)[] + sage: R. = PolynomialRing(Integers(14^30), implementation='ntl') sage: f = x^5 + 2*x + 1 sage: f << 3 x^8 + 2*x^4 + x^3 @@ -1561,7 +1562,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ TEST:: - sage: R. = Integers(15^30)[] + sage: R. = PolynomialRing(Integers(15^30), implementation='ntl') sage: f = x^5 + 2*x + 1 sage: f >> 3 x^2 @@ -1583,7 +1584,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = Integers(12^29)[] + sage: R. = PolynomialRing(Integers(12^29), implementation='ntl') sage: f = x^4 + x + 5 sage: f._derivative() 4*x^3 + 1 @@ -1618,7 +1619,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = Integers(12^29)[] + sage: R. = PolynomialRing(Integers(12^29), implementation='ntl') sage: f = x^4 + 2*x + 5 sage: f.reverse() 5*x^4 + 2*x^3 + 1 @@ -1640,7 +1641,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = Integers(10^50)[] + sage: R. = PolynomialRing(Integers(10^50), implementation='ntl') sage: x.valuation() 1 sage: f = x-3; f.valuation() @@ -1662,7 +1663,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = Integers(12^29)[] + sage: R. = PolynomialRing(Integers(12^29), implementation='ntl') sage: f = x^4 + 1 sage: not f False @@ -1675,7 +1676,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ EXAMPLES:: - sage: R. = Integers(14^34)[] + sage: R. = PolynomialRing(Integers(14^34), implementation='ntl') sage: f = x^4 - x - 1 sage: f.degree() 4 @@ -1691,7 +1692,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = Integers(15^30)[] + sage: R. = PolynomialRing(Integers(15^30), implementation='ntl') sage: f = sum(x^n for n in range(10)); f x^9 + x^8 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1 sage: f.truncate(6) @@ -1709,7 +1710,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = Integers(10^30)[] + sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') sage: f = x^3+7 sage: f(5) 132 @@ -1719,7 +1720,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): 132 sage: f(x) x^3 + 7 - sage: S. = Integers(5)[] + sage: S. = PolynomialRing(Integers(5), implementation='ntl') sage: f(y) y^3 + 2 """ @@ -1815,7 +1816,7 @@ cdef class Polynomial_dense_mod_p(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = GF(19)['x'] + sage: R. = PolynomialRing(GF(19),implementation='NTL') sage: f = x^3 + x + 1; g = x^3 - x - 1 sage: r = f.resultant(g); r 11 @@ -1829,7 +1830,7 @@ cdef class Polynomial_dense_mod_p(Polynomial_dense_mod_n): """ EXAMPLES:: - sage: _. = PolynomialRing(GF(19)) + sage: _. = PolynomialRing(GF(19),implementation='NTL') sage: f = x^3 + 3*x - 17 sage: f.discriminant() 12 From 228fd67db789406492dce289ee437bb5a04f1333 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Tue, 18 Mar 2014 16:49:10 +0100 Subject: [PATCH 024/546] Trac #9321: add warnings to sum() and symbolic_sum() documentation --- src/sage/misc/functional.py | 7 +++++++ src/sage/symbolic/expression.pyx | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/sage/misc/functional.py b/src/sage/misc/functional.py index b049dbfe635..8313bbddbda 100644 --- a/src/sage/misc/functional.py +++ b/src/sage/misc/functional.py @@ -542,6 +542,13 @@ def symbolic_sum(expression, *args, **kwds): Returns the symbolic sum `\sum_{v = a}^b expression` with respect to the variable `v` with endpoints `a` and `b`. + .. WARNING:: + + This function only works with symbolic expressions. To sum any + other objects like list elements or function return values, + please use python summation, see + http://docs.python.org/2.7/library/functions.html#sum + INPUT: - ``expression`` - a symbolic expression diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 9ecc7a84a5c..2bf924a400f 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -9752,6 +9752,13 @@ cdef class Expression(CommutativeRingElement): with respect to the variable `v` with endpoints `a` and `b`. + .. WARNING:: + + This function only works with symbolic expressions. To sum any + other objects like list elements or function return values, + please use python summation, see + http://docs.python.org/2.7/library/functions.html#sum + INPUT: From 09ba9b9e6c3868d4f8221afbd8940012967f79cc Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Wed, 19 Mar 2014 09:31:46 +0100 Subject: [PATCH 025/546] examples when/not to use Sage sum(); same also in calculus.py --- src/sage/calculus/calculus.py | 33 ++++++++++++++++++++++++++++++++ src/sage/misc/functional.py | 26 +++++++++++++++++++++++++ src/sage/symbolic/expression.pyx | 25 ++++++++++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index bb6660aa127..7715d1218e1 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -435,6 +435,39 @@ def symbolic_sum(expression, v, a, b, algorithm='maxima'): Returns the symbolic sum `\sum_{v = a}^b expression` with respect to the variable `v` with endpoints `a` and `b`. + .. WARNING:: + + This function only works with symbolic expressions. To sum any + other objects like list elements or function return values, + please use python summation, see + http://docs.python.org/2.7/library/functions.html#sum + + In particular, this does not work:: + + sage: var('n') + sage: list=[1,2,3,4,5] + sage: sum(list[n],n,0,3) + TypeError ... + ... + TypeError: unable to convert x (=n) to an integer + + Use python sum() instead:: + + sage: sum(list[n] for n in range(4)) + 10 + + Also, only a limited number of functions are recognized in symbolic sums:: + + sage: sum(valuation(n,2),n,1,5) + AttributeError ... + ... + AttributeError: 'sage.symbolic.expression.Expression' object has no attribute 'valuation' + + Again, use python sum():: + + sage: sum(valuation(n+1,2) for n in range(5)) + 3 + INPUT: - ``expression`` - a symbolic expression diff --git a/src/sage/misc/functional.py b/src/sage/misc/functional.py index 8313bbddbda..e0c008dc8a6 100644 --- a/src/sage/misc/functional.py +++ b/src/sage/misc/functional.py @@ -548,6 +548,32 @@ def symbolic_sum(expression, *args, **kwds): other objects like list elements or function return values, please use python summation, see http://docs.python.org/2.7/library/functions.html#sum + + In particular, this does not work:: + + sage: var('n') + sage: list=[1,2,3,4,5] + sage: sum(list[n],n,0,3) + TypeError ... + ... + TypeError: unable to convert x (=n) to an integer + + Use python sum() instead:: + + sage: sum(list[n] for n in range(4)) + 10 + + Also, only a limited number of functions are recognized in symbolic sums:: + + sage: sum(valuation(n,2),n,1,5) + AttributeError ... + ... + AttributeError: 'sage.symbolic.expression.Expression' object has no attribute 'valuation' + + Again, use python sum():: + + sage: sum(valuation(n+1,2) for n in range(5)) + 3 INPUT: diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 2bf924a400f..b7adabec47e 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -9759,6 +9759,31 @@ cdef class Expression(CommutativeRingElement): please use python summation, see http://docs.python.org/2.7/library/functions.html#sum + In particular, this does not work:: + + sage: var('n') + sage: list=[1,2,3,4,5] + sage: sum(list[n],n,0,3) + TypeError ... + ... + TypeError: unable to convert x (=n) to an integer + + Use python sum() instead:: + + sage: sum(list[n] for n in range(4)) + 10 + + Also, only a limited number of functions are recognized in symbolic sums:: + + sage: sum(valuation(n,2),n,1,5) + AttributeError ... + ... + AttributeError: 'sage.symbolic.expression.Expression' object has no attribute 'valuation' + + Again, use python sum():: + + sage: sum(valuation(n+1,2) for n in range(5)) + 3 INPUT: From 45fbd5044269755455b80d095f6e52e8dd7b9fe2 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Wed, 19 Mar 2014 09:43:59 +0100 Subject: [PATCH 026/546] fix doctests --- src/sage/calculus/calculus.py | 6 ++-- src/sage/misc/functional.py | 56 ++++++++++++++++---------------- src/sage/symbolic/expression.pyx | 8 ++--- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index 7715d1218e1..faa57e34c92 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -444,10 +444,10 @@ def symbolic_sum(expression, v, a, b, algorithm='maxima'): In particular, this does not work:: - sage: var('n') + sage: n = var('n') sage: list=[1,2,3,4,5] sage: sum(list[n],n,0,3) - TypeError ... + Traceback (most recent call last): ... TypeError: unable to convert x (=n) to an integer @@ -459,7 +459,7 @@ def symbolic_sum(expression, v, a, b, algorithm='maxima'): Also, only a limited number of functions are recognized in symbolic sums:: sage: sum(valuation(n,2),n,1,5) - AttributeError ... + Traceback (most recent call last): ... AttributeError: 'sage.symbolic.expression.Expression' object has no attribute 'valuation' diff --git a/src/sage/misc/functional.py b/src/sage/misc/functional.py index e0c008dc8a6..df64faa7ae9 100644 --- a/src/sage/misc/functional.py +++ b/src/sage/misc/functional.py @@ -542,38 +542,38 @@ def symbolic_sum(expression, *args, **kwds): Returns the symbolic sum `\sum_{v = a}^b expression` with respect to the variable `v` with endpoints `a` and `b`. - .. WARNING:: - - This function only works with symbolic expressions. To sum any - other objects like list elements or function return values, - please use python summation, see - http://docs.python.org/2.7/library/functions.html#sum - - In particular, this does not work:: + .. WARNING:: - sage: var('n') - sage: list=[1,2,3,4,5] - sage: sum(list[n],n,0,3) - TypeError ... - ... - TypeError: unable to convert x (=n) to an integer + This function only works with symbolic expressions. To sum any + other objects like list elements or function return values, + please use python summation, see + http://docs.python.org/2.7/library/functions.html#sum + + In particular, this does not work:: - Use python sum() instead:: - - sage: sum(list[n] for n in range(4)) - 10 + sage: n = var('n') + sage: list=[1,2,3,4,5] + sage: sum(list[n],n,0,3) + Traceback (most recent call last): + ... + TypeError: unable to convert x (=n) to an integer + + Use python sum() instead:: - Also, only a limited number of functions are recognized in symbolic sums:: - - sage: sum(valuation(n,2),n,1,5) - AttributeError ... - ... - AttributeError: 'sage.symbolic.expression.Expression' object has no attribute 'valuation' + sage: sum(list[n] for n in range(4)) + 10 + + Also, only a limited number of functions are recognized in symbolic sums:: - Again, use python sum():: - - sage: sum(valuation(n+1,2) for n in range(5)) - 3 + sage: sum(valuation(n,2),n,1,5) + Traceback (most recent call last): + ... + AttributeError: 'sage.symbolic.expression.Expression' object has no attribute 'valuation' + + Again, use python sum():: + + sage: sum(valuation(n+1,2) for n in range(5)) + 3 INPUT: diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index b7adabec47e..4cafc173a59 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -9761,10 +9761,10 @@ cdef class Expression(CommutativeRingElement): In particular, this does not work:: - sage: var('n') + sage: n = var('n') sage: list=[1,2,3,4,5] sage: sum(list[n],n,0,3) - TypeError ... + Traceback (most recent call last): ... TypeError: unable to convert x (=n) to an integer @@ -9776,7 +9776,7 @@ cdef class Expression(CommutativeRingElement): Also, only a limited number of functions are recognized in symbolic sums:: sage: sum(valuation(n,2),n,1,5) - AttributeError ... + Traceback (most recent call last): ... AttributeError: 'sage.symbolic.expression.Expression' object has no attribute 'valuation' @@ -9784,7 +9784,7 @@ cdef class Expression(CommutativeRingElement): sage: sum(valuation(n+1,2) for n in range(5)) 3 - + INPUT: - ``v`` - a variable or variable name From 5f47daf7c25df7063b612899d363a1a7f6719895 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Wed, 19 Mar 2014 17:22:44 +0100 Subject: [PATCH 027/546] moved warning after some examples --- src/sage/calculus/calculus.py | 68 ++++++++++++++++---------------- src/sage/misc/functional.py | 68 ++++++++++++++++---------------- src/sage/symbolic/expression.pyx | 68 ++++++++++++++++---------------- 3 files changed, 105 insertions(+), 99 deletions(-) diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index faa57e34c92..bdf7e825f53 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -435,39 +435,6 @@ def symbolic_sum(expression, v, a, b, algorithm='maxima'): Returns the symbolic sum `\sum_{v = a}^b expression` with respect to the variable `v` with endpoints `a` and `b`. - .. WARNING:: - - This function only works with symbolic expressions. To sum any - other objects like list elements or function return values, - please use python summation, see - http://docs.python.org/2.7/library/functions.html#sum - - In particular, this does not work:: - - sage: n = var('n') - sage: list=[1,2,3,4,5] - sage: sum(list[n],n,0,3) - Traceback (most recent call last): - ... - TypeError: unable to convert x (=n) to an integer - - Use python sum() instead:: - - sage: sum(list[n] for n in range(4)) - 10 - - Also, only a limited number of functions are recognized in symbolic sums:: - - sage: sum(valuation(n,2),n,1,5) - Traceback (most recent call last): - ... - AttributeError: 'sage.symbolic.expression.Expression' object has no attribute 'valuation' - - Again, use python sum():: - - sage: sum(valuation(n+1,2) for n in range(5)) - 3 - INPUT: - ``expression`` - a symbolic expression @@ -501,6 +468,41 @@ def symbolic_sum(expression, v, a, b, algorithm='maxima'): sage: symbolic_sum(1/k^5, k, 1, oo) zeta(5) + .. WARNING:: + + This function only works with symbolic expressions. To sum any + other objects like list elements or function return values, + please use python summation, see + http://docs.python.org/library/functions.html#sum + + In particular, this does not work:: + + sage: n = var('n') + sage: list=[1,2,3,4,5] + sage: sum(list[n],n,0,3) + Traceback (most recent call last): + ... + TypeError: unable to convert x (=n) to an integer + + Use python ``sum()`` instead:: + + sage: sum(list[n] for n in range(4)) + 10 + + Also, only a limited number of functions are recognized in symbolic sums:: + + sage: sum(valuation(n,2),n,1,5) + Traceback (most recent call last): + ... + AttributeError: 'sage.symbolic.expression.Expression' object has no attribute 'valuation' + + Again, use python ``sum()``:: + + sage: sum(valuation(n+1,2) for n in range(5)) + 3 + + (now back to the Sage ``sum`` examples) + A well known binomial identity:: sage: symbolic_sum(binomial(n,k), k, 0, n) diff --git a/src/sage/misc/functional.py b/src/sage/misc/functional.py index df64faa7ae9..7035bd8b568 100644 --- a/src/sage/misc/functional.py +++ b/src/sage/misc/functional.py @@ -542,39 +542,6 @@ def symbolic_sum(expression, *args, **kwds): Returns the symbolic sum `\sum_{v = a}^b expression` with respect to the variable `v` with endpoints `a` and `b`. - .. WARNING:: - - This function only works with symbolic expressions. To sum any - other objects like list elements or function return values, - please use python summation, see - http://docs.python.org/2.7/library/functions.html#sum - - In particular, this does not work:: - - sage: n = var('n') - sage: list=[1,2,3,4,5] - sage: sum(list[n],n,0,3) - Traceback (most recent call last): - ... - TypeError: unable to convert x (=n) to an integer - - Use python sum() instead:: - - sage: sum(list[n] for n in range(4)) - 10 - - Also, only a limited number of functions are recognized in symbolic sums:: - - sage: sum(valuation(n,2),n,1,5) - Traceback (most recent call last): - ... - AttributeError: 'sage.symbolic.expression.Expression' object has no attribute 'valuation' - - Again, use python sum():: - - sage: sum(valuation(n+1,2) for n in range(5)) - 3 - INPUT: - ``expression`` - a symbolic expression @@ -606,6 +573,41 @@ def symbolic_sum(expression, *args, **kwds): sage: sum(1/k^5, k, 1, oo) zeta(5) + .. WARNING:: + + This function only works with symbolic expressions. To sum any + other objects like list elements or function return values, + please use python summation, see + http://docs.python.org/library/functions.html#sum + + In particular, this does not work:: + + sage: n = var('n') + sage: list=[1,2,3,4,5] + sage: sum(list[n],n,0,3) + Traceback (most recent call last): + ... + TypeError: unable to convert x (=n) to an integer + + Use python ``sum()`` instead:: + + sage: sum(list[n] for n in range(4)) + 10 + + Also, only a limited number of functions are recognized in symbolic sums:: + + sage: sum(valuation(n,2),n,1,5) + Traceback (most recent call last): + ... + AttributeError: 'sage.symbolic.expression.Expression' object has no attribute 'valuation' + + Again, use python ``sum()``:: + + sage: sum(valuation(n+1,2) for n in range(5)) + 3 + + (now back to the Sage ``sum`` examples) + A well known binomial identity:: sage: sum(binomial(n,k), k, 0, n) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 4cafc173a59..63ea7bf8ad1 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -9752,39 +9752,6 @@ cdef class Expression(CommutativeRingElement): with respect to the variable `v` with endpoints `a` and `b`. - .. WARNING:: - - This function only works with symbolic expressions. To sum any - other objects like list elements or function return values, - please use python summation, see - http://docs.python.org/2.7/library/functions.html#sum - - In particular, this does not work:: - - sage: n = var('n') - sage: list=[1,2,3,4,5] - sage: sum(list[n],n,0,3) - Traceback (most recent call last): - ... - TypeError: unable to convert x (=n) to an integer - - Use python sum() instead:: - - sage: sum(list[n] for n in range(4)) - 10 - - Also, only a limited number of functions are recognized in symbolic sums:: - - sage: sum(valuation(n,2),n,1,5) - Traceback (most recent call last): - ... - AttributeError: 'sage.symbolic.expression.Expression' object has no attribute 'valuation' - - Again, use python sum():: - - sage: sum(valuation(n+1,2) for n in range(5)) - 3 - INPUT: - ``v`` - a variable or variable name @@ -9820,6 +9787,41 @@ cdef class Expression(CommutativeRingElement): sage: (1/k^5).sum(k, 1, oo) zeta(5) + .. WARNING:: + + This function only works with symbolic expressions. To sum any + other objects like list elements or function return values, + please use python summation, see + http://docs.python.org/library/functions.html#sum + + In particular, this does not work:: + + sage: n = var('n') + sage: list=[1,2,3,4,5] + sage: sum(list[n],n,0,3) + Traceback (most recent call last): + ... + TypeError: unable to convert x (=n) to an integer + + Use python ``sum()`` instead:: + + sage: sum(list[n] for n in range(4)) + 10 + + Also, only a limited number of functions are recognized in symbolic sums:: + + sage: sum(valuation(n,2),n,1,5) + Traceback (most recent call last): + ... + AttributeError: 'sage.symbolic.expression.Expression' object has no attribute 'valuation' + + Again, use python ``sum()``:: + + sage: sum(valuation(n+1,2) for n in range(5)) + 3 + + (now back to the Sage ``sum`` examples) + A well known binomial identity:: sage: assume(n>=0) From 433257a8680201b4f1a30a054949d8f735f7d341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 25 Mar 2014 21:39:14 +0100 Subject: [PATCH 028/546] trac #14004 added some references --- .../root_system/root_lattice_realizations.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/root_system/root_lattice_realizations.py b/src/sage/combinat/root_system/root_lattice_realizations.py index 8a812cabd86..a951d5ae2ac 100644 --- a/src/sage/combinat/root_system/root_lattice_realizations.py +++ b/src/sage/combinat/root_system/root_lattice_realizations.py @@ -708,6 +708,8 @@ def nonnesting_partition_lattice(self, facade=False): This is the lattice of order ideals of the root poset. + This has been defined by Postnikov, see Remark 2 in [Reiner97]_. + .. SEEALSO:: :meth:`generalized_nonnesting_partition_lattice`, :meth:`root_poset` @@ -722,12 +724,19 @@ def nonnesting_partition_lattice(self, facade=False): True REFERENCES: + + .. [Reiner97] Victor Reiner. *Non-crossing partitions for + classical reflection groups*. Discrete Mathematics 177 (1997) + .. [Arm06] Drew Armstrong. *Generalized Noncrossing Partitions and + Combinatorics of Coxeter Groups*. :arxiv:`math/0611106` """ return self.root_poset(facade=facade).order_ideals_lattice(facade=facade) def generalized_nonnesting_partition_lattice(self, m, facade=False): r""" - Return the lattice of m-nonnesting partitions + Return the lattice of `m`-nonnesting partitions + + This has been defined by Athanasiadis, see chapter 5 of [Arm06]_. INPUT: @@ -745,8 +754,6 @@ def generalized_nonnesting_partition_lattice(self, m, facade=False): Finite lattice containing 12 elements sage: P.coxeter_transformation()**20 == 1 True - - REFERENCES: """ from sage.combinat.multichoose_nk import MultichooseNK Phi_plus = self.positive_roots() From ee630f53b20aab08d094f0f9914875f30efd5e66 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Wed, 26 Mar 2014 16:17:37 +0100 Subject: [PATCH 029/546] add %c,%k1,%k2 to recognized maxima objects --- src/sage/calculus/calculus.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index bb6660aa127..829b03bcb93 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -1683,7 +1683,10 @@ def _inverse_laplace_latex_(self, *args): ####################################################### -symtable = {'%pi':'pi', '%e': 'e', '%i':'I', '%gamma':'euler_gamma'} +# Conversion dict for special maxima objects +# c,k1,k2 are from ode2() +symtable = {'%pi':'pi', '%e': 'e', '%i':'I', '%gamma':'euler_gamma',\ + '%c' : '_C', '%k1' : '_K1', '%k2' : '_K2'} from sage.misc.multireplace import multiple_replace import re From 44ae929f513bb00df4dc006d3888a9e597263264 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 31 Mar 2014 15:53:13 -0700 Subject: [PATCH 030/546] Hash function for families. --- src/sage/sets/family.py | 66 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/sage/sets/family.py b/src/sage/sets/family.py index ac89e6e7a06..7d5f06586c1 100644 --- a/src/sage/sets/family.py +++ b/src/sage/sets/family.py @@ -547,6 +547,25 @@ def __init__(self, dictionary, keys = None): self.keys = dictionary.keys self.values = dictionary.values + @cached_method + def __hash__(self): + """ + Return a hash value for ``self``. + + EXAMPLES:: + + sage: f = Family(["c", "a", "b"], lambda x: x+x) + sage: hash(f) == hash(f) + True + sage: f2 = Family(["a", "c", "b"], lambda x: x+x) + sage: hash(f) == hash(f2) + True + sage: g = Family(["a", "c", "b"], lambda x: x+x+x) + sage: hash(f) == hash(g) + False + """ + return hash(frozenset(list(self.keys()) + map(repr, self.values()))) + def keys(self): """ Returns the index set of this family @@ -857,6 +876,40 @@ def __init__(self, set, function, name=None): self.function = function self.function_name = name + @cached_method + def __hash__(self): + """ + Return a hash value for ``self``. + + TESTS:: + + sage: from sage.sets.family import LazyFamily + sage: f = LazyFamily([3,4,7], lambda i: 2*i) + sage: hash(f) == hash(f) + True + sage: g = LazyFamily(ZZ, lambda i: 2*i) + sage: hash(g) == hash(g) + True + """ + try: + h = hash(self.keys()) + + if self.function_name is not None: + name = self.function_name + "(i)" + elif isinstance(self.function, type(lambda x:1)): + name = self.function.__name__ + name = name+"(i)" + else: + name = repr(self.function) + if isinstance(self.function, AttrCallObject): + name = "i"+name[1:] + else: + name = name+"(i)" + + return h + hash(name) + except TypeError: + return super(LazyFamily, self).__hash__() + def __eq__(self, other): """ WARNING: Since there is no way to compare function, we only compare @@ -1073,6 +1126,19 @@ def __eq__(self, other): return (isinstance(other, self.__class__) and self._enumeration == other._enumeration) + def __hash__(self): + """ + Return a hash value for ``self``. + + TESTS:: + + sage: from sage.sets.family import TrivialFamily + sage: f = TrivialFamily((3,4,7)) + sage: hash(f) == hash(f) + True + """ + return hash(self._enumeration) + def _repr_(self): """ EXAMPLES:: From 0fd3d318a230b2865cf331c2c696c9d05ceba487 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Tue, 1 Apr 2014 23:57:13 +0100 Subject: [PATCH 031/546] Trac 804: make Matrix inherit from ModuleElement, not AlgebraElement --- src/doc/en/tutorial/tour_coercion.rst | 15 +++--- src/doc/fr/tutorial/tour_coercion.rst | 14 +++--- src/sage/matrix/matrix0.pyx | 15 +++--- src/sage/structure/element.pxd | 2 +- src/sage/structure/element.pyx | 70 +++++++++++++++------------ src/sage/symbolic/ring.pyx | 6 +-- 6 files changed, 64 insertions(+), 58 deletions(-) diff --git a/src/doc/en/tutorial/tour_coercion.rst b/src/doc/en/tutorial/tour_coercion.rst index 2b8d526b2fb..3e41361f13f 100644 --- a/src/doc/en/tutorial/tour_coercion.rst +++ b/src/doc/en/tutorial/tour_coercion.rst @@ -145,18 +145,17 @@ cached: Types versus parents -------------------- -The type ``RingElement`` should not be confused with the mathematical -notion of a ring element; for practical reasons, sometimes an object -is an instance of ``RingElement`` although it does not belong to a -ring: +The type ``RingElement`` does not correspond perfectly to the +mathematical notion of a ring element. For example, although square +matrices belong to a ring, they are not instances of ``RingElement``: :: - sage: M = Matrix(ZZ,2,3); M - [0 0 0] - [0 0 0] + sage: M = Matrix(ZZ,2,2); M + [0 0] + [0 0] sage: isinstance(M, RingElement) - True + False While *parents* are unique, equal *elements* of a parent in Sage are not necessarily identical. This is in contrast to the behaviour of Python diff --git a/src/doc/fr/tutorial/tour_coercion.rst b/src/doc/fr/tutorial/tour_coercion.rst index 4f863887d73..2d7b2fb884d 100644 --- a/src/doc/fr/tutorial/tour_coercion.rst +++ b/src/doc/fr/tutorial/tour_coercion.rst @@ -146,17 +146,17 @@ est construit, il est conservé en cache et réutilisé par la suite : Types et parents ---------------- -Il ne faut pas confondre le type ``RingElement`` avec la notion mathématique -d'élément d'anneau : il peut arriver que pour des raisons pratiques, un objet -soit de type ``RingElement`` alors qu'il n'appartient pas à un anneau : +Le type ``RingElement`` ne correspond pas parfaitement à la notion +mathématique d'élément d'anneau. Par exemple, bien que les matrices carrées +appartiennent à un anneau, elles ne sont pas de type ``RingElement`` : :: - sage: M = Matrix(ZZ,2,3); M - [0 0 0] - [0 0 0] + sage: M = Matrix(ZZ,2,2); M + [0 0] + [0 0] sage: isinstance(M, RingElement) - True + False Si les *parents* sont censés être uniques, des *éléments* égaux d'un parent ne sont pas nécessairement identiques. Le comportement de Sage diffère ici de diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index 391e719fc3f..d59ddb9c2b3 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -4363,13 +4363,12 @@ cdef class Matrix(sage.structure.element.Matrix): if PY_TYPE_CHECK(self._base_ring, CommutativeRing): return self._lmul_(left) cdef Py_ssize_t r,c - cpdef RingElement x - x = self._base_ring(left) + left = self._base_ring(left) cdef Matrix ans ans = self._parent.zero_matrix().__copy__() for r from 0 <= r < self._nrows: for c from 0 <= c < self._ncols: - ans.set_unsafe(r, c, x._mul_(self.get_unsafe(r, c))) + ans.set_unsafe(r, c, left * self.get_unsafe(r, c)) return ans cpdef ModuleElement _lmul_(self, RingElement right): @@ -4406,13 +4405,12 @@ cdef class Matrix(sage.structure.element.Matrix): """ # derived classes over a commutative base *just* overload this and not _rmul_ cdef Py_ssize_t r,c - cpdef RingElement x - x = self._base_ring(right) + right = self._base_ring(right) cdef Matrix ans ans = self._parent.zero_matrix().__copy__() for r from 0 <= r < self._nrows: for c from 0 <= c < self._ncols: - ans.set_unsafe(r, c, (self.get_unsafe(r, c))._mul_(x)) + ans.set_unsafe(r, c, self.get_unsafe(r, c) * right) return ans cdef sage.structure.element.Matrix _matrix_times_matrix_(self, sage.structure.element.Matrix right): @@ -4818,8 +4816,9 @@ cdef class Matrix(sage.structure.element.Matrix): """ if not self.is_square(): raise ArithmeticError("self must be a square matrix") - - return RingElement.__pow__(self, n, ignored) + if ignored is not None: + raise RuntimeError("__pow__ third argument not used") + return sage.structure.element.generic_power_c(self, n, None) ################################################### # Comparison diff --git a/src/sage/structure/element.pxd b/src/sage/structure/element.pxd index 9cac7a7bc8c..72804c08cb3 100644 --- a/src/sage/structure/element.pxd +++ b/src/sage/structure/element.pxd @@ -113,7 +113,7 @@ cdef class Vector(ModuleElement): cdef bint is_dense_c(self) -cdef class Matrix(AlgebraElement): +cdef class Matrix(ModuleElement): # All matrix classes must be written in Cython cdef Py_ssize_t _nrows cdef Py_ssize_t _ncols diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index eedd5a70b61..c1cbea8acbd 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -2521,7 +2521,7 @@ cdef class Vector(ModuleElement): def is_Vector(x): return IS_INSTANCE(x, Vector) -cdef class Matrix(AlgebraElement): +cdef class Matrix(ModuleElement): cdef bint is_sparse_c(self): raise NotImplementedError @@ -2536,36 +2536,6 @@ cdef class Matrix(AlgebraElement): global coercion_model return coercion_model.bin_op(left, right, imul) - cpdef RingElement _mul_(left, RingElement right): - """ - TESTS:: - - sage: m = matrix - sage: a = m([[m([[1,2],[3,4]]),m([[5,6],[7,8]])],[m([[9,10],[11,12]]),m([[13,14],[15,16]])]]) - sage: 3*a - [[ 3 6] - [ 9 12] [15 18] - [21 24]] - [[27 30] - [33 36] [39 42] - [45 48]] - - sage: m = matrix - sage: a = m([[m([[1,2],[3,4]]),m([[5,6],[7,8]])],[m([[9,10],[11,12]]),m([[13,14],[15,16]])]]) - sage: a*3 - [[ 3 6] - [ 9 12] [15 18] - [21 24]] - [[27 30] - [33 36] [39 42] - [45 48]] - """ - if have_same_parent(left, right): - return (left)._matrix_times_matrix_(right) - else: - global coercion_model - return coercion_model.bin_op(left, right, mul) - def __mul__(left, right): """ Multiplication of matrix by matrix, vector, or scalar @@ -2727,6 +2697,27 @@ cdef class Matrix(AlgebraElement): ... TypeError: unsupported operand parent(s) for '*': 'Full MatrixSpace of 2 by 2 dense matrices over Univariate Polynomial Ring in x over Rational Field' and 'Univariate Polynomial Ring in y over Rational Field' + Examples with matrices having matrix coefficients:: + + sage: m = matrix + sage: a = m([[m([[1,2],[3,4]]),m([[5,6],[7,8]])],[m([[9,10],[11,12]]),m([[13,14],[15,16]])]]) + sage: 3*a + [[ 3 6] + [ 9 12] [15 18] + [21 24]] + [[27 30] + [33 36] [39 42] + [45 48]] + + sage: m = matrix + sage: a = m([[m([[1,2],[3,4]]),m([[5,6],[7,8]])],[m([[9,10],[11,12]]),m([[13,14],[15,16]])]]) + sage: a*3 + [[ 3 6] + [ 9 12] [15 18] + [21 24]] + [[27 30] + [33 36] [39 42] + [45 48]] """ if have_same_parent(left, right): return (left)._matrix_times_matrix_(right) @@ -2734,6 +2725,23 @@ cdef class Matrix(AlgebraElement): global coercion_model return coercion_model.bin_op(left, right, mul) + def __div__(left, right): + """ + Division of the matrix ``left`` by the matrix or scalar ``right``. + + EXAMPLES:: + + sage: a = matrix(ZZ, 2, range(4)) + sage: a / 5 + [ 0 1/5] + [2/5 3/5] + """ + if have_same_parent(left, right): + return (left)._matrix_times_matrix_(~right) + else: + global coercion_model + return coercion_model.bin_op(left, right, div) + cdef Vector _vector_times_matrix_(matrix_right, Vector vector_left): raise TypeError diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index 2894e489bc5..04272ce6fc8 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -22,13 +22,13 @@ include "sage/ext/cdefs.pxi" from ginac cimport * from sage.rings.integer cimport Integer -from sage.rings.real_mpfr import RealNumber +from sage.rings.real_mpfr cimport RealNumber from sage.symbolic.expression cimport Expression, new_Expression_from_GEx, new_Expression_from_pyobject, is_Expression from sage.libs.pari.pari_instance cimport PariInstance from sage.misc.latex import latex_variable_name -from sage.structure.element cimport RingElement, Element +from sage.structure.element cimport RingElement, Element, Matrix from sage.structure.parent_base import ParentWithBase from sage.rings.ring cimport CommutativeRing from sage.categories.morphism cimport Morphism @@ -283,7 +283,7 @@ cdef class SymbolicRing(CommutativeRing): return new_Expression_from_GEx(self, g_mInfinity) elif x is unsigned_infinity: return new_Expression_from_GEx(self, g_UnsignedInfinity) - elif isinstance(x, RingElement): + elif isinstance(x, (RingElement, Matrix)): GEx_construct_pyobject(exp, x) else: raise TypeError From 3d90c49bc248680a90f2788697311e65f297234f Mon Sep 17 00:00:00 2001 From: Jason Grout Date: Sat, 8 Mar 2014 09:19:27 +0100 Subject: [PATCH 032/546] Trac #8734: Make sage variables unique in maxima. --- src/sage/calculus/calculus.py | 10 +++++++++- src/sage/symbolic/assumptions.py | 2 +- src/sage/symbolic/expression_conversions.py | 22 +++++++++++++-------- src/sage/symbolic/relation.py | 2 +- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index bb6660aa127..9e5d3b8d144 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -1757,7 +1757,14 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): sage: solve([2*x==3, x != 5], x) [[x == (3/2), (-7/2) != 0]] - """ + Make sure that we don't accidentally pick up variables in the maxima namespace (trac #8734):: + + sage: sage.calculus.calculus.maxima('my_new_var : 2') + 2 + sage: var('my_new_var').full_simplify() + my_new_var + + """ syms = sage.symbolic.pynac.symbol_table.get('maxima', {}).copy() if len(x) == 0: @@ -1768,6 +1775,7 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): #r = maxima._eval_line('listofvars(_tmp_);')[1:-1] s = maxima._eval_line('_tmp_;') + s = s.replace("_SAGE_VAR_","") formal_functions = maxima_tick.findall(s) if len(formal_functions) > 0: diff --git a/src/sage/symbolic/assumptions.py b/src/sage/symbolic/assumptions.py index 393cb3ec4cf..db1ec328614 100644 --- a/src/sage/symbolic/assumptions.py +++ b/src/sage/symbolic/assumptions.py @@ -118,7 +118,7 @@ def assume(self): cur = maxima.get("context") self._context = maxima.newcontext('context' + maxima._next_var_name()) try: - maxima.eval("declare(%s, %s)" % (repr(self._var), self._assumption)) + maxima.eval("declare(%s, %s)" % (self._var._maxima_init(), self._assumption)) # except TypeError, mess: # if 'inconsistent' in str(mess): # note Maxima doesn't tell you if declarations are redundant # raise ValueError, "Assumption is inconsistent" diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index d4fa603c3d1..67d422c7092 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -398,7 +398,7 @@ def __init__(self, interface): sage: m(sin(a)) 'sin((%pi)+(2))' sage: m(exp(x^2) + pi + 2) - '(%pi)+(exp((x)^(2)))+(2)' + '(%pi)+(exp((_SAGE_VAR_x)^(2)))+(2)' """ self.name_init = "_%s_init_"%interface.name() @@ -412,12 +412,18 @@ def symbol(self, ex): sage: from sage.symbolic.expression_conversions import InterfaceInit sage: m = InterfaceInit(maxima) sage: m.symbol(x) - 'x' + '_SAGE_VAR_x' sage: f(x) = x sage: m.symbol(f) + '_SAGE_VAR_x' + sage: ii = InterfaceInit(gp) + sage: ii.symbol(x) 'x' """ - return repr(SR(ex)) + if self.interface.name()=='maxima': + return '_SAGE_VAR_'+repr(SR(ex)) + else: + return repr(SR(ex)) def pyobject(self, ex, obj): """ @@ -452,9 +458,9 @@ def relation(self, ex, operator): sage: from sage.symbolic.expression_conversions import InterfaceInit sage: m = InterfaceInit(maxima) sage: m.relation(x==3, operator.eq) - 'x = 3' + '_SAGE_VAR_x = 3' sage: m.relation(x==3, operator.lt) - 'x < 3' + '_SAGE_VAR_x < 3' """ return "%s %s %s"%(self(ex.lhs()), self.relation_symbols[operator], self(ex.rhs())) @@ -524,7 +530,7 @@ def arithmetic(self, ex, operator): sage: from sage.symbolic.expression_conversions import InterfaceInit sage: m = InterfaceInit(maxima) sage: m.arithmetic(x+2, operator.add) - '(x)+(2)' + '(_SAGE_VAR_x)+(2)' """ args = ["(%s)"%self(op) for op in ex.operands()] return arithmetic_operators[operator].join(args) @@ -536,9 +542,9 @@ def composition(self, ex, operator): sage: from sage.symbolic.expression_conversions import InterfaceInit sage: m = InterfaceInit(maxima) sage: m.composition(sin(x), sin) - 'sin(x)' + 'sin(_SAGE_VAR_x)' sage: m.composition(ceil(x), ceil) - 'ceiling(x)' + 'ceiling(_SAGE_VAR_x)' sage: m = InterfaceInit(mathematica) sage: m.composition(sin(x), sin) diff --git a/src/sage/symbolic/relation.py b/src/sage/symbolic/relation.py index a52a8cddff8..ce4c5b56f36 100644 --- a/src/sage/symbolic/relation.py +++ b/src/sage/symbolic/relation.py @@ -204,7 +204,7 @@ sage: x = var('x') sage: eq = (x^(3/5) >= pi^2 + e^i) sage: eq._maxima_init_() - '(x)^(3/5) >= ((%pi)^(2))+(exp(0+%i*1))' + '(_SAGE_VAR_x)^(3/5) >= ((%pi)^(2))+(exp(0+%i*1))' sage: e1 = x^3 + x == sin(2*x) sage: z = e1._maxima_() sage: z.parent() is sage.calculus.calculus.maxima From a6cbf80ccf71dac48cc8cd352cb39bd913ecf651 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sat, 22 Mar 2014 11:11:58 +0100 Subject: [PATCH 033/546] factor out missing assumption error handling; filter _SAGE_VAR_ --- src/sage/interfaces/maxima_lib.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 7bf09dbae56..db66aaa6ba3 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -739,10 +739,7 @@ def sr_integral(self,*args): # if "divergent" in s or 'Principal Value' in s: raise ValueError, "Integral is divergent." elif "Is" in s: # Maxima asked for a condition - j = s.find('Is ') - s = s[j:] - k = s.find(' ',4) - raise ValueError, "Computation failed since Maxima requested additional constraints; using the 'assume' command before integral evaluation *may* help (example of legal syntax is 'assume(" + s[4:k] +">0)', see `assume?` for more details)\n" + s + self.missing_assumption(s) else: raise error @@ -787,10 +784,7 @@ def sr_sum(self,*args): # if "divergent" in s or 'Pole encountered' in s: raise ValueError, "Sum is divergent." elif "Is" in s: # Maxima asked for a condition - j = s.find('Is ') - s = s[j:] - k = s.find(' ',4) - raise ValueError, "Computation failed since Maxima requested additional constraints; using the 'assume' command before summation *may* help (example of legal syntax is 'assume(" + s[4:k] +">0)', see `assume?` for more details)\n" + s + self.missing_assumption(s) else: raise error @@ -852,9 +846,7 @@ def sr_limit(self,expr,v,a,dir=None): except RuntimeError as error: s = str(error) if "Is" in s: # Maxima asked for a condition - j = s.find('Is ') - s = s[j:] - raise ValueError, "Computation failed since Maxima requested additional constraints; using the 'assume' command before limit evaluation *may* help (see `assume?` for more details)\n" + s + self.missing_assumption(s) else: raise error @@ -874,7 +866,20 @@ def sr_tlimit(self,expr,v,a,dir=None): elif dir == "minus": L.append(max_minus) return max_to_sr(maxima_eval(([max_tlimit],L))) - + + def missing_assumption(self,errstr): + """ + Helper function for unified handling of failed computation because an + assumption was missing. + """ + j = errstr.find('Is ') + errstr = errstr[j:] + k = errstr.find(' ',4) + + outstr = "Computation failed since Maxima requested additional constraints; using the 'assume' command before integral evaluation *may* help (example of legal syntax is 'assume("\ + + errstr[4:k] +">0)', see `assume?` for more details)\n" + errstr + outstr = outstr.replace('_SAGE_VAR_','') + raise ValueError, outstr def is_MaximaLibElement(x): r""" From 3f24835a9c82aeccde6135249bf1557cb9ced788 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sat, 22 Mar 2014 12:03:17 +0100 Subject: [PATCH 034/546] fix typo leading to error --- src/sage/symbolic/assumptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/symbolic/assumptions.py b/src/sage/symbolic/assumptions.py index db1ec328614..a4b7606b8cd 100644 --- a/src/sage/symbolic/assumptions.py +++ b/src/sage/symbolic/assumptions.py @@ -118,7 +118,7 @@ def assume(self): cur = maxima.get("context") self._context = maxima.newcontext('context' + maxima._next_var_name()) try: - maxima.eval("declare(%s, %s)" % (self._var._maxima_init(), self._assumption)) + maxima.eval("declare(%s, %s)" % (self._var._maxima_init_(), self._assumption)) # except TypeError, mess: # if 'inconsistent' in str(mess): # note Maxima doesn't tell you if declarations are redundant # raise ValueError, "Assumption is inconsistent" From f36c6b52d6795d81b9c7918926e9a92ac5220cdd Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sat, 22 Mar 2014 16:19:01 +0100 Subject: [PATCH 035/546] two further maxima calls adapted; two more doctests fixed --- src/sage/symbolic/expression.pyx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 1f2af5b0502..a07302aab53 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -1565,7 +1565,7 @@ cdef class Expression(CommutativeRingElement): sage: f = x+2 > sqrt(3) sage: f._maxima_init_assume_() - '((x)+(2))>((3/1)^(1/2))' + '((_SAGE_VAR_x)+(2))>((3/1)^(1/2))' """ from sage.calculus.calculus import maxima @@ -1586,12 +1586,12 @@ cdef class Expression(CommutativeRingElement): sage: x = var('x') sage: x._assume_str() - 'x' + '_SAGE_VAR_x' sage: y = function('y', x) sage: y._assume_str() 'y' sage: abs(x)._assume_str() - 'abs(x)' + 'abs(_SAGE_VAR_x)' """ # if this is a function with a single argument which is a symbol, i.e. # this is of the form f(x), we pass the string 'f > 0' @@ -3884,7 +3884,7 @@ cdef class Expression(CommutativeRingElement): if var is None: cmd = 'trigreduce(%s)'%(M.name()) else: - cmd = 'trigreduce(%s,%s)'%(M.name(),str(var)) + cmd = 'trigreduce(%s,%s)'%(M.name(),'_SAGE_VAR_'+str(var)) ans = P(cmd) return self.parent()(ans) @@ -8724,7 +8724,8 @@ cdef class Expression(CommutativeRingElement): if len(dontfactor) > 0: m = self._maxima_() name = m.name() - cmd = 'block([dontfactor:%s],factor(%s))'%(dontfactor, name) + varstr = ','.join(['_SAGE_VAR_'+str(v) for v in dontfactor]) + cmd = 'block([dontfactor:[%s]],factor(%s))'%(varstr, name) return symbolic_expression_from_maxima_string(cmd) else: try: From ced268db4e1e150b453297d05bc42ab31abbeba5 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sat, 22 Mar 2014 17:15:43 +0100 Subject: [PATCH 036/546] fix and generalize missing_assumption(); resp. doctest adaptions --- src/sage/calculus/calculus.py | 15 +++++++++------ src/sage/interfaces/maxima_lib.py | 9 ++++++--- src/sage/symbolic/integration/integral.py | 9 ++++++--- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index 9e5d3b8d144..fdcb3a148da 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -1048,8 +1048,9 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): Traceback (most recent call last): ... ValueError: Computation failed since Maxima requested additional - constraints; using the 'assume' command before limit evaluation - *may* help (see `assume?` for more details) + constraints; using the 'assume' command before evaluation *may* help + (example of legal syntax is 'assume(a>0)', see `assume?` for + more details) Is a positive, negative, or zero? With this example, Maxima is looking for a LOT of information:: @@ -1059,16 +1060,18 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): Traceback (most recent call last): ... ValueError: Computation failed since Maxima requested additional - constraints; using the 'assume' command before limit evaluation - *may* help (see `assume?` for more details) + constraints; using the 'assume' command before evaluation *may* help + (example of legal syntax is 'assume(a>0)', see `assume?` for + more details) Is a an integer? sage: assume(a,'integer') sage: limit(x^a,x=0) Traceback (most recent call last): ... ValueError: Computation failed since Maxima requested additional - constraints; using the 'assume' command before limit evaluation - *may* help (see `assume?` for more details) + constraints; using the 'assume' command before evaluation *may* help + (example of legal syntax is 'assume(a>0)', see `assume?` for + more details) Is a an even number? sage: assume(a,'even') sage: limit(x^a,x=0) diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index db66aaa6ba3..fb3999b67e6 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -874,10 +874,13 @@ def missing_assumption(self,errstr): """ j = errstr.find('Is ') errstr = errstr[j:] - k = errstr.find(' ',4) + jj = 2 + if errstr[3] == ' ': + jj = 3 + k = errstr.find(' ',jj+1) - outstr = "Computation failed since Maxima requested additional constraints; using the 'assume' command before integral evaluation *may* help (example of legal syntax is 'assume("\ - + errstr[4:k] +">0)', see `assume?` for more details)\n" + errstr + outstr = "Computation failed since Maxima requested additional constraints; using the 'assume' command before evaluation *may* help (example of legal syntax is 'assume("\ + + errstr[jj+1:k] +">0)', see `assume?` for more details)\n" + errstr outstr = outstr.replace('_SAGE_VAR_','') raise ValueError, outstr diff --git a/src/sage/symbolic/integration/integral.py b/src/sage/symbolic/integration/integral.py index d0925292427..76be57b3377 100644 --- a/src/sage/symbolic/integration/integral.py +++ b/src/sage/symbolic/integration/integral.py @@ -393,7 +393,7 @@ def integrate(expression, v=None, a=None, b=None, algorithm=None): Traceback (most recent call last): ... ValueError: Computation failed since Maxima requested additional - constraints; using the 'assume' command before integral evaluation + constraints; using the 'assume' command before evaluation *may* help (example of legal syntax is 'assume(n+1>0)', see `assume?` for more details) Is n+1 zero or nonzero? @@ -515,7 +515,7 @@ def integrate(expression, v=None, a=None, b=None, algorithm=None): Traceback (most recent call last): ... ValueError: Computation failed since Maxima requested additional - constraints; using the 'assume' command before integral evaluation + constraints; using the 'assume' command before evaluation *may* help (example of legal syntax is 'assume(a>0)', see `assume?` for more details) Is a positive or negative? @@ -555,7 +555,10 @@ def integrate(expression, v=None, a=None, b=None, algorithm=None): sage: res = integral(f,x,0.0001414, 1.); res Traceback (most recent call last): ... - ValueError: Computation failed since Maxima requested additional constraints; using the 'assume' command before integral evaluation *may* help (example of legal syntax is 'assume(50015104*y^2-50015103>0)', see `assume?` for more details) + ValueError: Computation failed since Maxima requested additional + constraints; using the 'assume' command before evaluation + *may* help (example of legal syntax is 'assume(50015104*y^2-50015103>0)', + see `assume?` for more details) Is 50015104*y^2-50015103 positive, negative, or zero? sage: assume(y>1) sage: res = integral(f,x,0.0001414, 1.); res From a74ec004d982cebddcd5cbb6d489f1b5978451c7 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Mon, 24 Mar 2014 16:01:14 +0100 Subject: [PATCH 037/546] add even more cases to add _SAGE_VAR_; fix doctests --- src/sage/calculus/calculus.py | 2 +- src/sage/calculus/desolvers.py | 30 ++++++++++++--------- src/sage/calculus/functional.py | 2 +- src/sage/symbolic/expression_conversions.py | 24 +++++++++-------- 4 files changed, 33 insertions(+), 25 deletions(-) diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index fdcb3a148da..de971625abe 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -1755,7 +1755,7 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): sage: sefms("x # 3") == SR(x != 3) True sage: solve([x != 5], x) - #0: solve_rat_ineq(ineq=x # 5) + #0: solve_rat_ineq(ineq=_SAGE_VAR_x # 5) [[x - 5 != 0]] sage: solve([2*x==3, x != 5], x) [[x == (3/2), (-7/2) != 0]] diff --git a/src/sage/calculus/desolvers.py b/src/sage/calculus/desolvers.py index 1c44e8a6132..20d046e84db 100644 --- a/src/sage/calculus/desolvers.py +++ b/src/sage/calculus/desolvers.py @@ -266,7 +266,7 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) Some more types od ODE's:: sage: desolve(x*diff(y,x)^2-(1+x*y)*diff(y,x)+y==0,y,contrib_ode=True,show_method=True) - [[y(x) == c + log(x), y(x) == c*e^x], 'factor'] + [[y(x) == c*e^x, y(x) == c + log(x)], 'factor'] :: @@ -420,13 +420,14 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) if len(ivars) != 1: raise ValueError("Unable to determine independent variable, please specify.") ivar = ivars[0] - def sanitize_var(exprs): - return exprs.replace("'"+dvar_str+"("+ivar_str+")",dvar_str) de00 = de._maxima_() P = de00.parent() dvar_str=P(dvar.operator()).str() ivar_str=P(ivar).str() de00 = de00.str() + def sanitize_var(exprs): + t = exprs.replace("'"+dvar_str+"(_SAGE_VAR_"+ivar_str+")",dvar_str) + return t.replace("'"+dvar_str+"("+ivar_str+")",dvar_str) de0 = sanitize_var(de00) ode_solver="ode2" cmd="(TEMP:%s(%s,%s,%s), if TEMP=false then TEMP else substitute(%s=%s(%s),TEMP))"%(ode_solver,de0,dvar_str,ivar_str,dvar_str,dvar_str,ivar_str) @@ -660,11 +661,15 @@ def desolve_laplace(de, dvar, ics=None, ivar=None): ivar = ivars[0] ## verbatim copy from desolve - end + dvar_str = str(dvar) def sanitize_var(exprs): # 'y(x) -> y(x) - return exprs.replace("'"+str(dvar),str(dvar)) + t = exprs.replace("'"+dvar_str,dvar_str) + return t.replace("'_SAGE_VAR_"+dvar_str,dvar_str) de0=de._maxima_() P = de0.parent() - cmd = sanitize_var("desolve("+de0.str()+","+str(dvar)+")") + i = dvar_str.find('(') + dvar_str = dvar_str[:i+1] + '_SAGE_VAR_' + dvar_str[i+1:] + cmd = sanitize_var("desolve("+de0.str()+","+dvar_str+")") soln=P(cmd).rhs() if str(soln).strip() == 'false': raise NotImplementedError("Maxima was unable to solve this ODE.") @@ -836,16 +841,17 @@ def desolve_system_strings(des,vars,ics=None): maxima.eval(cmd) desstr = "[" + ",".join(dess) + "]" d = len(vars) - varss = list("'" + vars[i] + "(" + vars[0] + ")" for i in range(1,d)) + varss = list("'" + vars[i] + "(_SAGE_VAR_" + vars[0] + ")" for i in range(1,d)) varstr = "[" + ",".join(varss) + "]" if ics is not None: #d = len(ics) ## must be same as len(des) for i in range(1,d): - ic = "atvalue('" + vars[i] + "("+vars[0] + ")," + str(vars[0]) + "=" + str(ics[0]) + "," + str(ics[i]) + ")" + ic = "atvalue('" + vars[i] + "(_SAGE_VAR_"+vars[0] + ")," + "_SAGE_VAR_"\ + + str(vars[0]) + "=" + str(ics[0]) + "," + str(ics[i]) + ")" maxima.eval(ic) cmd = "desolve(" + desstr + "," + varstr + ");" soln = maxima(cmd) - return [f.rhs()._maxima_init_() for f in soln] + return [f.rhs()._maxima_init_().replace("_SAGE_VAR_"+vars[0],vars[0]) for f in soln] @rename_keyword(deprecation=6094, method="algorithm") def eulers_method(f,x0,y0,h,x1,algorithm="table"): @@ -1223,13 +1229,13 @@ def desolve_rk4(de, dvar, ics=None, ivar=None, end_points=None, step=0.1, output sol_1, sol_2 = [],[] if lower_boundics[0]: cmd="rk(%s,%s,%s,[%s,%s,%s,%s])\ - "%(de0.str(),str(dummy_dvar),str(ics[1]),str(ivar),str(ics[0]),upper_bound,step) + "%(de0.str(),'_SAGE_VAR_'+str(dummy_dvar),str(ics[1]),'_SAGE_VAR_'+str(ivar),str(ics[0]),upper_bound,step) sol_2=maxima(cmd).sage() sol_2.pop(0) sol=sol_1 @@ -1343,13 +1349,13 @@ def desolve_system_rk4(des, vars, ics=None, ivar=None, end_points=None, step=0.1 sol_1, sol_2 = [],[] if lower_boundics[0]: cmd="rk(%s,%s,%s,[%s,%s,%s,%s])\ - "%(desstr,varstr,icstr,str(ivar),str(x0),upper_bound,step) + "%(desstr,varstr,icstr,'_SAGE_VAR_'+str(ivar),str(x0),upper_bound,step) sol_2=maxima(cmd).sage() sol_2.pop(0) sol=sol_1 diff --git a/src/sage/calculus/functional.py b/src/sage/calculus/functional.py index 48d75aac9c0..6bfcded1ee5 100644 --- a/src/sage/calculus/functional.py +++ b/src/sage/calculus/functional.py @@ -243,7 +243,7 @@ def integral(f, *args, **kwds): Traceback (most recent call last): ... ValueError: Computation failed since Maxima requested additional - constraints; using the 'assume' command before integral evaluation + constraints; using the 'assume' command before evaluation *may* help (example of legal syntax is 'assume(a>0)', see `assume?` for more details) Is a positive, negative, or zero? diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index 67d422c7092..5ffa7f3c9c5 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -509,19 +509,21 @@ def derivative(self, ex, operator): params = operator.parameter_set() params = ["%s, %s"%(temp_args[i], params.count(i)) for i in set(params)] subs = ["%s = %s"%(t,a) for t,a in zip(temp_args,args)] - return "at(diff('%s(%s), %s), [%s])"%(f.name(), + outstr = "at(diff('%s(%s), %s), [%s])"%(f.name(), ", ".join(map(repr,temp_args)), ", ".join(params), - ", ".join(subs)) - - f = operator.function() - params = operator.parameter_set() - params = ["%s, %s"%(args[i], params.count(i)) for i in set(params)] - - return "diff('%s(%s), %s)"%(f.name(), - ", ".join(map(repr, args)), - ", ".join(params)) - + ", ".join(subs)) + else: + f = operator.function() + params = operator.parameter_set() + def prep_sage(arg): + return '_SAGE_VAR_' + repr(arg) + params = ["%s, %s"%(prep_sage(args[i]), params.count(i)) for i in set(params)] + outstr = "diff('%s(%s), %s)"%(f.name(), + ", ".join(map(prep_sage, args)), + ", ".join(params)) + return outstr + def arithmetic(self, ex, operator): """ EXAMPLES:: From 7b96659d6743e17ca15b84ea1ac79e466ce8bcb5 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Thu, 10 Apr 2014 11:36:06 +0200 Subject: [PATCH 038/546] 14802 --- src/sage/symbolic/expression.pyx | 41 +++++++++++++++++++++++---- src/sage/symbolic/function_factory.py | 14 +++++---- src/sage/symbolic/pynac.pyx | 20 +++++++++++++ 3 files changed, 63 insertions(+), 12 deletions(-) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 1f2af5b0502..2c14141a065 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -4485,6 +4485,33 @@ cdef class Expression(CommutativeRingElement): """ return self.number_of_operands() + def _unpack_operands(self): + """ + Unpack the operands of this expression converting each to a Python + object if possible. + + This corresponds to the conversion performed when arguments of a + function are unpacked as they are being passed to custom methods of + a symbolic function. + + EXAMPLES:: + + sage: t = SR._force_pyobject((1, 2, x, x+1, x+2)) + sage: t._unpack_operands() + (1, 2, x, x + 1, x + 2) + sage: type(t._unpack_operands()) + + sage: map(type, t._unpack_operands()) + [, , , , ] + sage: u = SR._force_pyobject((t, x^2)) + sage: u._unpack_operands() + ((1, 2, x, x + 1, x + 2), x^2) + sage: type(u._unpack_operands()[0]) + + """ + from sage.symbolic.pynac import unpack_operands + return unpack_operands(self) + def operands(self): """ Return a list containing the operands of this expression. @@ -10267,7 +10294,7 @@ cdef get_dynamic_class_for_function(unsigned serial): ....: BuiltinFunction.__init__(self, 'tfunc', nargs=1) ....: ....: class EvaluationMethods: - ....: def argp1(self, x): + ....: def argp1(fn, self, x): ....: ''' ....: Some documentation about a bogus function. ....: ''' @@ -10312,7 +10339,7 @@ cdef get_dynamic_class_for_function(unsigned serial): ....: BuiltinFunction.__init__(self, 'tfunc', nargs=2) ....: ....: class EvaluationMethods: - ....: def argsum(self, x, y): + ....: def argsum(fn, self, x, y): ....: return x + y ....: sage: tfunc2 = TFunc2() @@ -10330,13 +10357,15 @@ cdef get_dynamic_class_for_function(unsigned serial): # callable methods need to be wrapped to extract the operands # and pass them as arguments from sage.symbolic.function_factory import eval_on_operands - from sage.structure.parent import getattr_from_other_class + from sage.structure.misc import getattr_from_other_class for name in dir(eval_methods): - m = getattr_from_other_class(func_class, eval_methods, name) + m = getattr(eval_methods(), name) if callable(m): - setattr(eval_methods, name, eval_on_operands(m)) + new_m = eval_on_operands(getattr_from_other_class( + func_class, eval_methods, name)) + setattr(eval_methods, name, new_m) cls = dynamic_class('Expression_with_dynamic_methods', - (eval_methods, Expression)) + (Expression,), eval_methods) else: cls = Expression diff --git a/src/sage/symbolic/function_factory.py b/src/sage/symbolic/function_factory.py index f01ceecb9c7..051ee25e23c 100644 --- a/src/sage/symbolic/function_factory.py +++ b/src/sage/symbolic/function_factory.py @@ -360,18 +360,18 @@ def new_evalf(*args, **kwds): def eval_on_operands(f): """ Given a method ``f`` return a new method which takes a single symbolic - expression argument and passes the operands of the given expression as - arguments to ``f``. + expression argument and appends operands of the given expression to + the arguments of ``f``. EXAMPLES:: - sage: def f(x, y): + sage: def f(ex, x, y): ....: ''' ....: Some documentation. ....: ''' ....: return x + 2*y ....: - sage: f(x, 1) + sage: f(None, x, 1) x + 2 sage: from sage.symbolic.function_factory import eval_on_operands sage: g = eval_on_operands(f) @@ -381,6 +381,8 @@ def eval_on_operands(f): 'Some documentation.' """ @sage_wraps(f) - def new_f(ex): - return f(*ex.operands()) + def new_f(ex, *args, **kwds): + new_args = list(ex._unpack_operands()) + new_args.extend(args) + return f(ex, *new_args, **kwds) return new_f diff --git a/src/sage/symbolic/pynac.pyx b/src/sage/symbolic/pynac.pyx index fb7955b66af..b1da2522fee 100644 --- a/src/sage/symbolic/pynac.pyx +++ b/src/sage/symbolic/pynac.pyx @@ -103,6 +103,26 @@ cdef public object exprseq_to_PyTuple(GEx seq): res.append(new_Expression_from_GEx(SR, seq.op(i))) return tuple(res) +def unpack_operands(Expression ex): + """ + EXAMPLES:: + + sage: from sage.symbolic.pynac import unpack_operands + sage: t = SR._force_pyobject((1, 2, x, x+1, x+2)) + sage: unpack_operands(t) + (1, 2, x, x + 1, x + 2) + sage: type(unpack_operands(t)) + + sage: map(type, unpack_operands(t)) + [, , , , ] + sage: u = SR._force_pyobject((t, x^2)) + sage: unpack_operands(u) + ((1, 2, x, x + 1, x + 2), x^2) + sage: type(unpack_operands(u)[0]) + + """ + return exprseq_to_PyTuple(ex._gobj) + cdef public object exvector_to_PyTuple(GExVector seq): """ Converts arguments list given to a function to a PyTuple. From 978f3138c31a430b2626e92e1fb1881ce23685ca Mon Sep 17 00:00:00 2001 From: Fredrik Johansson Date: Thu, 10 Apr 2014 14:07:57 +0200 Subject: [PATCH 039/546] Trac 2516: generalized hypergeometric functions --- src/doc/en/reference/functions/index.rst | 1 + src/sage/calculus/calculus.py | 12 +- src/sage/functions/all.py | 2 + src/sage/functions/hypergeometric.py | 840 ++++++++++++++++++++ src/sage/interfaces/maxima_lib.py | 29 +- src/sage/symbolic/expression.pyx | 63 ++ src/sage/symbolic/expression_conversions.py | 28 + 7 files changed, 960 insertions(+), 15 deletions(-) create mode 100644 src/sage/functions/hypergeometric.py diff --git a/src/doc/en/reference/functions/index.rst b/src/doc/en/reference/functions/index.rst index 58f6c467154..cf6c20419dd 100644 --- a/src/doc/en/reference/functions/index.rst +++ b/src/doc/en/reference/functions/index.rst @@ -13,6 +13,7 @@ Functions sage/functions/orthogonal_polys sage/functions/other sage/functions/special + sage/functions/hypergeometric sage/functions/bessel sage/functions/exp_integral sage/functions/wigner diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index bb6660aa127..005f516c0c3 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -1692,7 +1692,7 @@ def _inverse_laplace_latex_(self, *args): maxima_qp = re.compile("\?\%[a-z|A-Z|0-9|_]*") # e.g., ?%jacobi_cd -maxima_var = re.compile("\%[a-z|A-Z|0-9|_]*") # e.g., ?%jacobi_cd +maxima_var = re.compile("\%[a-z|A-Z|0-9|_]*") # e.g., %jacobi_cd sci_not = re.compile("(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]\d+)") @@ -1700,6 +1700,8 @@ def _inverse_laplace_latex_(self, *args): maxima_polygamma = re.compile("psi\[(\d*)\]\(") # matches psi[n]( where n is a number +maxima_hyper = re.compile("\%f\[\d+,\d+\]") # matches %f[m,n] + def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): """ Given a string representation of a Maxima expression, parse it and @@ -1785,15 +1787,17 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): pass else: syms[X[2:]] = function_factory(X[2:]) - s = s.replace("?%","") + s = s.replace("?%", "") + + s = maxima_hyper.sub('hypergeometric', s) - s = polylog_ex.sub('polylog(\\1,',s) + s = polylog_ex.sub('polylog(\\1,', s) s = multiple_replace(symtable, s) s = s.replace("%","") s = s.replace("#","!=") # a lot of this code should be refactored somewhere... - s = maxima_polygamma.sub('psi(\g<1>,',s) # this replaces psi[n](foo) with psi(n,foo), ensuring that derivatives of the digamma function are parsed properly below + s = maxima_polygamma.sub('psi(\g<1>,', s) # this replaces psi[n](foo) with psi(n,foo), ensuring that derivatives of the digamma function are parsed properly below if equals_sub: s = s.replace('=','==') diff --git a/src/sage/functions/all.py b/src/sage/functions/all.py index 38de03e978d..39847202581 100644 --- a/src/sage/functions/all.py +++ b/src/sage/functions/all.py @@ -69,3 +69,5 @@ sin_integral, cos_integral, Si, Ci, sinh_integral, cosh_integral, Shi, Chi, exponential_integral_1, Ei) + +from hypergeometric import hypergeometric diff --git a/src/sage/functions/hypergeometric.py b/src/sage/functions/hypergeometric.py new file mode 100644 index 00000000000..5e09f1a4827 --- /dev/null +++ b/src/sage/functions/hypergeometric.py @@ -0,0 +1,840 @@ +r""" +Hypergeometric Functions + +This module implements manipulation of infinite hypergeometric series +represented in standard parametric form (as $\,_pF_q$ functions). + +AUTHORS: + +- Fredrik Johansson (2010): initial version + +- Eviatar Bach (2013): major changes + +EXAMPLES: + +Examples from :trac:`9908`:: + + sage: maxima('integrate(bessel_j(2, x), x)').sage() + 1/24*x^3*hypergeometric((3/2,), (5/2, 3), -1/4*x^2) + sage: sum(((2*I)^x/(x^3 + 1)*(1/4)^x), x, 0, oo) + hypergeometric((1, 1, -1/2*I*sqrt(3) - 1/2, 1/2*I*sqrt(3) - 1/2),... + (2, -1/2*I*sqrt(3) + 1/2, 1/2*I*sqrt(3) + 1/2), 1/2*I) + sage: sum((-1)^x/((2*x + 1)*factorial(2*x + 1)), x, 0, oo) + hypergeometric((1/2,), (3/2, 3/2), -1/4) + +Simplification (note that ``simplify_full`` does not yet call +``simplify_hypergeometric``):: + + sage: hypergeometric([-2], [], x).simplify_hypergeometric() + x^2 - 2*x + 1 + sage: hypergeometric([], [], x).simplify_hypergeometric() + e^x + sage: a = hypergeometric((hypergeometric((), (), x),), (), + ....: hypergeometric((), (), x)) + sage: a.simplify_hypergeometric() + 1/((-e^x + 1)^e^x) + sage: a.simplify_hypergeometric(algorithm='sage') + (-e^x + 1)^(-e^x) + +Equality testing:: + + sage: bool(hypergeometric([], [], x).derivative(x) == + ....: hypergeometric([], [], x)) # diff(e^x, x) == e^x + True + sage: bool(hypergeometric([], [], x) == hypergeometric([], [1], x)) + False + +Computing terms and series:: + + sage: z = var('z') + sage: hypergeometric([], [], z).series(z, 0) + Order(1) + sage: hypergeometric([], [], z).series(z, 1) + 1 + Order(z) + sage: hypergeometric([], [], z).series(z, 2) + 1 + 1*z + Order(z^2) + sage: hypergeometric([], [], z).series(z, 3) + 1 + 1*z + 1/2*z^2 + Order(z^3) + + sage: hypergeometric([-2], [], z).series(z, 3) + 1 + (-2)*z + 1*z^2 + sage: hypergeometric([-2], [], z).series(z, 6) + 1 + (-2)*z + 1*z^2 + sage: hypergeometric([-2], [], z).series(z, 6).is_terminating_series() + True + sage: hypergeometric([-2], [], z).series(z, 2) + 1 + (-2)*z + Order(z^2) + sage: hypergeometric([-2], [], z).series(z, 2).is_terminating_series() + False + + sage: hypergeometric([1], [], z).series(z, 6) + 1 + 1*z + 1*z^2 + 1*z^3 + 1*z^4 + 1*z^5 + Order(z^6) + sage: hypergeometric([], [1/2], -z^2/4).series(z, 11) + 1 + (-1/2)*z^2 + 1/24*z^4 + (-1/720)*z^6 + 1/40320*z^8 +... + (-1/3628800)*z^10 + Order(z^11) + + sage: hypergeometric([1], [5], x).series(x, 5) + 1 + 1/5*x + 1/30*x^2 + 1/210*x^3 + 1/1680*x^4 + Order(x^5) + + sage: sum(hypergeometric([1, 2], [3], 1/3).terms(6)).n() + 1.29788359788360 + sage: hypergeometric([1, 2], [3], 1/3).n() + 1.29837194594696 + sage: hypergeometric([], [], x).series(x, 20)(x=1).n() == e.n() + True + +Plotting:: + + sage: plot(hypergeometric([1, 1], [3, 3, 3], x), x, -30, 30) + sage: complex_plot(hypergeometric([x], [], 2), (-1, 1), (-1, 1)) + +Numeric evaluation:: + + sage: hypergeometric([1], [], 1/10).n() # geometric series + 1.11111111111111 + sage: hypergeometric([], [], 1).n() # e + 2.71828182845905 + sage: hypergeometric([], [], 3., hold=True) + hypergeometric((), (), 3.00000000000000) + sage: hypergeometric([1, 2, 3], [4, 5, 6], 1/2).n() + 1.02573619590134 + sage: hypergeometric([1, 2, 3], [4, 5, 6], 1/2).n(digits=30) + 1.02573619590133865036584139535 + sage: hypergeometric([5 - 3*I], [3/2, 2 + I, sqrt(2)], 4 + I).n() + 5.52605111678805 - 7.86331357527544*I + sage: hypergeometric((10, 10), (50,), 2.) + -1705.75733163554 - 356.749986056024*I + +Conversions:: + + sage: maxima(hypergeometric([1, 1, 1], [3, 3, 3], x)) + hypergeometric([1,1,1],[3,3,3],x) + sage: hypergeometric((5, 4), (4, 4), 3)._sympy_() + hyper((5, 4), (4, 4), 3) + sage: hypergeometric((5, 4), (4, 4), 3)._mathematica_init_() + 'HypergeometricPFQ[{5,4},{4,4},3]' + +Arbitrary level of nesting for conversions:: + + sage: maxima(nest(lambda y: hypergeometric([y], [], x), 3, 1)) + 1/(1-x)^(1/(1-x)^(1/(1-x))) + sage: maxima(nest(lambda y: hypergeometric([y], [3], x), 3, 1))._sage_() + hypergeometric((hypergeometric((hypergeometric((1,), (3,), x),), (3,),... + x),), (3,), x) + sage: nest(lambda y: hypergeometric([y], [], x), 3, 1)._mathematica_init_() + 'HypergeometricPFQ[{HypergeometricPFQ[{HypergeometricPFQ[{1},{},x]},... +""" +#***************************************************************************** +# Copyright (C) 2010 Fredrik Johansson +# Copyright (C) 2013 Eviatar Bach +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.rings.all import (Integer, ZZ, QQ, Infinity, binomial, + rising_factorial, factorial) +from sage.functions.other import sqrt, gamma, real_part +from sage.functions.log import exp, log +from sage.functions.trig import cos, sin +from sage.functions.hyperbolic import cosh, sinh +from sage.functions.other import erf +from sage.symbolic.constants import pi +from sage.symbolic.all import I +from sage.symbolic.function import BuiltinFunction, is_inexact +from sage.symbolic.ring import SR +from sage.structure.element import get_coercion_model +from sage.misc.latex import latex +from sage.misc.misc_c import prod +from sage.libs.mpmath import utils as mpmath_utils +from sage.symbolic.expression import Expression +from sage.calculus.functional import derivative + + +def rational_param_as_tuple(x): + """ + Utility function for converting rational pFq parameters to + tuples (which mpmath handles more efficiently). + + EXAMPLES:: + + sage: from sage.functions.hypergeometric import rational_param_as_tuple + sage: rational_param_as_tuple(1/2) + (1, 2) + sage: rational_param_as_tuple(3) + 3 + sage: rational_param_as_tuple(pi) + pi + + """ + try: + x = x.pyobject() + except AttributeError: + pass + try: + if x.parent() is QQ: + p = int(x.numer()) + q = int(x.denom()) + return p, q + except AttributeError: + pass + return x + + +class Hypergeometric(BuiltinFunction): + r""" + Represents a (formal) generalized infinite hypergeometric series. It is + defined as `\,{}_pF_q(a_1,\ldots,a_p;b_1,\ldots,b_q;z) = \sum_{n=0}^\infty + \frac{(a_1)_n\dots(a_p)_n}{(b_1)_n\dots(b_q)_n} \, \frac{z^n}{n!},` + + where `(x)_n` is the rising factorial. + + INPUT: + + - ``a`` -- a list or tuple of parameters + - ``b`` -- a list or tuple of parameters + - ``z`` -- a number or symbolic expression + + EXAMPLES:: + + sage: hypergeometric([], [], 1) + hypergeometric((), (), 1) + sage: hypergeometric([], [1], 1) + hypergeometric((), (1,), 1) + sage: hypergeometric([2, 3], [1], 1) + hypergeometric((2, 3), (1,), 1) + sage: hypergeometric([], [], x) + hypergeometric((), (), x) + sage: hypergeometric([x], [], x^2) + hypergeometric((x,), (), x^2) + + The only simplification that is done automatically is returning 1 if ``z`` + is 0:: + + sage: hypergeometric([], [], 0) + 1 + + For other simplifications use the ``simplify_hypergeometric`` method. + """ + def __init__(self): + BuiltinFunction.__init__(self, 'hypergeometric', nargs=3, + conversions={'mathematica': + 'HypergeometricPFQ', + 'maxima': 'hypergeometric', + 'sympy': 'hyper'}) + + def __call__(self, a, b, z, **kwargs): + return BuiltinFunction.__call__(self, + SR._force_pyobject(a), + SR._force_pyobject(b), + z, **kwargs) + + def _print_latex_(self, a, b, z): + r""" + TESTS:: + + sage: latex(hypergeometric([1, 1], [2], -1)) + \,_2F_1\left(\begin{matrix} 1,1 \\ 2 \end{matrix} ; -1 \right) + + """ + aa = ",".join(latex(c) for c in a) + bb = ",".join(latex(c) for c in b) + z = latex(z) + return (r"\,_{}F_{}\left(\begin{{matrix}} {} \\ {} \end{{matrix}} ; " + r"{} \right)").format(len(a), len(b), aa, bb, z) + + def _eval_(self, a, b, z, **kwargs): + coercion_model = get_coercion_model() + co = reduce(lambda x, y: coercion_model.canonical_coercion(x, y)[0], + a + b + (z,)) + if is_inexact(co) and not isinstance(co, Expression): + from sage.structure.coerce import parent + return self._evalf_(a, b, z, parent=parent(co)) + if not isinstance(z, Expression) and z == 0: # Expression is excluded + return Integer(1) # to avoid call to Maxima + return + + def _evalf_(self, a, b, z, parent): + """ + TESTS:: + + sage: hypergeometric([1, 1], [2], -1).n() + 0.693147180559945 + sage: hypergeometric([], [], RealField(100)(1)) + 2.7182818284590452353602874714 + + """ + from mpmath import hyper + aa = [rational_param_as_tuple(c) for c in a] + bb = [rational_param_as_tuple(c) for c in b] + return mpmath_utils.call(hyper, aa, bb, z, parent=parent) + + def _tderivative_(self, a, b, z, *args, **kwargs): + """ + EXAMPLES:: + + sage: hypergeometric([1/3, 2/3], [5], x^2).diff(x) + 4/45*x*hypergeometric((4/3, 5/3), (6,), x^2) + sage: hypergeometric([1, 2], [x], 2).diff(x) + Traceback (most recent call last): + ... + NotImplementedError: derivative of hypergeometric function with... + respect to parameters. Try calling .simplify_hypergeometric()... + first. + sage: hypergeometric([1/3, 2/3], [5], 2).diff(x) + 0 + """ + diff_param = kwargs['diff_param'] + if diff_param in hypergeometric(a, b, 1).variables(): # ignore z + raise NotImplementedError("derivative of hypergeometric function " + "with respect to parameters. Try calling" + " .simplify_hypergeometric() first.") + t = (reduce(lambda x, y: x * y, a, 1) * + reduce(lambda x, y: x / y, b, Integer(1))) + return (t * derivative(z, diff_param) * + hypergeometric([c + 1 for c in a], [c + 1 for c in b], z)) + + class EvaluationMethods: + def _fast_float_(cls, self, *args): + """ + Do not support the old ``fast_float`` + + OUTPUT: + + This method raises ``NotImplementedError``; use the newer + ``fast_callable`` implementation + + EXAMPLES:: + + sage: f = hypergeometric([], [], x) + sage: f._fast_float_() + Traceback (most recent call last): + ... + NotImplementedError + """ + raise NotImplementedError + + def _fast_callable_(cls, self, a, b, z, etb): + """ + Override the ``fast_callable`` method + + OUTPUT: + + A :class:`~sage.ext.fast_callable.ExpressionCall` representing the + hypergeometric function in the expression tree + + EXAMPLES:: + + sage: h = hypergeometric([], [], x) + sage: from sage.ext.fast_callable import ExpressionTreeBuilder + sage: etb = ExpressionTreeBuilder(vars=['x']) + sage: h._fast_callable_(etb) + {CommutativeRings.element_class}(v_0) + + sage: y = var('y') + sage: fast_callable(hypergeometric([y], [], x), + ....: vars=[x, y])(3, 4) + hypergeometric((4,), (), 3) + """ + return etb.call(self, *map(etb.var, etb._vars)) + + def sorted_parameters(cls, self, a, b, z): + """ + Return with parameters sorted in a canonical order + + EXAMPLES:: + + sage: hypergeometric([2, 1, 3], [5, 4], + ....: 1/2).sorted_parameters() + hypergeometric((1, 2, 3), (4, 5), 1/2) + + """ + return hypergeometric(sorted(a), sorted(b), z) + + def eliminate_parameters(cls, self, a, b, z): + """ + Eliminate repeated parameters by pairwise cancellation of identical + terms in ``a`` and ``b`` + + EXAMPLES:: + + sage: hypergeometric([1, 1, 2, 5], [5, 1, 4], + ....: 1/2).eliminate_parameters() + hypergeometric((1, 2), (4,), 1/2) + sage: hypergeometric([x], [x], x).eliminate_parameters() + hypergeometric((), (), x) + sage: hypergeometric((5, 4), (4, 4), 3).eliminate_parameters() + hypergeometric((5,), (4,), 3) + + """ + aa = list(a) # tuples are immutable + bb = list(b) + p = pp = len(aa) + q = qq = len(bb) + i = 0 + while i < qq and aa: + bbb = bb[i] + if bbb in aa: + aa.remove(bbb) + bb.remove(bbb) + pp -= 1 + qq -= 1 + else: + i += 1 + if (pp, qq) != (p, q): + return hypergeometric(aa, bb, z) + return self + + def is_termwise_finite(cls, self, a, b, z): + """ + Determine whether all terms of self are finite. Any infinite + terms or ambiguous terms beyond the first zero, if one exists, + are ignored. + + Ambiguous cases (where a term is the product of both zero + and an infinity) are not considered finite. + + EXAMPLES:: + + sage: hypergeometric([2], [3, 4], 5).is_termwise_finite() + True + sage: hypergeometric([2], [-3, 4], 5).is_termwise_finite() + False + sage: hypergeometric([-2], [-3, 4], 5).is_termwise_finite() + True + sage: hypergeometric([-3], [-3, 4], + ....: 5).is_termwise_finite() # ambiguous + False + + sage: hypergeometric([0], [-1], 5).is_termwise_finite() + True + sage: hypergeometric([0], [0], + ....: 5).is_termwise_finite() # ambiguous + False + sage: hypergeometric([1], [2], Infinity).is_termwise_finite() + False + sage: (hypergeometric([0], [0], Infinity) + ....: .is_termwise_finite()) # ambiguous + False + sage: (hypergeometric([0], [], Infinity) + ....: .is_termwise_finite()) # ambiguous + False + + """ + if z == 0: + return 0 not in b + if abs(z) == Infinity: + return False + if abs(z) == Infinity: + return False + for bb in b: + if bb in ZZ and bb <= 0: + if any((aa in ZZ) and (bb < aa <= 0) for aa in a): + continue + return False + return True + + def is_terminating(cls, self, a, b, z): + """ + Determine whether the series represented by self terminates + after a finite number of terms, i.e. whether any of the + numerator parameters are nonnegative integers (with no + preceding nonnegative denominator parameters), or z = 0. + + If terminating, the series represents a polynomial of z + + EXAMPLES:: + + sage: hypergeometric([1, 2], [3, 4], x).is_terminating() + False + sage: hypergeometric([1, -2], [3, 4], x).is_terminating() + True + sage: hypergeometric([1, -2], [], x).is_terminating() + True + + """ + if z == 0: + return True + for aa in a: + if (aa in ZZ) and (aa <= 0): + return self.is_termwise_finite() + return False + + def is_absolutely_convergent(cls, self, a, b, z): + """ + Determine whether self converges absolutely as an infinite series. + False is returned if not all terms are finite. + + EXAMPLES: + + Degree giving infinite radius of convergence:: + + sage: hypergeometric([2, 3], [4, 5], + ....: 6).is_absolutely_convergent() + True + sage: hypergeometric([2, 3], [-4, 5], + ....: 6).is_absolutely_convergent() # undefined + False + sage: (hypergeometric([2, 3], [-4, 5], Infinity) + ....: .is_absolutely_convergent()) # undefined + False + + Ordinary geometric series (unit radius of convergence):: + + sage: hypergeometric([1], [], 1/2).is_absolutely_convergent() + True + sage: hypergeometric([1], [], 2).is_absolutely_convergent() + False + sage: hypergeometric([1], [], 1).is_absolutely_convergent() + False + sage: hypergeometric([1], [], -1).is_absolutely_convergent() + False + sage: hypergeometric([1], [], -1).n() # Sum still exists + 0.500000000000000 + + Degree $p = q+1$ (unit radius of convergence):: + + sage: hypergeometric([2, 3], [4], 6).is_absolutely_convergent() + False + sage: hypergeometric([2, 3], [4], 1).is_absolutely_convergent() + False + sage: hypergeometric([2, 3], [5], 1).is_absolutely_convergent() + False + sage: hypergeometric([2, 3], [6], 1).is_absolutely_convergent() + True + sage: hypergeometric([-2, 3], [4], + ....: 5).is_absolutely_convergent() + True + sage: hypergeometric([2, -3], [4], + ....: 5).is_absolutely_convergent() + True + sage: hypergeometric([2, -3], [-4], + ....: 5).is_absolutely_convergent() + True + sage: hypergeometric([2, -3], [-1], + ....: 5).is_absolutely_convergent() + False + + Degree giving zero radius of convergence:: + + sage: hypergeometric([1, 2, 3], [4], + ....: 2).is_absolutely_convergent() + False + sage: hypergeometric([1, 2, 3], [4], + ....: 1/2).is_absolutely_convergent() + False + sage: (hypergeometric([1, 2, -3], [4], 1/2) + ....: .is_absolutely_convergent()) # polynomial + True + + """ + p, q = len(a), len(b) + if not self.is_termwise_finite(): + return False + if p <= q: + return True + if self.is_terminating(): + return True + if p == q + 1: + if abs(z) < 1: + return True + if abs(z) == 1: + if real_part(sum(b) - sum(a)) > 0: + return True + return False + + def terms(cls, self, a, b, z, n=None): + """ + Generate the terms of self (optionally only n terms). + + EXAMPLES:: + + sage: list(hypergeometric([-2, 1], [3, 4], x).terms()) + [1, -1/6*x, 1/120*x^2] + sage: list(hypergeometric([-2, 1], [3, 4], x).terms(2)) + [1, -1/6*x] + sage: list(hypergeometric([-2, 1], [3, 4], x).terms(0)) + [] + + """ + if n is None: + n = Infinity + t = Integer(1) + k = 1 + while k <= n: + yield t + for aa in a: + t *= (aa + k - 1) + for bb in b: + t /= (bb + k - 1) + t *= z + if t == 0: + break + t /= k + k += 1 + + def deflated(cls, self, a, b, z): + """ + Rewrite as a linear combination of functions of strictly lower + degree by eliminating all parameters ``a[i]`` and ``b[j]`` such + that ``a[i]`` = ``b[i]`` + ``m`` for nonnegative integer ``m``. + + EXAMPLES:: + + sage: x = hypergeometric([5], [4], 3) + sage: y = x.deflated() + sage: y + 7/4*hypergeometric((), (), 3) + sage: x.n(); y.n() + 35.1496896155784 + 35.1496896155784 + + sage: x = hypergeometric([6, 1], [3, 4, 5], 10) + sage: y = x.deflated() + sage: y + hypergeometric((1,), (4, 5), 10) +... + 1/2*hypergeometric((2,), (5, 6), 10) +... + 1/12*hypergeometric((3,), (6, 7), 10) +... + 1/252*hypergeometric((4,), (7, 8), 10) + sage: x.n(); y.n() + 2.87893612686782 + 2.87893612686782 + + sage: x = hypergeometric([6, 7], [3, 4, 5], 10) + sage: y = x.deflated() + sage: y + hypergeometric((), (5,), 10) +... + 5*hypergeometric((), (6,), 10) +... + 19/3*hypergeometric((), (7,), 10) +... + 181/63*hypergeometric((), (8,), 10) +... + 265/504*hypergeometric((), (9,), 10) +... + 25/648*hypergeometric((), (10,), 10) +... + 25/27216*hypergeometric((), (11,), 10) + sage: x.n(); y.n() + 63.0734110716969 + 63.0734110716969 + + """ + return sum(map(prod, self._deflated())) + + def _deflated(cls, self, a, b, z): + new = self.eliminate_parameters() + aa = new.operands()[0].operands() + bb = new.operands()[1].operands() + for i, aaa in enumerate(aa): + for j, bbb in enumerate(bb): + m = aaa - bbb + if m in ZZ and m > 0: + aaaa = aa[:i] + aa[i + 1:] + bbbb = bb[:j] + bb[j + 1:] + terms = [] + for k in xrange(m + 1): + # TODO: could rewrite prefactors as recurrence + term = binomial(m, k) + for c in aaaa: + term *= rising_factorial(c, k) + for c in bbbb: + term /= rising_factorial(c, k) + term *= z ** k + term /= rising_factorial(aaa - m, k) + F = hypergeometric([c + k for c in aaaa], + [c + k for c in bbbb], z) + unique = [] + counts = [] + for c, f in F._deflated(): + if f in unique: + counts[unique.index(f)] += c + else: + unique.append(f) + counts.append(c) + Fterms = zip(counts, unique) + terms += [(term * termG, G) for (termG, G) in + Fterms] + return terms + return ((1, new),) + +hypergeometric = Hypergeometric() + + +def closed_form(hyp): + """ + Try to evaluate self in closed form using elementary + (and other simple) functions. + + It may be necessary to call :meth:`deflated` first to + find some closed forms. + + EXAMPLES:: + + sage: from sage.functions.hypergeometric import closed_form + sage: var('a b c z') + (a, b, c, z) + sage: closed_form(hypergeometric([1], [], 1 + z)) + -1/z + sage: closed_form(hypergeometric([], [], 1 + z)) + e^(z + 1) + sage: closed_form(hypergeometric([], [1/2], 4)) + cosh(4) + sage: closed_form(hypergeometric([], [3/2], 4)) + 1/4*sinh(4) + sage: closed_form(hypergeometric([], [5/2], 4)) + -3/64*sinh(4) + 3/16*cosh(4) + sage: closed_form(hypergeometric([], [-3/2], 4)) + -4*sinh(4) + 19/3*cosh(4) + sage: closed_form(hypergeometric([-3, 1], [var('a')], z)) + -6*z^3/((a + 1)*(a + 2)*a) + 6*z^2/((a + 1)*a) - 3*z/a + 1 + sage: closed_form(hypergeometric([-3, 1/3], [-4], z)) + 7/162*z^3 + 1/9*z^2 + 1/4*z + 1 + sage: closed_form(hypergeometric([], [], z)) + e^z + sage: closed_form(hypergeometric([a], [], z)) + (-z + 1)^(-a) + sage: closed_form(hypergeometric([1, 1, 2], [1, 1], z)) + (z - 1)^(-2) + sage: closed_form(hypergeometric([2, 3], [1], x)) + -1/(x - 1)^3 + 3*x/(x - 1)^4 + sage: closed_form(hypergeometric([1/2], [3/2], -5)) + 1/10*sqrt(pi)*sqrt(5)*erf(sqrt(5)) + sage: closed_form(hypergeometric([2], [5], 3)) + 4 + sage: closed_form(hypergeometric([2], [5], 5)) + 48/625*e^5 + 612/625 + sage: closed_form(hypergeometric([1/2, 7/2], [3/2], z)) + 1/sqrt(-z + 1) + 2/3*z/(-z + 1)^(3/2) + 1/5*z^2/(-z + 1)^(5/2) + sage: closed_form(hypergeometric([1/2, 1], [2], z)) + -2*(sqrt(-z + 1) - 1)/z + sage: closed_form(hypergeometric([1, 1], [2], z)) + -log(-z + 1)/z + sage: closed_form(hypergeometric([1, 1], [3], z)) + -2*((z - 1)*log(-z + 1)/z - 1)/z + sage: closed_form(hypergeometric([1, 1, 1], [2, 2], x)) + hypergeometric((1, 1, 1), (2, 2), x) + """ + if hyp.is_terminating(): + return sum(hyp.terms()) + + new = hyp.eliminate_parameters() + + def _closed_form(hyp): + a, b, z = hyp.operands() + a, b = a.operands(), b.operands() + p, q = len(a), len(b) + + if z == 0: + return Integer(1) + if p == q == 0: + return exp(z) + if p == 1 and q == 0: + return (1 - z) ** (-a[0]) + + if p == 0 and q == 1: + # TODO: make this require only linear time + def _0f1(b, z): + F12 = cosh(2 * sqrt(z)) + F32 = sinh(2 * sqrt(z)) / (2 * sqrt(z)) + if 2 * b == 1: + return F12 + if 2 * b == 3: + return F32 + if 2 * b > 3: + return ((b - 2) * (b - 1) / z * (_0f1(b - 2, z) - + _0f1(b - 1, z))) + if 2 * b < 1: + return (_0f1(b + 1, z) + z / (b * (b + 1)) * + _0f1(b + 2, z)) + raise ValueError + # Can evaluate 0F1 in terms of elementary functions when + # the parameter is a half-integer + if 2 * b[0] in ZZ and b[0] not in ZZ: + return _0f1(b[0], z) + + # Confluent hypergeometric function + if p == 1 and q == 1: + aa, bb = a[0], b[0] + if aa * 2 == 1 and bb * 2 == 3: + t = sqrt(-z) + return sqrt(pi) / 2 * erf(t) / t + if a == 1 and b == 2: + return (exp(z) - 1) / z + n, m = aa, bb + if n in ZZ and m in ZZ and m > 0 and n > 0: + rf = rising_factorial + if m <= n: + return (exp(z) * sum(rf(m - n, k) * (-z) ** k / + factorial(k) / rf(m, k) for k in + xrange(n - m + 1))) + else: + T = sum(rf(n - m + 1, k) * z ** k / + (factorial(k) * rf(2 - m, k)) for k in + xrange(m - n)) + U = sum(rf(1 - n, k) * (-z) ** k / + (factorial(k) * rf(2 - m, k)) for k in + xrange(n)) + return (factorial(m - 2) * rf(1 - m, n) * + z ** (1 - m) / factorial(n - 1) * + (T - exp(z) * U)) + + if p == 2 and q == 1: + R12 = QQ('1/2') + R32 = QQ('3/2') + + def _2f1(a, b, c, z): + """ + Evaluation of 2F1(a, b, c, z), assuming a, b, c positive + integers or half-integers + """ + if b == c: + return (1 - z) ** (-a) + if a == c: + return (1 - z) ** (-b) + if a == 0 or b == 0: + return Integer(1) + if a > b: + a, b = b, a + if b >= 2: + F1 = _2f1(a, b - 1, c, z) + F2 = _2f1(a, b - 2, c, z) + q = (b - 1) * (z - 1) + return (((c - 2 * b + 2 + (b - a - 1) * z) * F1 + + (b - c - 1) * F2) / q) + if c > 2: + # how to handle this case? + if a - c + 1 == 0 or b - c + 1 == 0: + raise NotImplementedError + F1 = _2f1(a, b, c - 1, z) + F2 = _2f1(a, b, c - 2, z) + r1 = (c - 1) * (2 - c - (a + b - 2 * c + 3) * z) + r2 = (c - 1) * (c - 2) * (1 - z) + q = (a - c + 1) * (b - c + 1) * z + return (r1 * F1 + r2 * F2) / q + + if (a, b, c) == (R12, 1, 2): + return (2 - 2 * sqrt(1 - z)) / z + if (a, b, c) == (1, 1, 2): + return -log(1 - z) / z + if (a, b, c) == (1, R32, R12): + return (1 + z) / (1 - z) ** 2 + if (a, b, c) == (1, R32, 2): + return 2 * (1 / sqrt(1 - z) - 1) / z + if (a, b, c) == (R32, 2, R12): + return (1 + 3 * z) / (1 - z) ** 3 + if (a, b, c) == (R32, 2, 1): + return (2 + z) / (2 * (sqrt(1 - z) * (1 - z) ** 2)) + if (a, b, c) == (2, 2, 1): + return (1 + z) / (1 - z) ** 3 + raise NotImplementedError + aa, bb = a + cc, = b + if z == 1: + return (gamma(cc) * gamma(cc - aa - bb) / gamma(cc - aa) / + gamma(cc - bb)) + if ((aa * 2) in ZZ and (bb * 2) in ZZ and (cc * 2) in ZZ and + aa > 0 and bb > 0 and cc > 0): + try: + return _2f1(aa, bb, cc, z) + except NotImplementedError: + pass + return hyp + return sum([coeff * _closed_form(pfq) for coeff, pfq in new._deflated()]) diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 7bf09dbae56..b1180bd37bf 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -1159,14 +1159,15 @@ def sage_rat(x,y): ## Here we build dictionaries for operators needing special conversions. -ratdisrep=EclObject("ratdisrep") -mrat=EclObject("MRAT") -mqapply=EclObject("MQAPPLY") -max_li=EclObject("$LI") -max_psi=EclObject("$PSI") -max_array=EclObject("ARRAY") -mdiff=EclObject("%DERIVATIVE") -max_lambert_w=sage_op_dict[sage.functions.log.lambert_w] +ratdisrep = EclObject("ratdisrep") +mrat = EclObject("MRAT") +mqapply = EclObject("MQAPPLY") +max_li = EclObject("$LI") +max_psi = EclObject("$PSI") +max_hyper = EclObject("$%F") +max_array = EclObject("ARRAY") +mdiff = EclObject("%DERIVATIVE") +max_lambert_w = sage_op_dict[sage.functions.log.lambert_w] def mrat_to_sage(expr): r""" @@ -1222,10 +1223,14 @@ def mqapply_to_sage(expr): """ if caaadr(expr) == max_li: return sage.functions.log.polylog(max_to_sr(cadadr(expr)), - max_to_sr(caddr(expr))) + max_to_sr(caddr(expr))) if caaadr(expr) == max_psi: return sage.functions.other.psi(max_to_sr(cadadr(expr)), - max_to_sr(caddr(expr))) + max_to_sr(caddr(expr))) + if caaadr(expr) == max_hyper: + return sage.functions.hypergeometric.hypergeometric(mlist_to_sage(car(cdr(cdr(expr)))), + mlist_to_sage(car(cdr(cdr(cdr(expr))))), + max_to_sr(car(cdr(cdr(cdr(cdr(expr))))))) else: op=max_to_sr(cadr(expr)) max_args=cddr(expr) @@ -1261,7 +1266,7 @@ def mlist_to_sage(expr): - ``expr`` - ECL object; a Maxima MLIST expression (i.e., a list) - OUTPUT: a python list of converted expressions. + OUTPUT: a Python list of converted expressions. EXAMPLES:: @@ -1480,6 +1485,8 @@ def sr_to_max(expr): return EclObject(l) elif (op in special_sage_to_max): return EclObject(special_sage_to_max[op](*[sr_to_max(o) for o in expr.operands()])) + elif op == tuple: + return maxima(expr.operands()).ecl() elif not (op in sage_op_dict): # Maxima does some simplifications automatically by default # so calling maxima(expr) can change the structure of expr diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 2c14141a065..f9fc1f851ad 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -8034,6 +8034,69 @@ cdef class Expression(CommutativeRingElement): full_simplify = simplify_full + def simplify_hypergeometric(self, algorithm='maxima'): + """ + Simplify an expression containing hypergeometric functions + + INPUT: + + - ``self`` -- symbolic expression + + - ``algorithm`` -- (default: ``'maxima'``) the algorithm to use for + for simplification. Implemented are ``'maxima'``, which uses Maxima's + ``hgfred`` function, and ``'sage'``, which uses an algorithm + implemented in the hypergeometric module + + ALIAS: :meth:`hypergeometric_simplify` and + :meth:`simplify_hypergeometric` are the same + + EXAMPLES:: + + sage: hypergeometric((5, 4), (4, 1, 2, 3), + ....: x).simplify_hypergeometric() + 1/144*x^2*hypergeometric((), (3, 4), x) +... + 1/3*x*hypergeometric((), (2, 3), x) + hypergeometric((), (1, 2), x) + sage: (2*hypergeometric((), (), x)).simplify_hypergeometric() + 2*e^x + sage: (nest(lambda y: hypergeometric([y], [1], x), 3, 1) + ....: .simplify_hypergeometric()) + laguerre(-laguerre(-e^x, x), x) + sage: (nest(lambda y: hypergeometric([y], [1], x), 3, 1) + ....: .simplify_hypergeometric(algorithm='sage')) + hypergeometric((hypergeometric((e^x,), (1,), x),), (1,), x) + + """ + from sage.functions.hypergeometric import hypergeometric, closed_form + from sage.calculus.calculus import maxima + try: + op = self.operator() + except RuntimeError: + return self + ops = self.operands() + if op == hypergeometric: + if algorithm == 'maxima': + return (self.parent() + (maxima.hgfred(map(lambda o: o.simplify_hypergeometric(algorithm), + ops[0].operands()), + map(lambda o: o.simplify_hypergeometric(algorithm), + ops[1].operands()), + ops[2].simplify_hypergeometric(algorithm)))) + elif algorithm == 'sage': + return (closed_form + (hypergeometric(map(lambda o: o.simplify_hypergeometric(algorithm), + ops[0].operands()), + map(lambda o: o.simplify_hypergeometric(algorithm), + ops[1].operands()), + ops[2].simplify_hypergeometric(algorithm)))) + else: + return NotImplementedError('unknown algorithm') + if not op: + return self + return op(*map(lambda o: o.simplify_hypergeometric(algorithm), ops)) + + hypergeometric_simplify = simplify_hypergeometric + + def simplify_rectform(self, complexity_measure = string_length): r""" Attempt to simplify this expression by expressing it in the diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index d4fa603c3d1..8a735b19ead 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -216,6 +216,8 @@ def __call__(self, ex=None): return self.relation(ex, operator) elif isinstance(operator, FDerivativeOperator): return self.derivative(ex, operator) + elif operator == tuple: + return self.tuple(ex) else: return self.composition(ex, operator) @@ -459,6 +461,20 @@ def relation(self, ex, operator): return "%s %s %s"%(self(ex.lhs()), self.relation_symbols[operator], self(ex.rhs())) + def tuple(self, ex): + """ + EXAMPLES:: + + sage: from sage.symbolic.expression_conversions import InterfaceInit + sage: m = InterfaceInit(maxima) + sage: t = SR._force_pyobject((3, 4, e^x)) + sage: m.tuple(t) + '[3,4,exp(x)]' + """ + x = map(self, ex.operands()) + X = ','.join(x) + return '%s%s%s'%(self.interface._left_list_delim(), X, self.interface._right_list_delim()) + def derivative(self, ex, operator): """ EXAMPLES:: @@ -1418,6 +1434,18 @@ def composition(self, ex, function): """ return self.etb.call(function, *ex.operands()) + def tuple(self, ex): + r""" + Given a symbolic tuple, return its elements as a Python list + + EXAMPLES:: + + sage: from sage.ext.fast_callable import ExpressionTreeBuilder + sage: etb = ExpressionTreeBuilder(vars=['x']) + sage: SR._force_pyobject((2, 3, x^2))._fast_callable_(etb) + [2, 3, x^2] + """ + return ex.operands() def fast_callable(ex, etb): """ From 9553ba95b5a901428f1b191bb92232038b68c50e Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Thu, 10 Apr 2014 14:41:12 +0200 Subject: [PATCH 040/546] Trac 2516: fix some doctests, typos --- src/sage/functions/bessel.py | 8 ++------ src/sage/functions/hypergeometric.py | 23 +++++++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/sage/functions/bessel.py b/src/sage/functions/bessel.py index fc51800c48a..74c67d1e0ad 100644 --- a/src/sage/functions/bessel.py +++ b/src/sage/functions/bessel.py @@ -260,15 +260,11 @@ class Function_Bessel_J(BuiltinFunction): sage: bessel_J(1.0, 1.0) - A[0] < 1e-15 True - Currently, integration is not supported (directly) since we cannot - yet convert hypergeometric functions to and from Maxima:: + Integration is supported directly and through Maxima:: sage: f = bessel_J(2, x) sage: f.integrate(x) - Traceback (most recent call last): - ... - TypeError: cannot coerce arguments: no canonical coercion from to Symbolic Ring - + 1/24*x^3*hypergeometric((3/2,), (5/2, 3), -1/4*x^2) sage: m = maxima(bessel_J(2, x)) sage: m.integrate(x) hypergeometric([3/2],[5/2,3],-x^2/4)*x^3/24 diff --git a/src/sage/functions/hypergeometric.py b/src/sage/functions/hypergeometric.py index 5e09f1a4827..b527a7cc52b 100644 --- a/src/sage/functions/hypergeometric.py +++ b/src/sage/functions/hypergeometric.py @@ -134,8 +134,11 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.rings.all import (Integer, ZZ, QQ, Infinity, binomial, - rising_factorial, factorial) +from sage.rings.integer import Integer +from sage.rings.integer_ring import ZZ +from sage.rings.rational_field import QQ +from sage.rings.infinity import Infinity +from sage.rings.arith import (binomial, rising_factorial, factorial) from sage.functions.other import sqrt, gamma, real_part from sage.functions.log import exp, log from sage.functions.trig import cos, sin @@ -342,7 +345,7 @@ def _fast_callable_(cls, self, a, b, z, etb): def sorted_parameters(cls, self, a, b, z): """ - Return with parameters sorted in a canonical order + Return with parameters sorted in a canonical order. EXAMPLES:: @@ -356,7 +359,7 @@ def sorted_parameters(cls, self, a, b, z): def eliminate_parameters(cls, self, a, b, z): """ Eliminate repeated parameters by pairwise cancellation of identical - terms in ``a`` and ``b`` + terms in ``a`` and ``b``. EXAMPLES:: @@ -443,7 +446,7 @@ def is_terminating(cls, self, a, b, z): numerator parameters are nonnegative integers (with no preceding nonnegative denominator parameters), or z = 0. - If terminating, the series represents a polynomial of z + If terminating, the series represents a polynomial of z. EXAMPLES:: @@ -680,11 +683,11 @@ def closed_form(hyp): sage: closed_form(hypergeometric([], [3/2], 4)) 1/4*sinh(4) sage: closed_form(hypergeometric([], [5/2], 4)) - -3/64*sinh(4) + 3/16*cosh(4) + 3/16*cosh(4) - 3/64*sinh(4) sage: closed_form(hypergeometric([], [-3/2], 4)) - -4*sinh(4) + 19/3*cosh(4) + 19/3*cosh(4) - 4*sinh(4) sage: closed_form(hypergeometric([-3, 1], [var('a')], z)) - -6*z^3/((a + 1)*(a + 2)*a) + 6*z^2/((a + 1)*a) - 3*z/a + 1 + -3*z/a + 6*z^2/((a + 1)*a) - 6*z^3/((a + 2)*(a + 1)*a) + 1 sage: closed_form(hypergeometric([-3, 1/3], [-4], z)) 7/162*z^3 + 1/9*z^2 + 1/4*z + 1 sage: closed_form(hypergeometric([], [], z)) @@ -696,13 +699,13 @@ def closed_form(hyp): sage: closed_form(hypergeometric([2, 3], [1], x)) -1/(x - 1)^3 + 3*x/(x - 1)^4 sage: closed_form(hypergeometric([1/2], [3/2], -5)) - 1/10*sqrt(pi)*sqrt(5)*erf(sqrt(5)) + 1/10*sqrt(5)*sqrt(pi)*erf(sqrt(5)) sage: closed_form(hypergeometric([2], [5], 3)) 4 sage: closed_form(hypergeometric([2], [5], 5)) 48/625*e^5 + 612/625 sage: closed_form(hypergeometric([1/2, 7/2], [3/2], z)) - 1/sqrt(-z + 1) + 2/3*z/(-z + 1)^(3/2) + 1/5*z^2/(-z + 1)^(5/2) + 1/5*z^2/(-z + 1)^(5/2) + 2/3*z/(-z + 1)^(3/2) + 1/sqrt(-z + 1) sage: closed_form(hypergeometric([1/2, 1], [2], z)) -2*(sqrt(-z + 1) - 1)/z sage: closed_form(hypergeometric([1, 1], [2], z)) From 9ce99c3a00d9bf92f9a866ec8714e5aea17cbe7e Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Fri, 11 Apr 2014 19:54:41 +0100 Subject: [PATCH 041/546] implement QuotientRingElement._im_gens_() --- src/sage/rings/quotient_ring_element.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sage/rings/quotient_ring_element.py b/src/sage/rings/quotient_ring_element.py index d94df2595bb..5e62b93d6de 100644 --- a/src/sage/rings/quotient_ring_element.py +++ b/src/sage/rings/quotient_ring_element.py @@ -419,6 +419,22 @@ def _div_(self, right): "a multiple of the denominator.") return P(XY[0]) + def _im_gens_(self, codomain, im_gens): + """ + TESTS: + + Ring homomorphisms whose domain is the fraction field of a + quotient ring work correctly (see :trac:`16135`):: + + sage: R. = QQ[] + sage: K = R.quotient(x^2 - y^3).fraction_field() + sage: L. = FunctionField(QQ) + sage: f = K.hom((t^3, t^2)) + sage: map(f, K.gens()) + [t^3, t^2] + + """ + return self.lift()._im_gens_(codomain, im_gens) def __int__(self): """ From 5a415fb8ced74dbb579f3e18fff0e2605778e299 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sat, 12 Apr 2014 17:29:42 +0200 Subject: [PATCH 042/546] Trac 2516: further doctest fixes --- src/sage/ext/fast_callable.pyx | 2 ++ src/sage/functions/hypergeometric.py | 15 +++------------ src/sage/symbolic/expression_conversions.py | 1 - src/sage/symbolic/ring.pyx | 8 +++++--- 4 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/sage/ext/fast_callable.pyx b/src/sage/ext/fast_callable.pyx index 582b2e9be49..299a5f2a60f 100644 --- a/src/sage/ext/fast_callable.pyx +++ b/src/sage/ext/fast_callable.pyx @@ -419,8 +419,10 @@ def fast_callable(x, domain=None, vars=None, from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing if is_PolynomialRing(x.parent()) or is_MPolynomialRing(x.parent()): vars = x.parent().variable_names() + etb = ExpressionTreeBuilder(vars=vars, domain=domain) et = x._fast_callable_(etb) + if isinstance(domain, RealField_class): import sage.ext.interpreters.wrapper_rr builder = sage.ext.interpreters.wrapper_rr.Wrapper_rr diff --git a/src/sage/functions/hypergeometric.py b/src/sage/functions/hypergeometric.py index b527a7cc52b..b0a527057dd 100644 --- a/src/sage/functions/hypergeometric.py +++ b/src/sage/functions/hypergeometric.py @@ -259,7 +259,7 @@ def _eval_(self, a, b, z, **kwargs): return Integer(1) # to avoid call to Maxima return - def _evalf_(self, a, b, z, parent): + def _evalf_(self, a, b, z, parent, algorithm=None): """ TESTS:: @@ -597,10 +597,7 @@ def deflated(cls, self, a, b, z): sage: x = hypergeometric([6, 1], [3, 4, 5], 10) sage: y = x.deflated() sage: y - hypergeometric((1,), (4, 5), 10) +... - 1/2*hypergeometric((2,), (5, 6), 10) +... - 1/12*hypergeometric((3,), (6, 7), 10) +... - 1/252*hypergeometric((4,), (7, 8), 10) + 1/252*hypergeometric((4,), (7, 8), 10) + 1/12*hypergeometric((3,), (6, 7), 10) + 1/2*hypergeometric((2,), (5, 6), 10) + hypergeometric((1,), (4, 5), 10) sage: x.n(); y.n() 2.87893612686782 2.87893612686782 @@ -608,13 +605,7 @@ def deflated(cls, self, a, b, z): sage: x = hypergeometric([6, 7], [3, 4, 5], 10) sage: y = x.deflated() sage: y - hypergeometric((), (5,), 10) +... - 5*hypergeometric((), (6,), 10) +... - 19/3*hypergeometric((), (7,), 10) +... - 181/63*hypergeometric((), (8,), 10) +... - 265/504*hypergeometric((), (9,), 10) +... - 25/648*hypergeometric((), (10,), 10) +... - 25/27216*hypergeometric((), (11,), 10) + 25/27216*hypergeometric((), (11,), 10) + 25/648*hypergeometric((), (10,), 10) + 265/504*hypergeometric((), (9,), 10) + 181/63*hypergeometric((), (8,), 10) + 19/3*hypergeometric((), (7,), 10) + 5*hypergeometric((), (6,), 10) + hypergeometric((), (5,), 10) sage: x.n(); y.n() 63.0734110716969 63.0734110716969 diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index 8a735b19ead..6c18af095ba 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -1135,7 +1135,6 @@ def __init__(self, ex, *vars): vars = ex.variables() if vars: - from sage.misc.superseded import deprecation deprecation(5930, "Substitution using function-call syntax and unnamed arguments is deprecated and will be removed from a future release of Sage; you can use named arguments instead, like EXPR(x=..., y=...)") diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index 9c914aa86e2..316e8e33cad 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -745,9 +745,11 @@ cdef class SymbolicRing(CommutativeRing): elif len(args) == 1 and isinstance(args[0], dict): d = args[0] else: - from sage.misc.superseded import deprecation - vars = _the_element.operands() - deprecation(5930, "Substitution using function-call syntax and unnamed arguments is deprecated and will be removed from a future release of Sage; you can use named arguments instead, like EXPR(x=..., y=...)") + import inspect + if not hasattr(_the_element,'_fast_callable_') or not inspect.ismethod(_the_element._fast_callable_): + # only warn if _the_element is not dynamic + from sage.misc.superseded import deprecation + deprecation(5930, "Substitution using function-call syntax and unnamed arguments is deprecated and will be removed from a future release of Sage; you can use named arguments instead, like EXPR(x=..., y=...)") d = {} vars = _the_element.variables() From ebe4c6a12c88cf6fdf0e7eb7b43bcfe05dcab8a4 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sun, 13 Apr 2014 16:31:43 +0200 Subject: [PATCH 043/546] 2516: fix doctests --- src/sage/functions/hypergeometric.py | 7 ++++++- src/sage/symbolic/expression_conversions.py | 1 + src/sage/symbolic/random_tests.py | 4 +++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/sage/functions/hypergeometric.py b/src/sage/functions/hypergeometric.py index b0a527057dd..f64d13fc570 100644 --- a/src/sage/functions/hypergeometric.py +++ b/src/sage/functions/hypergeometric.py @@ -249,6 +249,8 @@ def _print_latex_(self, a, b, z): r"{} \right)").format(len(a), len(b), aa, bb, z) def _eval_(self, a, b, z, **kwargs): + if not isinstance(a,tuple) or not isinstance(b,tuple): + raise ValueError('First two parameters must be of type list.') coercion_model = get_coercion_model() co = reduce(lambda x, y: coercion_model.canonical_coercion(x, y)[0], a + b + (z,)) @@ -269,6 +271,8 @@ def _evalf_(self, a, b, z, parent, algorithm=None): 2.7182818284590452353602874714 """ + if not isinstance(a,tuple) or not isinstance(b,tuple): + raise ValueError('First two parameters must be of type list.') from mpmath import hyper aa = [rational_param_as_tuple(c) for c in a] bb = [rational_param_as_tuple(c) for c in b] @@ -334,13 +338,14 @@ def _fast_callable_(cls, self, a, b, z, etb): sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=['x']) sage: h._fast_callable_(etb) - {CommutativeRings.element_class}(v_0) + {hypergeometric((), (), x)}(v_0) sage: y = var('y') sage: fast_callable(hypergeometric([y], [], x), ....: vars=[x, y])(3, 4) hypergeometric((4,), (), 3) """ + self.__name__ = self.__repr__() # was clobbered by category mechanics return etb.call(self, *map(etb.var, etb._vars)) def sorted_parameters(cls, self, a, b, z): diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index 6c18af095ba..8a735b19ead 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -1135,6 +1135,7 @@ def __init__(self, ex, *vars): vars = ex.variables() if vars: + from sage.misc.superseded import deprecation deprecation(5930, "Substitution using function-call syntax and unnamed arguments is deprecated and will be removed from a future release of Sage; you can use named arguments instead, like EXPR(x=..., y=...)") diff --git a/src/sage/symbolic/random_tests.py b/src/sage/symbolic/random_tests.py index 066c19e325c..9d1e9d53125 100644 --- a/src/sage/symbolic/random_tests.py +++ b/src/sage/symbolic/random_tests.py @@ -18,6 +18,7 @@ import sage.calculus.calculus import sage.symbolic.pynac from sage.symbolic.constants import * +from sage.functions.hypergeometric import hypergeometric ################################################################### @@ -51,7 +52,8 @@ def _mk_full_functions(): return [(1.0, f, f.number_of_arguments()) for (name, f) in items if hasattr(f, 'number_of_arguments') and - f.number_of_arguments() > 0] + f.number_of_arguments() > 0 and + f <> hypergeometric] # For creating simple expressions From bc1d66efab841c4670bc9e07f6b5c0e4a580e0dd Mon Sep 17 00:00:00 2001 From: Nils Bruin Date: Sun, 13 Apr 2014 23:48:37 -0700 Subject: [PATCH 044/546] trac 2516: fix maxima_lib to properly translate hypergeometric --- src/sage/interfaces/maxima_lib.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index b1180bd37bf..bca00fd2e4f 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -1045,6 +1045,7 @@ def reduce_load_MaximaLib(): cadadr=EclObject("cadadr") meval=EclObject("meval") NIL=EclObject("NIL") +lisp_length=EclObject("length") ## Dictionaries for standard operators sage_op_dict = { @@ -1363,7 +1364,8 @@ def dummy_integrate(expr): sage.functions.log.polylog : lambda N,X : [[mqapply],[[max_li, max_array],N],X], sage.functions.other.psi1 : lambda X : [[mqapply],[[max_psi, max_array],0],X], sage.functions.other.psi2 : lambda N,X : [[mqapply],[[max_psi, max_array],N],X], - sage.functions.log.lambert_w : lambda N,X : [[max_lambert_w], X] if N==EclObject(0) else [[mqapply],[[max_lambert_w, max_array],N],X] + sage.functions.log.lambert_w : lambda N,X : [[max_lambert_w], X] if N==EclObject(0) else [[mqapply],[[max_lambert_w, max_array],N],X], + sage.functions.hypergeometric.hypergeometric : lambda A, B, X : [[mqapply],[[max_hyper, max_array],lisp_length(A.cdr()),lisp_length(B.cdr())],A,B,X] } @@ -1486,7 +1488,7 @@ def sr_to_max(expr): elif (op in special_sage_to_max): return EclObject(special_sage_to_max[op](*[sr_to_max(o) for o in expr.operands()])) elif op == tuple: - return maxima(expr.operands()).ecl() + return EclObject( ([mlist],tuple(sr_to_max(op) for op in expr.operands())) ) elif not (op in sage_op_dict): # Maxima does some simplifications automatically by default # so calling maxima(expr) can change the structure of expr From dfcc9b05c5ba39e8b24ebb135c265d1814ff3910 Mon Sep 17 00:00:00 2001 From: Nils Bruin Date: Mon, 14 Apr 2014 18:31:11 -0700 Subject: [PATCH 045/546] fix "SR tuple" to "maxima list" conversion --- src/sage/interfaces/maxima_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index bca00fd2e4f..d30dc903d0a 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -1488,7 +1488,7 @@ def sr_to_max(expr): elif (op in special_sage_to_max): return EclObject(special_sage_to_max[op](*[sr_to_max(o) for o in expr.operands()])) elif op == tuple: - return EclObject( ([mlist],tuple(sr_to_max(op) for op in expr.operands())) ) + return EclObject( ([mlist],list(sr_to_max(op) for op in expr.operands())) ) elif not (op in sage_op_dict): # Maxima does some simplifications automatically by default # so calling maxima(expr) can change the structure of expr From 4a570d870ac2d06f9562b612ce6685e68a0b000d Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Tue, 15 Apr 2014 16:51:15 +0200 Subject: [PATCH 046/546] 8734: more additions of _SAGE_VAR_ in doctests --- src/doc/en/constructions/calculus.rst | 4 ++-- src/sage/functions/bessel.py | 6 +++--- src/sage/functions/orthogonal_polys.py | 10 +++++----- src/sage/functions/other.py | 8 ++++---- src/sage/interfaces/interface.py | 20 ++++++++++---------- src/sage/interfaces/maxima.py | 4 ++-- src/sage/interfaces/maxima_abstract.py | 12 ++++++------ src/sage/interfaces/maxima_lib.py | 12 ++++++------ src/sage/symbolic/expression_conversions.py | 4 ++-- 9 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/doc/en/constructions/calculus.rst b/src/doc/en/constructions/calculus.rst index dde6b4bd0dd..c18e10ebc37 100644 --- a/src/doc/en/constructions/calculus.rst +++ b/src/doc/en/constructions/calculus.rst @@ -100,7 +100,7 @@ Taylor series: sage: g.taylor(x, 0, 3) -62/945*f0*k^2*x^2 + 11/45*f0 - 2/3*f0/(k^2*x^2) + f0/(k^4*x^4) sage: maxima(g).powerseries('x',0) - 16*f0*('sum((2^(2*i1-1)-1)*bern(2*i1)*k^(2*i1-1)*x^(2*i1-1)/factorial(2*i1),i1,0,inf))^4 + 16*f0*('sum((2^(2*i1-1)-1)*bern(2*i1)*k^(2*i1-1)*_SAGE_VAR_x^(2*i1-1)/factorial(2*i1),i1,0,inf))^4 Of course, you can view the LaTeX-ed version of this using ``view(g.powerseries('x',0))``. @@ -116,7 +116,7 @@ The Maclaurin and power series of sage: [bernoulli(2*i) for i in range(1,7)] [1/6, -1/30, 1/42, -1/30, 5/66, -691/2730] sage: maxima(f).powerseries(x,0) - 'sum((-1)^i2*2^(2*i2-1)*bern(2*i2)*x^(2*i2)/(i2*factorial(2*i2)),i2,1,inf) + 'sum((-1)^i2*2^(2*i2-1)*bern(2*i2)*_SAGE_VAR_x^(2*i2)/(i2*factorial(2*i2)),i2,1,inf) .. index:: pair: calculus; integration diff --git a/src/sage/functions/bessel.py b/src/sage/functions/bessel.py index fe78e9e80d8..628286a7a25 100644 --- a/src/sage/functions/bessel.py +++ b/src/sage/functions/bessel.py @@ -271,7 +271,7 @@ class Function_Bessel_J(BuiltinFunction): sage: m = maxima(bessel_J(2, x)) sage: m.integrate(x) - hypergeometric([3/2],[5/2,3],-x^2/4)*x^3/24 + hypergeometric([3/2],[5/2,3],-_SAGE_VAR_x^2/4)*_SAGE_VAR_x^3/24 Visualization:: @@ -1005,9 +1005,9 @@ def Bessel(*args, **kwds): sage: x,y = var('x,y') sage: f = maxima(Bessel(typ='K')(x,y)) sage: f.derivative('x') - %pi*csc(%pi*x)*('diff(bessel_i(-x,y),x,1)-'diff(bessel_i(x,y),x,1))/2-%pi*bessel_k(x,y)*cot(%pi*x) + %pi*csc(%pi*_SAGE_VAR_x)*('diff(bessel_i(-_SAGE_VAR_x,_SAGE_VAR_y),_SAGE_VAR_x,1)-'diff(bessel_i(_SAGE_VAR_x,_SAGE_VAR_y),_SAGE_VAR_x,1))/2-%pi*bessel_k(_SAGE_VAR_x,_SAGE_VAR_y)*cot(%pi*_SAGE_VAR_x) sage: f.derivative('y') - -(bessel_k(x+1,y)+bessel_k(x-1,y))/2 + -(bessel_k(_SAGE_VAR_x+1,_SAGE_VAR_y)+bessel_k(_SAGE_VAR_x-1,_SAGE_VAR_y))/2 Compute the particular solution to Bessel's Differential Equation that satisfies `y(1) = 1` and `y'(1) = 1`, then verify the initial conditions diff --git a/src/sage/functions/orthogonal_polys.py b/src/sage/functions/orthogonal_polys.py index 7b5adcb1ba0..3c9a1e6daa7 100644 --- a/src/sage/functions/orthogonal_polys.py +++ b/src/sage/functions/orthogonal_polys.py @@ -721,9 +721,9 @@ def _maxima_init_evaled_(self, n, x): sage: var('n, x') (n, x) sage: chebyshev_T._maxima_init_evaled_(1,x) - 'x' + '_SAGE_VAR_x' sage: maxima(chebyshev_T(n, chebyshev_T(n, x))) - chebyshev_t(n,chebyshev_t(n,x)) + chebyshev_t(_SAGE_VAR_n,chebyshev_t(_SAGE_VAR_n,_SAGE_VAR_x)) """ return maxima.eval('chebyshev_t({0},{1})'.format(n._maxima_init_(), x._maxima_init_())) @@ -1030,11 +1030,11 @@ def _maxima_init_evaled_(self, n, x): sage: var('n, x') (n, x) sage: maxima(chebyshev_U(5,x)) - 32*x^5-32*x^3+6*x + 32*_SAGE_VAR_x^5-32*_SAGE_VAR_x^3+6*_SAGE_VAR_x sage: maxima(chebyshev_U(n,x)) - chebyshev_u(n,x) + chebyshev_u(_SAGE_VAR_n,_SAGE_VAR_x) sage: maxima(chebyshev_U(2,x)) - 4*x^2-1 + 4*_SAGE_VAR_x^2-1 """ return maxima.eval('chebyshev_u({0},{1})'.format(n._maxima_init_(), x._maxima_init_())) diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 53c36fede90..afbd491da4d 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -243,7 +243,7 @@ def _derivative_(self, x, diff_param=None): sage: derivative(erf(c*x),x) 2*c*e^(-c^2*x^2)/sqrt(pi) sage: erf(c*x).diff(x)._maxima_init_() - '((%pi)^(-1/2))*(c)*(exp(((c)^(2))*((x)^(2))*(-1)))*(2)' + '((%pi)^(-1/2))*(_SAGE_VAR_c)*(exp(((_SAGE_VAR_c)^(2))*((_SAGE_VAR_x)^(2))*(-1)))*(2)' """ return 2*exp(-x**2)/sqrt(pi) @@ -1286,7 +1286,7 @@ def __init__(self): sage: factorial._maxima_init_() 'factorial' sage: maxima(factorial(z)) - factorial(z) + factorial(_SAGE_VAR_z) sage: _.sage() factorial(z) sage: k = var('k') @@ -1438,7 +1438,7 @@ def __init__(self): sage: n,k = var('n,k') sage: maxima(binomial(n,k)) - binomial(n,k) + binomial(_SAGE_VAR_n,_SAGE_VAR_k) sage: _.sage() binomial(n, k) sage: binomial._maxima_init_() @@ -1806,7 +1806,7 @@ def __init__(self): sage: latex(arg(x)) {\rm arg}\left(x\right) sage: maxima(arg(x)) - atan2(0,x) + atan2(0,_SAGE_VAR_x) sage: maxima(arg(2+i)) atan(1/2) sage: maxima(arg(sqrt(2)+i)) diff --git a/src/sage/interfaces/interface.py b/src/sage/interfaces/interface.py index 16ae4d703f2..ad29e1c6065 100644 --- a/src/sage/interfaces/interface.py +++ b/src/sage/interfaces/interface.py @@ -1082,11 +1082,11 @@ def _add_(self, right): sage: f = maxima.cos(x) sage: g = maxima.sin(x) sage: f + g - sin(x)+cos(x) + sin(_SAGE_VAR_x)+cos(_SAGE_VAR_x) sage: f + 2 - cos(x)+2 + cos(_SAGE_VAR_x)+2 sage: 2 + f - cos(x)+2 + cos(_SAGE_VAR_x)+2 """ return self._operation("+", right) @@ -1097,11 +1097,11 @@ def _sub_(self, right): sage: f = maxima.cos(x) sage: g = maxima.sin(x) sage: f - g - cos(x)-sin(x) + cos(_SAGE_VAR_x)-sin(_SAGE_VAR_x) sage: f - 2 - cos(x)-2 + cos(_SAGE_VAR_x)-2 sage: 2 - f - 2-cos(x) + 2-cos(_SAGE_VAR_x) """ return self._operation('-', right) @@ -1112,9 +1112,9 @@ def _mul_(self, right): sage: f = maxima.cos(x) sage: g = maxima.sin(x) sage: f*g - cos(x)*sin(x) + cos(_SAGE_VAR_x)*sin(_SAGE_VAR_x) sage: 2*f - 2*cos(x) + 2*cos(_SAGE_VAR_x) """ return self._operation('*', right) @@ -1125,9 +1125,9 @@ def _div_(self, right): sage: f = maxima.cos(x) sage: g = maxima.sin(x) sage: f/g - cos(x)/sin(x) + cos(_SAGE_VAR_x)/sin(_SAGE_VAR_x) sage: f/2 - cos(x)/2 + cos(_SAGE_VAR_x)/2 """ return self._operation("/", right) diff --git a/src/sage/interfaces/maxima.py b/src/sage/interfaces/maxima.py index 7603881f6a7..b673d925744 100644 --- a/src/sage/interfaces/maxima.py +++ b/src/sage/interfaces/maxima.py @@ -421,7 +421,7 @@ gamma(1/7) sage: del f sage: maxima(sin(x)) - sin(x) + sin(_SAGE_VAR_x) This tests to make sure we handle the case where Maxima asks if an expression is positive or zero. @@ -1101,7 +1101,7 @@ class MaximaElement(MaximaAbstractElement, ExpectElement): sage: maxima(3) 3 sage: maxima(cos(x)+e^234) - cos(x)+%e^234 + cos(_SAGE_VAR_x)+%e^234 """ def __init__(self, parent, value, is_name=False, name=None): diff --git a/src/sage/interfaces/maxima_abstract.py b/src/sage/interfaces/maxima_abstract.py index cc147d4ab69..40b2f686ec0 100644 --- a/src/sage/interfaces/maxima_abstract.py +++ b/src/sage/interfaces/maxima_abstract.py @@ -496,7 +496,7 @@ def _equality_symbol(self): sage: var('x y') (x, y) sage: maxima(x == y) - x=y + _SAGE_VAR_x=_SAGE_VAR_y """ return '=' @@ -513,7 +513,7 @@ def _inequality_symbol(self): sage: maxima._inequality_symbol() '#' sage: maxima((x != 1)) - x#1 + _SAGE_VAR_x#1 """ return '#' @@ -1898,7 +1898,7 @@ def _operation(self, operation, right): sage: f = maxima.cos(x) sage: f._operation("+", f) - 2*cos(x) + 2*cos(_SAGE_VAR_x) """ P = self._check_valid() @@ -2188,16 +2188,16 @@ def _add_(self, f): sage: (f+maxima.cos(x))(2) sin(2)+cos(2) sage: (f+maxima.cos(y)) # This is a function with only ONE argument! - cos(y)+sin(x) + cos(_SAGE_VAR_y)+sin(x) sage: (f+maxima.cos(y))(2) - cos(y)+sin(2) + cos(_SAGE_VAR_y)+sin(2) :: sage: f = maxima.function('x','sin(x)') sage: g = -maxima.cos(x) sage: g+f - sin(x)-cos(x) + sin(x)-cos(_SAGE_VAR_x) sage: (g+f)(2) # The sum IS a function sin(2)-cos(2) sage: 2+f diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 3263d3b4b5d..626eeabf903 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -255,7 +255,7 @@ def max_to_string(s): sage: from sage.interfaces.maxima_lib import maxima_lib, max_to_string sage: ecl = maxima_lib(cos(x)).ecl() sage: max_to_string(ecl) - 'cos(x)' + 'cos(_SAGE_VAR_x)' """ return maxprint(s).python()[1:-1] @@ -561,7 +561,7 @@ def _create(self, value, name=None): sage: maxima_lib._create(c,'m') 'm' sage: maxima_lib.get('m') - 'x+cos(19)' + '_SAGE_VAR_x+cos(19)' """ name = self._next_var_name() if name is None else name if isinstance(value,EclObject): @@ -912,7 +912,7 @@ class MaximaLibElement(MaximaAbstractElement): sage: maxima_lib(4) 4 sage: maxima_lib(log(x)) - log(x) + log(_SAGE_VAR_x) """ def ecl(self): @@ -927,7 +927,7 @@ def ecl(self): sage: from sage.interfaces.maxima_lib import maxima_lib sage: maxima_lib(x+cos(19)).ecl() - + """ try: return self._ecl @@ -1198,9 +1198,9 @@ def mrat_to_sage(expr): (x, y, z) sage: c = maxima_lib((x+y^2+z^9)/x^6+z^8/y).rat() sage: c - (y*z^9+x^6*z^8+y^3+x*y)/(x^6*y) + (_SAGE_VAR_y*_SAGE_VAR_z^9+_SAGE_VAR_x^6*_SAGE_VAR_z^8+_SAGE_VAR_y^3+_SAGE_VAR_x*_SAGE_VAR_y)/(_SAGE_VAR_x^6*_SAGE_VAR_y) sage: c.ecl() - sage: mrat_to_sage(c.ecl()) (x^6*z^8 + y*z^9 + y^3 + x*y)/(x^6*y) diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index 0eb7de544a6..6f7a3d99f26 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -475,10 +475,10 @@ def derivative(self, ex, operator): sage: a = f(x).diff(x); a D[0](f)(x) sage: print m.derivative(a, a.operator()) - diff('f(x), x, 1) + diff('f(_SAGE_VAR_x), _SAGE_VAR_x, 1) sage: b = f(x).diff(x, x) sage: print m.derivative(b, b.operator()) - diff('f(x), x, 2) + diff('f(_SAGE_VAR_x), _SAGE_VAR_x, 2) We can also convert expressions where the argument is not just a variable, but the result is an "at" expression using temporary From be9367f966fc76ba6028b57ab4317530cfd1205e Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Tue, 15 Apr 2014 18:04:16 +0200 Subject: [PATCH 047/546] 8734: one more maxima call adapted; more doctests fixed --- src/sage/interfaces/maxima_lib.py | 44 +++++++++++------------ src/sage/tests/french_book/recequadiff.py | 2 +- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 626eeabf903..544f93e2be3 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -420,11 +420,13 @@ def _eval_line(self, line, locals=None, reformat=True, **kwds): else: statement = line[:ind_semi] line = line[ind_semi+1:] - if statement: result = ((result + '\n') if result else '') + max_to_string(maxima_eval("#$%s$"%statement)) + if statement: + result = ((result + '\n') if result else '') + max_to_string(maxima_eval("#$%s$"%statement)) else: statement = line[:ind_dollar] line = line[ind_dollar+1:] - if statement: _ = maxima_eval("#$%s$"%statement) + if statement: + _ = maxima_eval("#$%s$"%statement) if not reformat: return result return ''.join([x.strip() for x in result.split()]) @@ -564,10 +566,17 @@ def _create(self, value, name=None): '_SAGE_VAR_x+cos(19)' """ name = self._next_var_name() if name is None else name - if isinstance(value,EclObject): - maxima_eval([[msetq],cadadr("#$%s$#$"%name),value]) - else: - self.set(name, value) + try: + if isinstance(value,EclObject): + maxima_eval([[msetq],cadadr("#$%s$#$"%name),value]) + else: + self.set(name, value) + except RuntimeError as error: + s = str(error) + if "Is" in s: # Maxima asked for a condition + self.missing_assumption(s) + else: + raise error return name def _function_class(self): @@ -649,10 +658,7 @@ def sr_integral(self,*args): sage: integrate(1/(x^3 *(a+b*x)^(1/3)),x) Traceback (most recent call last): ... - ValueError: Computation failed since Maxima requested additional - constraints; using the 'assume' command before integral evaluation - *may* help (example of legal syntax is 'assume(a>0)', see - `assume?` for more details) + ValueError: Computation failed ... Is a positive or negative? sage: assume(a>0) sage: integrate(1/(x^3 *(a+b*x)^(1/3)),x) @@ -662,10 +668,7 @@ def sr_integral(self,*args): sage: integral(x^n,x) Traceback (most recent call last): ... - ValueError: Computation failed since Maxima requested additional - constraints; using the 'assume' command before integral evaluation - *may* help (example of legal syntax is 'assume(n+1>0)', - see `assume?` for more details) + ValueError: Computation failed ... Is n+1 zero or nonzero? sage: assume(n+1>0) sage: integral(x^n,x) @@ -756,10 +759,7 @@ def sr_sum(self,*args): sage: sum(a*q^k, k, 0, oo) Traceback (most recent call last): ... - ValueError: Computation failed since Maxima requested additional - constraints; using the 'assume' command before summation *may* help - (example of legal syntax is 'assume(abs(q)-1>0)', see `assume?` - for more details) + ValueError: Computation failed ... Is abs(q)-1 positive, negative, or zero? sage: assume(q > 1) sage: sum(a*q^k, k, 0, oo) @@ -806,17 +806,13 @@ def sr_limit(self,expr,v,a,dir=None): sage: limit(x^a,x=0) Traceback (most recent call last): ... - ValueError: Computation failed since Maxima requested additional - constraints; using the 'assume' command before limit evaluation - *may* help (see `assume?` for more details) + ValueError: Computation failed ... Is a positive, negative, or zero? sage: assume(a>0) sage: limit(x^a,x=0) Traceback (most recent call last): ... - ValueError: Computation failed since Maxima requested additional - constraints; using the 'assume' command before limit evaluation - *may* help (see `assume?` for more details) + ValueError: Computation failed ... Is a an integer? sage: assume(a,'integer') sage: assume(a,'even') # Yes, Maxima will ask this too diff --git a/src/sage/tests/french_book/recequadiff.py b/src/sage/tests/french_book/recequadiff.py index 924b2333985..de333a35ded 100755 --- a/src/sage/tests/french_book/recequadiff.py +++ b/src/sage/tests/french_book/recequadiff.py @@ -247,7 +247,7 @@ sage: desolve(eq1,[f,x]) Traceback (most recent call last): ... - TypeError: ECL says: Maxima asks: + TypeError: ... Is k positive, negative, or zero? Sage example in ./recequadiff.tex, line 728:: From 1e9b54bca59e9b69cd10a08a60255012a90c4f01 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Wed, 16 Apr 2014 15:47:55 +0200 Subject: [PATCH 048/546] 8734: more doctests fixed --- src/doc/en/constructions/calculus.rst | 6 +++--- src/sage/functions/bessel.py | 4 ++-- src/sage/interfaces/maxima_abstract.py | 9 +++++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/doc/en/constructions/calculus.rst b/src/doc/en/constructions/calculus.rst index c18e10ebc37..5af99b9056c 100644 --- a/src/doc/en/constructions/calculus.rst +++ b/src/doc/en/constructions/calculus.rst @@ -99,8 +99,8 @@ Taylor series: sage: g = f0/sinh(k*x)^4 sage: g.taylor(x, 0, 3) -62/945*f0*k^2*x^2 + 11/45*f0 - 2/3*f0/(k^2*x^2) + f0/(k^4*x^4) - sage: maxima(g).powerseries('x',0) - 16*f0*('sum((2^(2*i1-1)-1)*bern(2*i1)*k^(2*i1-1)*_SAGE_VAR_x^(2*i1-1)/factorial(2*i1),i1,0,inf))^4 + sage: maxima(g).powerseries('_SAGE_VAR_x',0) # TODO: write this without maxima + 16*_SAGE_VAR_f0*('sum((2^(2*i1-1)-1)*bern(2*i1)*_SAGE_VAR_k^(2*i1-1)*_SAGE_VAR_x^(2*i1-1)/factorial(2*i1),i1,0,inf))^4 Of course, you can view the LaTeX-ed version of this using ``view(g.powerseries('x',0))``. @@ -115,7 +115,7 @@ The Maclaurin and power series of -1/467775*x^10 - 1/37800*x^8 - 1/2835*x^6 - 1/180*x^4 - 1/6*x^2 sage: [bernoulli(2*i) for i in range(1,7)] [1/6, -1/30, 1/42, -1/30, 5/66, -691/2730] - sage: maxima(f).powerseries(x,0) + sage: maxima(f).powerseries(x,0) # TODO: write this without maxima 'sum((-1)^i2*2^(2*i2-1)*bern(2*i2)*_SAGE_VAR_x^(2*i2)/(i2*factorial(2*i2)),i2,1,inf) .. index:: diff --git a/src/sage/functions/bessel.py b/src/sage/functions/bessel.py index 628286a7a25..bd488421cdf 100644 --- a/src/sage/functions/bessel.py +++ b/src/sage/functions/bessel.py @@ -1004,9 +1004,9 @@ def Bessel(*args, **kwds): sage: x,y = var('x,y') sage: f = maxima(Bessel(typ='K')(x,y)) - sage: f.derivative('x') + sage: f.derivative('_SAGE_VAR_x') %pi*csc(%pi*_SAGE_VAR_x)*('diff(bessel_i(-_SAGE_VAR_x,_SAGE_VAR_y),_SAGE_VAR_x,1)-'diff(bessel_i(_SAGE_VAR_x,_SAGE_VAR_y),_SAGE_VAR_x,1))/2-%pi*bessel_k(_SAGE_VAR_x,_SAGE_VAR_y)*cot(%pi*_SAGE_VAR_x) - sage: f.derivative('y') + sage: f.derivative('_SAGE_VAR_y') -(bessel_k(_SAGE_VAR_x+1,_SAGE_VAR_y)+bessel_k(_SAGE_VAR_x-1,_SAGE_VAR_y))/2 Compute the particular solution to Bessel's Differential Equation that diff --git a/src/sage/interfaces/maxima_abstract.py b/src/sage/interfaces/maxima_abstract.py index 40b2f686ec0..d1e0461026a 100644 --- a/src/sage/interfaces/maxima_abstract.py +++ b/src/sage/interfaces/maxima_abstract.py @@ -1753,11 +1753,11 @@ def _latex_(self): sage: y,d = var('y,d') sage: f = function('f') sage: latex(maxima(derivative(f(x*y), x))) - \left(\left.{{{\it \partial}}\over{{\it \partial}\,{\it t_0}}}\,f \left({\it t_0}\right)\right|_{\left[ {\it t_0}=x\,y \right] } \right)\,y + \left(\left.{{{\it \partial}}\over{{\it \partial}\,{\it t_0}}}\,f \left({\it t_0}\right)\right|_{\left[ {\it t_0}=x\,y \right] } \right)\,{\it y} sage: latex(maxima(derivative(f(x,y,d), d,x,x,y))) - {{{\it \partial}^4}\over{{\it \partial}\,d\,{\it \partial}\,x^2\, {\it \partial}\,y}}\,f\left(x , y , d\right) + {{{\it \partial}^4}\over{{\it \partial}\,{\it d}\, {\it \partial}\,{\it x}^2\,{\it \partial}\, {\it y}}}\,f\left({\it x} , {\it y} , {\it d}\right) sage: latex(maxima(d/(d-2))) - {{d}\over{d-2}} + {{{\it d}}\over{{\it d}-2}} """ self._check_valid() P = self.parent() @@ -1771,7 +1771,8 @@ def _latex_(self): '\\%':'', '\\arcsin ':'\\sin^{-1} ', '\\arccos ':'\\cos^{-1} ', - '\\arctan ':'\\tan^{-1} '}, s) + '\\arctan ':'\\tan^{-1} ', + '\\_SAGE\\_VAR\\_':''}, s) # Fix a maxima bug, which gives a latex representation of multiplying # two numbers as a single space. This was really bad when 2*17^(1/3) From 0dbbacd5af23a111706b660ec997c3b302a33525 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Wed, 16 Apr 2014 16:47:35 +0200 Subject: [PATCH 049/546] 2516: fix last doctests --- src/sage/ext/fast_callable.pyx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/ext/fast_callable.pyx b/src/sage/ext/fast_callable.pyx index 299a5f2a60f..a741c16cb7a 100644 --- a/src/sage/ext/fast_callable.pyx +++ b/src/sage/ext/fast_callable.pyx @@ -482,6 +482,8 @@ def function_name(fn): sage: function_name(factorial) '{factorial}' """ + if isinstance(fn, Expression): + return "{%r}" % fn builtins = get_builtin_functions() if fn in builtins: return builtins[fn] From 39863465b9b87a83a21fbe9b6235ced1e51c2766 Mon Sep 17 00:00:00 2001 From: Andrey Novoseltsev Date: Wed, 16 Apr 2014 15:34:17 -0600 Subject: [PATCH 050/546] Change "field" to "base_ring" in toric_plotter. --- src/sage/geometry/toric_plotter.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/toric_plotter.py b/src/sage/geometry/toric_plotter.py index cd2863d84f1..301988bf27c 100644 --- a/src/sage/geometry/toric_plotter.py +++ b/src/sage/geometry/toric_plotter.py @@ -478,7 +478,6 @@ def plot_lattice(self): # Plot the origin anyway, otherwise rays/generators may look ugly. return self.plot_points([self.origin]) d = self.dimension - extra_options = self.extra_options if d == 1: points = ((x, 0) for x in range(ceil(self.xmin), floor(self.xmax) + 1)) @@ -604,6 +603,12 @@ def plot_walls(self, walls): sage: tp = ToricPlotter(dict(), 2, quadrant.rays()) sage: print tp.plot_walls([quadrant]) Graphics object consisting of 2 graphics primitives + + Let's also check that truncating polyhedron is functioning correctly:: + + sage: tp = ToricPlotter({"mode": "box"}, 2, quadrant.rays()) + sage: print tp.plot_walls([quadrant]) + Graphics object consisting of 2 graphics primitives """ result = Graphics() if not walls or not self.show_walls: @@ -622,7 +627,7 @@ def plot_walls(self, walls): ieqs = [(self.xmax, -1, 0, 0), (- self.xmin, 1, 0, 0), (self.ymax, 0, -1, 0), (- self.ymin, 0, 1, 0), (self.zmax, 0, 0, -1), (- self.zmin, 0, 0, 1)] - box = Polyhedron(ieqs=ieqs, field=RDF) + box = Polyhedron(ieqs=ieqs, base_ring=RDF) for wall, color in zip(walls, colors): result += box.intersection(wall.polyhedron()).render_solid( alpha=alpha, color=color, zorder=zorder, **extra_options) @@ -631,7 +636,7 @@ def plot_walls(self, walls): for wall, color in zip(walls, colors): vertices = [rays[i] for i in wall.ambient_ray_indices()] vertices.append(origin) - result += Polyhedron(vertices=vertices, field=RDF).render_solid( + result += Polyhedron(vertices=vertices, base_ring=RDF).render_solid( alpha=alpha, color=color, zorder=zorder, **extra_options) label_sectors = [] round = mode == "round" From 06b952805ab10b7f30b4c06c1fcd487cbf1b7dce Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Fri, 18 Apr 2014 16:48:35 +0200 Subject: [PATCH 051/546] 8734: fix doctests --- src/sage/interfaces/maxima_abstract.py | 52 ++++++++++++-------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/src/sage/interfaces/maxima_abstract.py b/src/sage/interfaces/maxima_abstract.py index d1e0461026a..b37421ec724 100644 --- a/src/sage/interfaces/maxima_abstract.py +++ b/src/sage/interfaces/maxima_abstract.py @@ -2184,25 +2184,20 @@ def _add_(self, f): sage: f+3 sin(x)+3 - :: + The parameter ``x`` is different from the symbolic variable:: - sage: (f+maxima.cos(x))(2) - sin(2)+cos(2) - sage: (f+maxima.cos(y)) # This is a function with only ONE argument! + sage: (f+maxima.cos(x)) + cos(_SAGE_VAR_x)+sin(x) + sage: (f+maxima.cos(y)) cos(_SAGE_VAR_y)+sin(x) + + Note that you make get unexpected results when calling symbolic expressions + and not explicitly giving the variables:: + + sage: (f+maxima.cos(x))(2) + cos(_SAGE_VAR_x)+sin(2) sage: (f+maxima.cos(y))(2) cos(_SAGE_VAR_y)+sin(2) - - :: - - sage: f = maxima.function('x','sin(x)') - sage: g = -maxima.cos(x) - sage: g+f - sin(x)-cos(_SAGE_VAR_x) - sage: (g+f)(2) # The sum IS a function - sin(2)-cos(2) - sage: 2+f - sin(x)+2 """ return self._operation("+", f) @@ -2214,20 +2209,21 @@ def _sub_(self, f): sage: x,y = var('x,y') sage: f = maxima.function('x','sin(x)') - sage: g = -maxima.cos(x) # not a function - sage: f-g - sin(x)+cos(x) - sage: (f-g)(2) - sin(2)+cos(2) - sage: (f-maxima.cos(y)) # This function only has the argument x! - sin(x)-cos(y) - sage: _(2) - sin(2)-cos(y) + + The parameter ``x`` is different from the symbolic variable:: - :: - - sage: g-f - -sin(x)-cos(x) + sage: (f-maxima.cos(x)) + sin(x)-cos(_SAGE_VAR_x) + sage: (f-maxima.cos(y)) + sin(x)-cos(_SAGE_VAR_y) + + Note that you make get unexpected results when calling symbolic expressions + and not explicitly giving the variables:: + + sage: (f-maxima.cos(x))(2) + sin(2)-cos(_SAGE_VAR_x) + sage: (f-maxima.cos(y))(2) + sin(2)-cos(_SAGE_VAR_y) """ return self._operation("-", f) From 934d057d76651b1662a661cadb7ac4e10d9d477c Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Fri, 18 Apr 2014 18:26:57 +0200 Subject: [PATCH 052/546] 2516: remove hack --- src/sage/functions/hypergeometric.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/functions/hypergeometric.py b/src/sage/functions/hypergeometric.py index f64d13fc570..41497fb5914 100644 --- a/src/sage/functions/hypergeometric.py +++ b/src/sage/functions/hypergeometric.py @@ -345,7 +345,6 @@ def _fast_callable_(cls, self, a, b, z, etb): ....: vars=[x, y])(3, 4) hypergeometric((4,), (), 3) """ - self.__name__ = self.__repr__() # was clobbered by category mechanics return etb.call(self, *map(etb.var, etb._vars)) def sorted_parameters(cls, self, a, b, z): From 0f5a4ff613ba4ae4cd29b73f6f16d61725d19f6b Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Fri, 18 Apr 2014 18:59:33 +0200 Subject: [PATCH 053/546] 2516: fix doctest --- src/sage/ext/fast_callable.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/ext/fast_callable.pyx b/src/sage/ext/fast_callable.pyx index a741c16cb7a..87398d98089 100644 --- a/src/sage/ext/fast_callable.pyx +++ b/src/sage/ext/fast_callable.pyx @@ -482,7 +482,8 @@ def function_name(fn): sage: function_name(factorial) '{factorial}' """ - if isinstance(fn, Expression): + from sage.structure.dynamic_class import DynamicMetaclass + if isinstance(type(fn), DynamicMetaclass): return "{%r}" % fn builtins = get_builtin_functions() if fn in builtins: From 1c1b4eb80425807e7edb96240f102629cc13f312 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 24 Apr 2014 15:48:22 -0400 Subject: [PATCH 054/546] Changed monomial_cmp to generator_cmp and added free (static)method to monoids and groups category. --- src/sage/categories/groups.py | 33 +++++++++++++++++++++++++++- src/sage/categories/monoids.py | 34 +++++++++++++++++++++++++++++ src/sage/combinat/free_module.py | 3 +++ src/sage/groups/free_group.py | 5 ++++- src/sage/misc/indexed_generators.py | 10 ++++----- src/sage/monoids/free_monoid.py | 14 +++++++++++- 6 files changed, 91 insertions(+), 8 deletions(-) diff --git a/src/sage/categories/groups.py b/src/sage/categories/groups.py index f2624d0b259..9f97b17aae9 100644 --- a/src/sage/categories/groups.py +++ b/src/sage/categories/groups.py @@ -54,6 +54,37 @@ def example(self): from sage.groups.matrix_gps.linear import GL return GL(4,QQ) + @staticmethod + def free(n=None, names='x', index_set=None, abelian=False, **kwds): + r""" + Return the free (abelian) group. + + INPUT: + + - ``n`` -- integer or ``None`` (default). The nnumber of + generators. If not specified the ``names`` are counted. + + - ``names`` -- string or list/tuple/iterable of strings (default: + ``'x'``). The generator names or name prefix. + + - ``index_set`` -- (optional) an index set for the generators; if + specified then the optional keyword ``abelian`` can be used + + - ``abelian`` -- (default: ``False``) whether the free monoid is + abelian or not + + EXAMPLES:: + + sage: Groups.free(index_set=ZZ) + Free group indexed by Integer Ring + sage: Groups().free(index_set=ZZ) + Free group indexed by Integer Ring + sage: F. = Groups().free(); F + Free Group on generators {x, y, z} + """ + from sage.groups.free_group import FreeGroup + return FreeGroup(n, names, index_set, abelian, **kwds) + class ParentMethods: def group_generators(self): @@ -466,4 +497,4 @@ def counit(self,x): return sum(x.coefficients()) class ElementMethods: - pass \ No newline at end of file + pass diff --git a/src/sage/categories/monoids.py b/src/sage/categories/monoids.py index 52187d74824..d209f916847 100644 --- a/src/sage/categories/monoids.py +++ b/src/sage/categories/monoids.py @@ -59,6 +59,40 @@ def super_categories(self): """ return [Semigroups()] + @staticmethod + def free(n=None, names=None, index_set=None, abelian=False, **kwds): + r""" + Return a free monoid on `n` generators or with the generators indexed by + a set `I`. + + We construct free monoids by specifing either: + + - the number of generators and/or the names of the generators + - the indexing set for the generators + + INPUT: + + - ``n`` -- integer + + - ``names`` -- names of generators + + - ``index_set`` -- an indexing set for the generators + + - ``abelian`` -- (default: ``False``) whether the free monoid is + abelian or not + + EXAMPLES:: + + sage: Monoids.free(index_set=ZZ) + Free monoid indexed by Integer Ring + sage: Monoids().free(index_set=ZZ) + Free monoid indexed by Integer Ring + sage: F. = Monoids().free(); F + Free monoid on 3 generators (x, y, z) + """ + from sage.monoids.free_monoid import FreeMonoid + return FreeMonoid(n, names, index_set, abelian, **kwds) + class ParentMethods: @cached_method def one(self): diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index 8f54e16ace5..fc16b49ee31 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -1336,6 +1336,9 @@ def __init__(self, R, basis_keys, element_class = None, category = None, prefix= # ignore the optional 'key' since it only affects CachedRepresentation kwds.pop('key', None) # This needs to be first as per #10127 + if 'monomial_cmp' in kwds: + kwds['generator_cmp'] = kwds['monomial_cmp'] + del kwds['monomial_cmp'] IndexedGenerators.__init__(self, basis_keys, prefix, **kwds) Parent.__init__(self, base = R, category = category, diff --git a/src/sage/groups/free_group.py b/src/sage/groups/free_group.py index 3fcf51d7d2c..03c2f64a50c 100644 --- a/src/sage/groups/free_group.py +++ b/src/sage/groups/free_group.py @@ -448,6 +448,9 @@ def FreeGroup(n=None, names='x', index_set=None, abelian=False, **kwds): - ``index_set`` -- (optional) an index set for the generators; if specified then the optional keyword ``abelian`` can be used + - ``abelian`` -- (default: ``False``) whether the free monoid is abelian + or not + EXAMPLES:: sage: G. = FreeGroup(); G @@ -500,7 +503,7 @@ def FreeGroup(n=None, names='x', index_set=None, abelian=False, **kwds): n = len(names) from sage.structure.parent import normalize_names names = tuple(normalize_names(n, names)) - if index_set is not None: + if index_set is not None or abelian: if abelian: from sage.groups.indexed_group import IndexedFreeAbelianGroup return IndexedFreeAbelianGroup(index_set, names=names, **kwds) diff --git a/src/sage/misc/indexed_generators.py b/src/sage/misc/indexed_generators.py index db154448489..27062772646 100644 --- a/src/sage/misc/indexed_generators.py +++ b/src/sage/misc/indexed_generators.py @@ -71,7 +71,7 @@ class IndexedGenerators: string to use for tensor product in the print representation. If None, use the ``sage.categories.tensor.symbol``. - - ``monomial_cmp`` -- a comparison function (optional, default ``cmp``), + - ``generator_cmp`` -- a comparison function (optional, default ``cmp``), to use for sorting elements in the output of elements .. NOTE:: @@ -96,7 +96,7 @@ def __init__(self, indices, prefix="x", **kwds): 'scalar_mult': "*", 'latex_scalar_mult': None, 'tensor_symbol': None, - 'monomial_cmp': cmp} + 'generator_cmp': cmp} # 'bracket': its default value here is None, meaning that # the value of self._repr_option_bracket is used; the default # value of that attribute is True -- see immediately before @@ -151,7 +151,7 @@ def print_options(self, **kwds): - ``scalar_mult`` - ``latex_scalar_mult`` - ``tensor_symbol`` - - ``monomial_cmp`` + - ``generator_cmp`` See the documentation for :class:`CombinatorialFreeModule` for descriptions of the effects of setting each of these options. @@ -172,7 +172,7 @@ def print_options(self, **kwds): TESTS:: sage: sorted(F.print_options().items()) - [('bracket', '('), ('latex_bracket', False), ('latex_prefix', None), ('latex_scalar_mult', None), ('monomial_cmp', ), ('prefix', 'x'), ('scalar_mult', '*'), ('tensor_symbol', None)] + [('bracket', '('), ('latex_bracket', False), ('latex_prefix', None), ('latex_scalar_mult', None), ('generator_cmp', ), ('prefix', 'x'), ('scalar_mult', '*'), ('tensor_symbol', None)] sage: F.print_options(bracket='[') # reset """ # don't just use kwds.get(...) because I want to distinguish @@ -182,7 +182,7 @@ def print_options(self, **kwds): for option in kwds: if option in ['prefix', 'latex_prefix', 'bracket', 'latex_bracket', 'scalar_mult', 'latex_scalar_mult', 'tensor_symbol', - 'monomial_cmp' + 'generator_cmp' ]: self._print_options[option] = kwds[option] else: diff --git a/src/sage/monoids/free_monoid.py b/src/sage/monoids/free_monoid.py index 92f4b42cb80..195864f141c 100644 --- a/src/sage/monoids/free_monoid.py +++ b/src/sage/monoids/free_monoid.py @@ -73,7 +73,7 @@ def create_object(self, version, key, **kwds): FreeMonoid_factory = FreeMonoidFactory("sage.monoids.free_monoid.FreeMonoid_factory") -def FreeMonoid(n=None, names=None, index_set=None, **kwds): +def FreeMonoid(n=None, names=None, index_set=None, abelian=False, **kwds): r""" Return a free monoid on `n` generators or with the generators indexed by a set `I`. @@ -91,6 +91,9 @@ def FreeMonoid(n=None, names=None, index_set=None, **kwds): - ``index_set`` -- an indexing set for the generators + - ``abelian`` -- (default: ``False``) whether the free monoid is abelian + or not + OUTPUT: A free monoid. @@ -101,7 +104,16 @@ def FreeMonoid(n=None, names=None, index_set=None, **kwds): Free monoid on 5 generators (a, b, c, d, e) sage: FreeMonoid(index_set=ZZ) Free monoid indexed by Integer Ring + + sage: F. = FreeMonoid(abelian=True); F + Free abelian monoid on 3 generators (x, y, z) + sage: FreeMonoid(index_set=ZZ, abelian=True) + Free abelian monoid indexed by Integer Ring """ + if abelian: + from sage.monoids.free_abelian_monoid import FreeAbelianMonoid + return FreeAbelianMonoid(n, names, index_set, **kwds) + if isinstance(n, str): # Swap args (this works if names is None as well) names, n = n, names From 47b5be756efad8f784b4bdab01c5a37a70ebd849 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 24 Apr 2014 15:52:04 -0400 Subject: [PATCH 055/546] Removed __contains__ and fix monomial_cmp in indexed_monoid.py --- src/sage/monoids/indexed_monoid.py | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/src/sage/monoids/indexed_monoid.py b/src/sage/monoids/indexed_monoid.py index f86ce3810fc..6111f633aea 100644 --- a/src/sage/monoids/indexed_monoid.py +++ b/src/sage/monoids/indexed_monoid.py @@ -462,10 +462,10 @@ def _sorted_items(self): sage: x = a*b^2*e*d sage: x._sorted_items() ((0, 1), (1, 2), (4, 1), (3, 1)) - sage: F.print_options(monomial_cmp = lambda x,y: -cmp(x,y)) + sage: F.print_options(generator_cmp = lambda x,y: -cmp(x,y)) sage: x._sorted_items() ((0, 1), (1, 2), (4, 1), (3, 1)) - sage: F.print_options(monomial_cmp=cmp) # reset to original state + sage: F.print_options(generator_cmp=cmp) # reset to original state .. SEEALSO:: @@ -560,10 +560,10 @@ def _sorted_items(self): sage: x = a*b^2*e*d sage: x._sorted_items() [(0, 1), (1, 2), (3, 1), (4, 1)] - sage: F.print_options(monomial_cmp = lambda x,y: -cmp(x,y)) + sage: F.print_options(generator_cmp = lambda x,y: -cmp(x,y)) sage: x._sorted_items() [(4, 1), (3, 1), (1, 2), (0, 1)] - sage: F.print_options(monomial_cmp=cmp) # reset to original state + sage: F.print_options(generator_cmp=cmp) # reset to original state .. SEEALSO:: @@ -572,7 +572,7 @@ def _sorted_items(self): print_options = self.parent().print_options() v = self._monomial.items() try: - v.sort(cmp = print_options['monomial_cmp']) + v.sort(cmp = print_options['generator_cmp']) except StandardError: # Sorting the output is a plus, but if we can't, no big deal pass return v @@ -776,24 +776,6 @@ def _an_element_(self): pass return x - def __contains__(self, x): - r""" - Return ``True`` if `x` is an element of ``self``. - - EXAMPLES:: - - sage: F = FreeAbelianMonoid(index_set=ZZ) - sage: F.gen(2)*F.gen(3) in F - True - - Note that a monoid on `\NN` generators is not considered a - submonoid of one on `\ZZ` generators:: - - sage: FreeAbelianMonoid(index_set=NN).gen(2) in F - False - """ - return isinstance(x, self.element_class) and x.parent() is self - def gens(self): """ Return the generators of ``self``. From 23301a86c56549062b6441c7b2ac6336da0d1001 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 25 Apr 2014 12:57:19 -0400 Subject: [PATCH 056/546] Misc fixes and tweaks. --- src/sage/combinat/combinatorial_algebra.py | 4 +- src/sage/combinat/free_module.py | 40 +++++++------------- src/sage/combinat/sf/sfa.py | 6 +-- src/sage/combinat/symmetric_group_algebra.py | 10 ++--- src/sage/misc/indexed_generators.py | 2 +- 5 files changed, 23 insertions(+), 39 deletions(-) diff --git a/src/sage/combinat/combinatorial_algebra.py b/src/sage/combinat/combinatorial_algebra.py index c398b5a7259..60b48eb39fc 100644 --- a/src/sage/combinat/combinatorial_algebra.py +++ b/src/sage/combinat/combinatorial_algebra.py @@ -192,9 +192,7 @@ def __init__(self, R, cc = None, element_class = None, category = None): # for backward compatibility if cc is None: - if hasattr(self, "_basis_keys"): - cc = self._indices - elif hasattr(self, "_indices"): + if hasattr(self, "_indices"): cc = self._indices assert(cc is not None) diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index fc16b49ee31..cadaf06fe9e 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -181,17 +181,17 @@ def _sorted_items_for_printing(self): sage: f = B['a'] + 2*B['c'] + 3 * B['b'] sage: f._sorted_items_for_printing() [('a', 1), ('b', 3), ('c', 2)] - sage: F.print_options(monomial_cmp = lambda x,y: -cmp(x,y)) + sage: F.print_options(generator_cmp = lambda x,y: -cmp(x,y)) sage: f._sorted_items_for_printing() [('c', 2), ('b', 3), ('a', 1)] - sage: F.print_options(monomial_cmp=cmp) #reset to original state + sage: F.print_options(generator_cmp=cmp) #reset to original state .. seealso:: :meth:`_repr_`, :meth:`_latex_`, :meth:`print_options` """ print_options = self.parent().print_options() v = self._monomial_coefficients.items() try: - v.sort(cmp = print_options['monomial_cmp'], + v.sort(cmp = print_options['generator_cmp'], key = lambda monomial_coeff: monomial_coeff[0]) except Exception: # Sorting the output is a plus, but if we can't, no big deal pass @@ -218,13 +218,13 @@ def _repr_(self): function on elements of the support:: sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], - ... monomial_cmp = lambda x,y: cmp(y,x)) + ... generator_cmp = lambda x,y: cmp(y,x)) sage: e = F.basis() sage: e['a'] + 3*e['b'] + 2*e['c'] 2*B['c'] + 3*B['b'] + B['a'] sage: F = CombinatorialFreeModule(QQ, ['ac', 'ba', 'cb'], - ... monomial_cmp = lambda x,y: cmp(x[1],y[1])) + ... generator_cmp = lambda x,y: cmp(x[1],y[1])) sage: e = F.basis() sage: e['ac'] + 3*e['ba'] + 2*e['cb'] 3*B['ba'] + 2*B['cb'] + B['ac'] @@ -325,13 +325,13 @@ def _latex_(self): function on elements of the support:: sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], - ... monomial_cmp = lambda x,y: cmp(y,x)) + ... generator_cmp = lambda x,y: cmp(y,x)) sage: e = F.basis() sage: latex(e['a'] + 3*e['b'] + 2*e['c']) 2B_{c} + 3B_{b} + B_{a} sage: F = CombinatorialFreeModule(QQ, ['ac', 'ba', 'cb'], - ... monomial_cmp = lambda x,y: cmp(x[1],y[1])) + ... generator_cmp = lambda x,y: cmp(x[1],y[1])) sage: e = F.basis() sage: latex(e['ac'] + 3*e['ba'] + 2*e['cb']) 3B_{ba} + 2B_{cb} + B_{ac} @@ -1053,7 +1053,7 @@ class CombinatorialFreeModule(UniqueRepresentation, Module, IndexedGenerators): string to use for tensor product in the print representation. If None, use the ``sage.categories.tensor.symbol``. - - ``monomial_cmp`` - a comparison function (optional, default cmp), + - ``generator_cmp`` - a comparison function (optional, default cmp), to use for sorting elements in the output of elements .. note:: These print options may also be accessed and modified using the @@ -1161,7 +1161,10 @@ class CombinatorialFreeModule(UniqueRepresentation, Module, IndexedGenerators): sage: F = CombinatorialFreeModule(QQ, ['a','b'], prefix='x') sage: original_print_options = F.print_options() sage: sorted(original_print_options.items()) - [('bracket', None), ('latex_bracket', False), ('latex_prefix', None), ('latex_scalar_mult', None), ('monomial_cmp', ), ('prefix', 'x'), ('scalar_mult', '*'), ('tensor_symbol', None)] + [('bracket', None), ('generator_cmp', ), + ('latex_bracket', False), ('latex_prefix', None), + ('latex_scalar_mult', None), ('prefix', 'x'), + ('scalar_mult', '*'), ('tensor_symbol', None)] sage: e = F.basis() sage: e['a'] - 3 * e['b'] @@ -1177,7 +1180,7 @@ class CombinatorialFreeModule(UniqueRepresentation, Module, IndexedGenerators): sage: latex(e['a'] - 3 * e['b']) y_{a} - 3 y_{b} - sage: F.print_options(monomial_cmp = lambda x,y: -cmp(x,y)) + sage: F.print_options(generator_cmp = lambda x,y: -cmp(x,y)) sage: e['a'] - 3 * e['b'] -3 x{'b'} + x{'a'} sage: F.print_options(**original_print_options) # reset print options @@ -1597,23 +1600,6 @@ def _an_element_impl(self): """ return self.element_class(self, {}) - @lazy_attribute - def _basis_keys(self): - """ - Deprecated: use ``self._indices`` instead. - - EXAMPLES:: - - sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c']) - sage: F._basis_keys - doctest:...: DeprecationWarning: "FM._basis_keys" is deprecated. Use "F._indices" instead! - See http://trac.sagemath.org/15289 for details. - {'a', 'b', 'c'} - """ - from sage.misc.superseded import deprecation - deprecation(15289, '"FM._basis_keys" is deprecated. Use "F._indices" instead!') - return self._indices - def dimension(self): """ Returns the dimension of the combinatorial algebra (which is given diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 45adade6ae5..cae29c63d0f 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -2020,11 +2020,11 @@ def set_print_style(self, ps): sage: s.set_print_style('lex') """ if ps == 'lex': - self.print_options(monomial_cmp = lambda x,y: cmp(x,y)) + self.print_options(generator_cmp = lambda x,y: cmp(x,y)) elif ps == 'length': - self.print_options(monomial_cmp = lambda x,y: cmp(len(x), len(y))) + self.print_options(generator_cmp = lambda x,y: cmp(len(x), len(y))) elif ps == 'maximal_part': - self.print_options(monomial_cmp = lambda x,y: cmp(_lmax(x), _lmax(y))) + self.print_options(generator_cmp = lambda x,y: cmp(_lmax(x), _lmax(y))) else: raise ValueError("the print style must be one of lex, length, or maximal_part ") self._print_style = ps diff --git a/src/sage/combinat/symmetric_group_algebra.py b/src/sage/combinat/symmetric_group_algebra.py index a326d3ba626..1b44f70f69f 100644 --- a/src/sage/combinat/symmetric_group_algebra.py +++ b/src/sage/combinat/symmetric_group_algebra.py @@ -640,7 +640,7 @@ def algebra_generators(self): a[1] = 1 b = range(2, self.n+2) b[self.n-1] = 1 - return [self.monomial(self._basis_keys(a)), self.monomial(self._basis_keys(b))] + return [self.monomial(self._indices(a)), self.monomial(self._indices(b))] def _conjugacy_classes_representatives_underlying_group(self): r""" @@ -962,7 +962,7 @@ def jucys_murphy(self, k): p = range(1, self.n+1) p[i-1] = k p[k-1] = i - res += self.monomial(self._basis_keys(p)) + res += self.monomial(self._indices(p)) return res @@ -2026,7 +2026,7 @@ def _coerce_start(self, x): if x == []: return self.one() if len(x) < self.n and x in Permutations(): - return self.monomial(self._basis_keys( list(x) + range(len(x)+1, self.n+1) )) + return self.monomial(self._indices( list(x) + range(len(x)+1, self.n+1) )) raise TypeError class HeckeAlgebraSymmetricGroup_t(HeckeAlgebraSymmetricGroup_generic): @@ -2073,7 +2073,7 @@ def t_action_on_basis(self, perm, i): # -- Darij, 19 Nov 2013 if perm[i-1] < perm[i]: - return self.monomial(self._basis_keys(perm_i)) + return self.monomial(self._indices(perm_i)) else: #Ti^2 = (q - q^(-1))*Ti - q1*q2 q = self.q() @@ -2202,7 +2202,7 @@ def jucys_murphy(self, k): raise ValueError("k (= %(k)d) must be between 1 and n (= %(n)d)" % {'k': k, 'n': self.n}) q = self.q() - P = self._basis_keys + P = self._indices v = self.sum_of_terms( ( ( P(range(1, l) + [k] + range(l+1, k) + [l]), q ** l - q ** (l-1) ) for l in range(1, k) ), diff --git a/src/sage/misc/indexed_generators.py b/src/sage/misc/indexed_generators.py index 27062772646..0e87d08f335 100644 --- a/src/sage/misc/indexed_generators.py +++ b/src/sage/misc/indexed_generators.py @@ -100,7 +100,7 @@ def __init__(self, indices, prefix="x", **kwds): # 'bracket': its default value here is None, meaning that # the value of self._repr_option_bracket is used; the default # value of that attribute is True -- see immediately before - # the method _repr_term. If 'bracket' is any value + # the method _repr_generator. If 'bracket' is any value # except None, then it overrides the value of # self._repr_option_bracket. Future users might consider # using 'bracket' instead of _repr_option_bracket. From b486f244b98a16b144cd45399eee3112ed5f824b Mon Sep 17 00:00:00 2001 From: Sebastien Besnier Date: Fri, 25 Apr 2014 17:01:12 +0000 Subject: [PATCH 057/546] Fix parents, domain, codomain and _composition_ isogenies. --- .../elliptic_curves/ell_curve_isogeny.py | 75 +------------------ 1 file changed, 1 insertion(+), 74 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index 101b81eda37..cb8d2d21eb8 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -1322,7 +1322,7 @@ def __perform_inheritance_housekeeping(self): self._codomain = self.__E2 # sets up the parent - parent = homset.Hom(self.__E1(0).parent(), self.__E2(0).parent()) + parent = homset.Hom(self.__E1, self.__E2) Morphism.__init__(self, parent) return @@ -2574,46 +2574,6 @@ def __compute_E2_via_kohel(self): # public isogeny methods # - def domain(self): - r""" - Returns the domain curve of this isogeny. - - EXAMPLES:: - - sage: E = EllipticCurve(QQ, [0,0,0,1,0]) - sage: phi = EllipticCurveIsogeny(E, E(0,0)) - sage: phi.domain() == E - True - - sage: E = EllipticCurve(GF(31), [1,0,0,1,2]) - sage: phi = EllipticCurveIsogeny(E, [17, 1]) - sage: phi.domain() - Elliptic Curve defined by y^2 + x*y = x^3 + x + 2 over Finite Field of size 31 - - """ - return self.__E1 - - - def codomain(self): - r""" - Returns the codomain (range) curve of this isogeny. - - EXAMPLES:: - - sage: E = EllipticCurve(QQ, [0,0,0,1,0]) - sage: phi = EllipticCurveIsogeny(E, E((0,0))) - sage: phi.codomain() - Elliptic Curve defined by y^2 = x^3 - 4*x over Rational Field - - sage: E = EllipticCurve(GF(31), [1,0,0,1,2]) - sage: phi = EllipticCurveIsogeny(E, [17, 1]) - sage: phi.codomain() - Elliptic Curve defined by y^2 + x*y = x^3 + 24*x + 6 over Finite Field of size 31 - - """ - return self.__E2 - - def degree(self): r""" Returns the degree of this isogeny. @@ -3329,39 +3289,6 @@ def formal(self,prec=20): # Overload Morphism methods that we want to # - def _composition_(self, right, homset): - r""" - Composition operator function inherited from morphism class. - - EXAMPLES:: - - sage: E = EllipticCurve(j=GF(7)(0)) - sage: phi = EllipticCurveIsogeny(E, [E(0), E((0,1)), E((0,-1))]) - sage: phi._composition_(phi, phi.parent()) - Traceback (most recent call last): - ... - NotImplementedError - - The following should test that :meth:`_composition_` is called - upon a product. However phi is currently improperly - constructed (see :trac:`12880`), which triggers an assertion - failure before the actual call :: - - sage: phi*phi - Traceback (most recent call last): - ... - TypeError: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 is not in Category of hom sets in Category of Schemes - - Here would be the desired output:: - - sage: phi*phi # not tested - Traceback (most recent call last): - ... - NotImplementedError - """ - raise NotImplementedError - - def is_injective(self): r""" Method inherited from the morphism class. Returns ``True`` if From ecc5f70423cf25c566fd8f30364a3ffe295d6ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Sat, 26 Apr 2014 15:10:25 +0200 Subject: [PATCH 058/546] assert_have_dot2tex should reraise exception --- src/sage/graphs/dot2tex_utils.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sage/graphs/dot2tex_utils.py b/src/sage/graphs/dot2tex_utils.py index 0b808000dc8..c6d4d9a3afe 100644 --- a/src/sage/graphs/dot2tex_utils.py +++ b/src/sage/graphs/dot2tex_utils.py @@ -48,18 +48,20 @@ def assert_have_dot2tex(): For support, please contact . """ - missing_error_string = """ -dot2tex not available. + import_error_string = """ +An error occured when importing dot2tex. Please see :meth:`sage.graphs.generic_graph.GenericGraph.layout_graphviz` for installation instructions. """ try: import dot2tex + except ImportError as e: + print import_error_string + raise # re-raise current exception + else: if dot2tex.dot2tex("graph {}", format = "positions") != {}: raise RuntimeError(check_error_string) - except ImportError: - raise RuntimeError(missing_error_string) def quoted_latex(x): """ From 73d99af403e760801ae2576dfbb6f7629d629f30 Mon Sep 17 00:00:00 2001 From: Sebastien Besnier Date: Sat, 26 Apr 2014 20:08:42 +0000 Subject: [PATCH 059/546] Corrects the precedent commit. I've restored the `meth::_composition_` (it was not the good ticket to change this; see #16245), updated its doctest and also restored the doctests for `meth::domain()` and `meth::codomain()`. --- .../elliptic_curves/ell_curve_isogeny.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index cb8d2d21eb8..741c0e0cfab 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -700,6 +700,7 @@ class EllipticCurveIsogeny(Morphism): True sage: phi_s.rational_maps() == phi.rational_maps() True + However only cyclic normalized isogenies can be constructed this way. So it won't find the isogeny [3]:: @@ -736,6 +737,22 @@ class EllipticCurveIsogeny(Morphism): sage: phi.rational_maps() (((4/25*i + 3/25)*x^5 + (4/5*i - 2/5)*x^3 - x)/(x^4 + (-4/5*i + 2/5)*x^2 + (-4/25*i - 3/25)), ((11/125*i + 2/125)*x^6*y + (-23/125*i + 64/125)*x^4*y + (141/125*i + 162/125)*x^2*y + (3/25*i - 4/25)*y)/(x^6 + (-6/5*i + 3/5)*x^4 + (-12/25*i - 9/25)*x^2 + (2/125*i - 11/125))) + + TESTS (track #12880):: + + sage: E = EllipticCurve(QQ, [0,0,0,1,0]) + sage: phi = EllipticCurveIsogeny(E, E(0,0)) + sage: phi.domain() == E + True + sage: phi.codomain() + Elliptic Curve defined by y^2 = x^3 - 4*x over Rational Field + + sage: E = EllipticCurve(GF(31), [1,0,0,1,2]) + sage: phi = EllipticCurveIsogeny(E, [17, 1]) + sage: phi.domain() + Elliptic Curve defined by y^2 + x*y = x^3 + x + 2 over Finite Field of size 31 + sage: phi.codomain() + Elliptic Curve defined by y^2 + x*y = x^3 + 24*x + 6 over Finite Field of size 31 """ #################### @@ -3289,6 +3306,30 @@ def formal(self,prec=20): # Overload Morphism methods that we want to # + def _composition_(self, right, homset): + r""" + Composition operator function inherited from morphism class. + + EXAMPLES:: + + sage: E = EllipticCurve(j=GF(7)(0)) + sage: phi = EllipticCurveIsogeny(E, [E(0), E((0,1)), E((0,-1))]) + sage: phi._composition_(phi, phi.parent()) + Traceback (most recent call last): + ... + NotImplementedError + + The following should test that :meth:`_composition_` is called + upon a product (modified for ticket #12880 ; see ticket #16245 where we + fix the _composition_ issue). + + sage: phi*phi + Traceback (most recent call last): + ... + NotImplementedError + """ + raise NotImplementedError + def is_injective(self): r""" Method inherited from the morphism class. Returns ``True`` if From 09fdbedfa23931be1035e8860c61eb4a233b61ea Mon Sep 17 00:00:00 2001 From: Sebastien Besnier Date: Mon, 28 Apr 2014 11:26:13 +0000 Subject: [PATCH 060/546] Corrects stylistic staff in doctests. --- src/sage/schemes/elliptic_curves/ell_curve_isogeny.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index 741c0e0cfab..43f6e07fe39 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -738,7 +738,7 @@ class EllipticCurveIsogeny(Morphism): (((4/25*i + 3/25)*x^5 + (4/5*i - 2/5)*x^3 - x)/(x^4 + (-4/5*i + 2/5)*x^2 + (-4/25*i - 3/25)), ((11/125*i + 2/125)*x^6*y + (-23/125*i + 64/125)*x^4*y + (141/125*i + 162/125)*x^2*y + (3/25*i - 4/25)*y)/(x^6 + (-6/5*i + 3/5)*x^4 + (-12/25*i - 9/25)*x^2 + (2/125*i - 11/125))) - TESTS (track #12880):: + Domain and codomain tests (see :trac:`12880`):: sage: E = EllipticCurve(QQ, [0,0,0,1,0]) sage: phi = EllipticCurveIsogeny(E, E(0,0)) @@ -3320,7 +3320,7 @@ def _composition_(self, right, homset): NotImplementedError The following should test that :meth:`_composition_` is called - upon a product (modified for ticket #12880 ; see ticket #16245 where we + upon a product (modified for :trac:`12880` ; see :trac:`16245` where we fix the _composition_ issue). sage: phi*phi From 81eede490c2d46a5875145b5ba1138e08605ffdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= Date: Mon, 5 May 2014 17:43:32 +0200 Subject: [PATCH 061/546] 15289: Proofreading --- src/sage/categories/groups.py | 10 +++++----- src/sage/categories/monoids.py | 10 +++++----- src/sage/combinat/free_module.py | 2 +- src/sage/groups/free_group.py | 6 +++--- src/sage/groups/indexed_group.py | 31 +++++++++++++++++++++---------- 5 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/sage/categories/groups.py b/src/sage/categories/groups.py index 9f97b17aae9..e988a07413b 100644 --- a/src/sage/categories/groups.py +++ b/src/sage/categories/groups.py @@ -61,17 +61,17 @@ def free(n=None, names='x', index_set=None, abelian=False, **kwds): INPUT: - - ``n`` -- integer or ``None`` (default). The nnumber of + - ``n`` -- an integer or ``None`` (default). The number of generators. If not specified the ``names`` are counted. - - ``names`` -- string or list/tuple/iterable of strings (default: - ``'x'``). The generator names or name prefix. + - ``names`` -- a string or list/tuple/iterable of strings + (default: ``'x'``). The generator names or name prefix. - ``index_set`` -- (optional) an index set for the generators; if specified then the optional keyword ``abelian`` can be used - - ``abelian`` -- (default: ``False``) whether the free monoid is - abelian or not + - ``abelian`` -- (default: ``False``) whether to construct the + free abelian group or the free group EXAMPLES:: diff --git a/src/sage/categories/monoids.py b/src/sage/categories/monoids.py index d209f916847..553289a23e8 100644 --- a/src/sage/categories/monoids.py +++ b/src/sage/categories/monoids.py @@ -65,21 +65,21 @@ def free(n=None, names=None, index_set=None, abelian=False, **kwds): Return a free monoid on `n` generators or with the generators indexed by a set `I`. - We construct free monoids by specifing either: + A free monoid is constructed by specifing either: - the number of generators and/or the names of the generators - the indexing set for the generators INPUT: - - ``n`` -- integer + - ``n`` -- an integer - - ``names`` -- names of generators + - ``names`` -- names for the generators - ``index_set`` -- an indexing set for the generators - - ``abelian`` -- (default: ``False``) whether the free monoid is - abelian or not + - ``abelian`` -- (default: ``False``) whether to construct the + free abelian monoid or the free monoid EXAMPLES:: diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index cadaf06fe9e..107ff495b01 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -1356,7 +1356,7 @@ def __init__(self, R, basis_keys, element_class = None, category = None, prefix= def _ascii_art_term(self, m): r""" - Return an ascii art representing the generator indexed by ``m``. + Return an ascii art representation of the term indexed by ``m``. TESTS:: diff --git a/src/sage/groups/free_group.py b/src/sage/groups/free_group.py index 03c2f64a50c..5bf5c59f3ad 100644 --- a/src/sage/groups/free_group.py +++ b/src/sage/groups/free_group.py @@ -448,8 +448,8 @@ def FreeGroup(n=None, names='x', index_set=None, abelian=False, **kwds): - ``index_set`` -- (optional) an index set for the generators; if specified then the optional keyword ``abelian`` can be used - - ``abelian`` -- (default: ``False``) whether the free monoid is abelian - or not + - ``abelian`` -- (default: ``False``) whether to construct a free + abelian group or a free group EXAMPLES:: @@ -472,7 +472,7 @@ def FreeGroup(n=None, names='x', index_set=None, abelian=False, **kwds): sage: FreeGroup() Free Group on generators {x} - We give two examples with the index set:: + We give two examples using the ``index_set`` option:: sage: FreeGroup(index_set=ZZ) Free group indexed by Integer Ring diff --git a/src/sage/groups/indexed_group.py b/src/sage/groups/indexed_group.py index 6de82b1d9b3..f9bee271699 100644 --- a/src/sage/groups/indexed_group.py +++ b/src/sage/groups/indexed_group.py @@ -58,17 +58,20 @@ def __init__(self, indices, prefix, category=None, **kwds): def _repr_(self): """ + Return a string representation of ``self`` + TESTS:: - sage: FreeGroup(index_set=ZZ) + sage: FreeGroup(index_set=ZZ) # indirect doctest Free group indexed by Integer Ring """ return 'Free group indexed by {}'.format(self._indices) def order(self): r""" - Return the number of elements of ``self``, which is `\infty` unless - this is the trivial group. + Return the number of elements of ``self``. + + This is `\infty` unless this is the trivial group. EXAMPLES:: @@ -86,6 +89,9 @@ def order(self): return Integer(1) return infinity + # TODO: once #10963 is merged, use the categories + # Groups().Infinite() / Groups().Finite() and get rid of this + # method def is_finite(self): """ Return ``True`` if ``self`` is finite. @@ -106,8 +112,9 @@ def is_finite(self): def rank(self): """ - Return the rank of ``self``, which is the number of - generators of ``self``. + Return the rank of ``self``. + + This is the number of generators of ``self``. EXAMPLES:: @@ -124,9 +131,13 @@ def rank(self): return self.gens().cardinality() class Element(IndexedFreeMonoid.Element): - def __lt__(self, y): + def __lt__(self, other): """ - Check less than. + Return whether ``self`` is smaller than ``y``. + + This is done by comparing lexicographically the words for + ``self`` and ``y``. In particular this assumes that the + (index of) the generators are totally ordered. EXAMPLES:: @@ -143,9 +154,9 @@ def __lt__(self, y): sage: a^2*b < a*b^-1*a*b True """ - if not isinstance(y, IndexedMonoidElement): + if not isinstance(other, IndexedMonoidElement): return False - return self.to_word_list() < y.to_word_list() + return self.to_word_list() < other.to_word_list() def __len__(self): """ @@ -219,7 +230,7 @@ def __invert__(self): def __pow__(self, n): """ - Raise ``self`` to the power of ``n``. + Raise ``self`` to the `n`-th power. EXAMPLES:: From 7d27baacb4721ee041162cb280b7ee48d2b0da34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= Date: Mon, 5 May 2014 22:47:02 +0200 Subject: [PATCH 062/546] #15289: misc minor improvements to the doc --- src/sage/misc/indexed_generators.py | 26 +++++++++++++------------- src/sage/monoids/indexed_monoid.py | 12 ++++++------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/sage/misc/indexed_generators.py b/src/sage/misc/indexed_generators.py index 0e87d08f335..f32fdcc0b69 100644 --- a/src/sage/misc/indexed_generators.py +++ b/src/sage/misc/indexed_generators.py @@ -32,12 +32,12 @@ class IndexedGenerators: True. - ``bracket`` -- ``None``, bool, string, or list or tuple of - strings (optional, default None): if ``None``, use the value of the + strings (optional, default ``None``): if ``None``, use the value of the attribute ``self._repr_option_bracket``, which has default value - True. (``self._repr_option_bracket`` is available for backwards + ``True``. (``self._repr_option_bracket`` is available for backwards compatibility. Users should set ``bracket`` instead. If - ``bracket`` is set to anything except None, it overrides - the value of ``self._repr_option_bracket``.) If False, do not + ``bracket`` is set to anything except ``None``, it overrides + the value of ``self._repr_option_bracket``.) If ``False``, do not include brackets when printing elements: a monomial indexed by 'a' would be printed as ``B'a'``, and a monomial indexed by (1,2,3) would be printed as ``B(1,2,3)``. If True, use "[" and @@ -62,16 +62,16 @@ class IndexedGenerators: - ``scalar_mult`` -- string to use for scalar multiplication in the print representation (optional, default "*") - - ``latex_scalar_mult`` -- string or ``None`` (optional, default ``None``), + - ``latex_scalar_mult`` -- string or ``None`` (default: ``None``), string to use for scalar multiplication in the latex representation. If None, use the empty string if ``scalar_mult`` is set to "*", otherwise use the value of ``scalar_mult``. - - ``tensor_symbol`` -- string or ``None`` (optional, default ``None``), + - ``tensor_symbol`` -- string or ``None`` (default: ``None``), string to use for tensor product in the print representation. If - None, use the ``sage.categories.tensor.symbol``. + ``None``, use ``sage.categories.tensor.symbol``. - - ``generator_cmp`` -- a comparison function (optional, default ``cmp``), + - ``generator_cmp`` -- a comparison function (default: ``cmp``), to use for sorting elements in the output of elements .. NOTE:: @@ -180,6 +180,7 @@ def print_options(self, **kwds): # being there altogether. if kwds: for option in kwds: + # TODO: make this into a set and put it in a global variable? if option in ['prefix', 'latex_prefix', 'bracket', 'latex_bracket', 'scalar_mult', 'latex_scalar_mult', 'tensor_symbol', 'generator_cmp' @@ -197,7 +198,7 @@ def _repr_generator(self, m): Return a string representing the generator indexed by ``m``. The output can be customized by setting any of the following - options when initializing the module: + options when initializing the parent: - ``prefix`` - ``bracket`` @@ -272,7 +273,7 @@ def _repr_generator(self, m): left = bracket right = bracket return self.prefix() + left + repr(m) + right # mind the (m), to accept a tuple for m - + def _ascii_art_generator(self, m): r""" Return an ascii art representing the generator indexed by ``m``. @@ -302,11 +303,10 @@ def _ascii_art_generator(self, m): def _latex_generator(self, m): r""" - Return a string for the `\LaTeX` code for the generator - indexed by ``m``. + Return a `\LaTeX` for the generator indexed by ``m``. The output can be customized by setting any of the following - options when initializing the module: + options when initializing the parent: - ``prefix`` - ``latex_prefix`` diff --git a/src/sage/monoids/indexed_monoid.py b/src/sage/monoids/indexed_monoid.py index 6111f633aea..fce88fbfa61 100644 --- a/src/sage/monoids/indexed_monoid.py +++ b/src/sage/monoids/indexed_monoid.py @@ -33,17 +33,17 @@ class IndexedMonoidElement(MonoidElement): An element of an indexed monoid. This is an abstract class which uses the (abstract) method - :meth:`_sorted_items` for all of it's functions. So to implement an + :meth:`_sorted_items` for all of its functions. So to implement an element of an indexed monoid, one just needs to implement - :meth:`_sorted_items`, which returns an list of pairs ``(i, p)`` where + :meth:`_sorted_items`, which returns a list of pairs ``(i, p)`` where ``i`` is the index and ``p`` is the corresponding power, sorted in some order. For example, in the free monoid there is no such choice, but for the free abelian monoid, one could want lex order or have the highest powers first. - Indexed monoid elements are ordered lexicographically based upon the - order of word from :meth:`_sorted_items` and the order of the indexing - set. + Indexed monoid elements are ordered lexicographically w.r.t. the + result of :meth:`_sorted_items` (which for abelian free monoids is + influenced by the order on the indexing set). """ def __init__(self, F, x): """ @@ -87,7 +87,7 @@ def _sorted_items(self): :meth:`_repr_`, :meth:`_latex_`, :meth:`print_options` """ - + def _repr_(self): """ Return a string representation of ``self``. From 6b94a6576031e6e341143a4fc1fd83263a800ed9 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 8 May 2014 18:03:24 +0200 Subject: [PATCH 063/546] Improve sums of squares of integers --- src/sage/libs/pari/decl.pxi | 1 + src/sage/libs/pari/gen.pyx | 15 ++ src/sage/rings/all.py | 3 +- src/sage/rings/arith.py | 421 ++++++++++++++++++++++++++++-------- 4 files changed, 346 insertions(+), 94 deletions(-) diff --git a/src/sage/libs/pari/decl.pxi b/src/sage/libs/pari/decl.pxi index 41a8e1a809e..6e3366467bc 100644 --- a/src/sage/libs/pari/decl.pxi +++ b/src/sage/libs/pari/decl.pxi @@ -638,6 +638,7 @@ cdef extern from 'pari/pari.h': ulong Fl_sqrt(ulong a, ulong p) GEN znprimroot0(GEN m) GEN znstar(GEN x) + GEN sqrtint(GEN x) # arith2.c diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index 71b3f142d58..e820d4332c9 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -4787,6 +4787,21 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gsqrt(x.g, prec_bits_to_words(precision))) + def sqrtint(gen x): + r""" + Return the integer square root of the integer `x`, rounded + towards zero. + + EXAMPLES:: + + sage: pari(8).sqrtint() + 2 + sage: pari(10^100).sqrtint() + 100000000000000000000000000000000000000000000000000 + """ + pari_catch_sig_on() + return P.new_gen(sqrtint(x.g)) + def sqrtn(gen x, n, unsigned long precision=0): r""" x.sqrtn(n): return the principal branch of the n-th root of x, diff --git a/src/sage/rings/all.py b/src/sage/rings/all.py index 7389443dee0..5822f82cc84 100644 --- a/src/sage/rings/all.py +++ b/src/sage/rings/all.py @@ -151,7 +151,8 @@ farey, continued_fraction_list, convergent, convergents, \ continuant, number_of_divisors, hilbert_symbol, hilbert_conductor, \ hilbert_conductor_inverse, falling_factorial, rising_factorial, \ - integer_ceil, integer_floor, two_squares, four_squares, \ + integer_ceil, integer_floor, \ + two_squares, three_squares, four_squares, sum_of_k_squares, \ subfactorial, is_power_of_two, differences, \ sort_complex_numbers_for_display, \ fundamental_discriminant, squarefree_divisors, \ diff --git a/src/sage/rings/arith.py b/src/sage/rings/arith.py index b9efb9f7b7f..0ea90152efb 100644 --- a/src/sage/rings/arith.py +++ b/src/sage/rings/arith.py @@ -4906,141 +4906,376 @@ def integer_floor(x): raise NotImplementedError("computation of floor of %s not implemented"%x) - -def two_squares(n, algorithm='gap'): +def two_squares(n): """ - Write the integer n as a sum of two integer squares if possible; - otherwise raise a ValueError. + Write the integer `n` as a sum of two integer squares if possible; + otherwise raise a ``ValueError``. + + INPUT: + + - ``n`` -- an integer + + OUTPUT: a tuple `(a,b)` of non-negative integers such that + `n = a^2 + b^2` with `a <= b`. EXAMPLES:: sage: two_squares(389) (10, 17) - sage: two_squares(7) + sage: two_squares(21) Traceback (most recent call last): ... - ValueError: 7 is not a sum of two squares + ValueError: 21 is not a sum of 2 squares + sage: two_squares(21^2) + (0, 21) sage: a,b = two_squares(2009); a,b (28, 35) sage: a^2 + b^2 2009 + sage: two_squares(2^222+1) + (253801659504708621991421712450521, 2583712713213354898490304645018692) + sage: two_squares(0) + (0, 0) + sage: two_squares(-1) + Traceback (most recent call last): + ... + ValueError: -1 is not a sum of 2 squares + + ALGORITHM: - TODO: Create an implementation using PARI's continued fraction - implementation. + See http://www.schorn.ch/howto.html """ from sage.rings.all import Integer - n = Integer(n) + n = pari(n) - if algorithm == 'gap': - import sage.interfaces.gap as gap - a = gap.gap.eval('TwoSquares(%s)'%n) - if a == 'fail': - raise ValueError("%s is not a sum of two squares"%n) - x, y = eval(a) - return Integer(x), Integer(y) + if n <= 0: + if n == 0: + z = ZZ.zero() + return (z, z) + raise ValueError("%s is not a sum of 2 squares"%n) + + # Start by factoring n (which seems to be unavoidable) + F = n.factor(proof=False) + + # First check whether it is possible to write n as a sum of two + # squares: all prime powers p^e must have p = 2 or p = 1 mod 4 + # or e even. + for i in range(F.nrows()): + if F[1][i] % 2 == 1 and F[0][i] % 4 == 3: + raise ValueError("%s is not a sum of 2 squares"%n) + + # We run over all factors of n, write each factor p^e as + # a sum of 2 squares and accumulate the product + # (using multiplication in Z[I]) in a^2 + b^2. + a = pari(1) + b = pari(0) + for i in range(F.nrows()): + e = int(F[1][i]) + if e >= 2: + m = F[0][i] ** (e//2) + a *= m + b *= m + if e % 2 == 1: + p = F[0][i] + if p == 2: + # (a + bi) *= (1 + I) + a,b = a - b, a + b + else: # p = 1 mod 4 + # Find a square root of -1 mod p. + # If y is a non-square, then y^((p-1)/4) is a square root of -1. + y = pari(2).Mod(p) + while True: + s = y**((p-1)/4) + if not s*s + 1: + s = s.lift() + break + y += 1 + # Apply Cornacchia's algorithm to write p as r^2 + s^2. + r = p + while s*s > p: + r,s = s, r % s + r %= s + + # Multiply (a + bI) by (r + sI) + a,b = a*r - b*s, b*r + a*s + + a = Integer(a.abs()) + b = Integer(b.abs()) + assert a*a + b*b == n + if a <= b: + return (a,b) else: - raise RuntimeError("unknown algorithm '%s'"%algorithm) + return (b,a) -def _brute_force_four_squares(n): +def three_squares(n): """ - Brute force search for decomposition into a sum of four squares, - for cases that the main algorithm fails to handle. + Write the integer `n` as a sum of three integer squares if possible; + otherwise raise a ``ValueError``. INPUT: - - ``n`` - a positive integer + - ``n`` -- an integer - OUTPUT: - - - a list of four numbers whose squares sum to n + OUTPUT: a tuple `(a,b,c)` of non-negative integers such that + `n = a^2 + b^2 + c^2` with `a <= b <= c`. EXAMPLES:: - sage: from sage.rings.arith import _brute_force_four_squares - sage: _brute_force_four_squares(567) - [1, 1, 6, 23] + sage: three_squares(389) + (7, 12, 14) + sage: three_squares(946) + (3, 19, 24) + sage: three_squares(2986) + (12, 21, 49) + sage: three_squares(7^100) + (0, 0, 1798465042647412146620280340569649349251249) + sage: three_squares(11^111-1) + (616274160655975340150706442680, 901582938385735143295060746161, 6270382387635744140394001363065311967964099981788593947233) + sage: three_squares(16 * 7) + Traceback (most recent call last): + ... + ValueError: 112 is not a sum of 3 squares + sage: three_squares(0) + (0, 0, 0) + sage: three_squares(-1) + Traceback (most recent call last): + ... + ValueError: -1 is not a sum of 3 squares + + ALGORITHM: + + See http://www.schorn.ch/howto.html """ - from math import sqrt - for i in range(0,int(sqrt(n))+1): - for j in range(i,int(sqrt(n-i**2))+1): - for k in range(j, int(sqrt(n-i**2-j**2))+1): - rem = n-i**2-j**2-k**2 - if rem >= 0: - l = int(sqrt(rem)) - if rem-l**2==0: - return [i,j,k,l] + from sage.rings.all import Integer + n = pari(n) + + if n <= 0: + if n == 0: + z = ZZ.zero() + return (z, z, z) + raise ValueError("%s is not a sum of 3 squares"%n) + + # First, remove all factors 4 from n + e = n.valuation(2)//2 + m = 2**e + N = n/(m*m) + m = Integer(m) + + # Let x be the largest integer at most sqrt(N) + x = N.sqrtint() + # We need to check for this special case, + # otherwise N - x^2 will always factor. + if x*x == N: + z = ZZ.zero() + return (z, z, x*m) + + # Consider different cases to find an x such that N - x^2 is easily + # written as the sum of 2 squares, because it is either p or 2p, + # with p a prime which is 1 mod 4. + if N % 4 == 1: + # Write N = x^2 + p with x even, p = 1 mod 4 prime + if x % 2 == 1: + x -= 1 + while x >= 0: + p = N - x*x + if p.ispseudoprime(): + break + x -= 2 + elif N % 4 == 2: + # Write N = x^2 + p with x odd, p = 1 mod 4 prime + if x % 2 == 0: + x -= 1 + while x >= 0: + p = N - x*x + if p.ispseudoprime(): + break + x -= 2 + elif N % 8 == 3: + # Write N = x^2 + 2p with x odd, p = 1 mod 4 prime + if x % 2 == 0: + x -= 1 + while x >= 0: + p = (N - x*x)/2 + if p.ispseudoprime(): + break + x -= 2 + else: # 7 mod 8 + raise ValueError("%s is not a sum of 3 squares"%n) + + if x < 0: + # We found no good x, brute force instead. + # Normally, this should only happen for small values of N. + if N > 10000: + from warnings import warn + warn("Brute forcing sum of 3 squares for large N = %s"%N, RuntimeWarning) + x = N.sqrtint() + + # In the usual case, this loop will only be executed once, since + # we already know the "right" value of x. + # This will only really loop if we hit the "x < 0" case above. + while True: + try: + a,b = two_squares(N - x*x) + break + except ValueError: + x -= 1 + assert x >= 0 + + x = Integer(x) + if x >= b: + return (a*m, b*m, x*m) + elif x >= a: + return (a*m, x*m, b*m) + else: + return (x*m, a*m, b*m) def four_squares(n): """ - Computes the decomposition into the sum of four squares, - using an algorithm described by Peter Schorn at: - http://www.schorn.ch/howto.html. + Write the integer `n` as a sum of four integer squares. INPUT: - - ``n`` - an integer - - OUTPUT: + - ``n`` -- an integer - - a list of four numbers whose squares sum to n + OUTPUT: a tuple `(a,b,c,d)` of non-negative integers such that + `n = a^2 + b^2 + c^2 + d^2` with `a <= b <= c <= d`. EXAMPLES:: sage: four_squares(3) - [0, 1, 1, 1] + (0, 1, 1, 1) + sage: four_squares(13) + (0, 0, 2, 3) sage: four_squares(130) - [0, 0, 3, 11] + (0, 0, 3, 11) sage: four_squares(1101011011004) - [2, 1049178, 2370, 15196] - sage: sum([i-sum([q^2 for q in four_squares(i)]) for i in range(2,10000)]) # long time - 0 + (130, 348, 1170, 1049290) + sage: four_squares(10^100-1) + (155024616290, 2612183768627, 14142135623730950488016887, 99999999999999999999999999999999999999999999999999) + sage: for i in range(10000): # long time + ....: S = four_squares(i) + ....: assert sum(x^2 for x in S) == i """ - from sage.rings.finite_rings.integer_mod import mod - from math import sqrt - from sage.rings.arith import _brute_force_four_squares - try: - ts = two_squares(n) - return [0,0,ts[0],ts[1]] - except ValueError: - pass - m = n - v = 0 - while mod(m,4) == 0: - v = v +1 - m = m // 4 - if mod(m,8) == 7: - d = 1 - m = m - 1 - else: - d = 0 - if mod(m,8)==3: - x = int(sqrt(m)) - if mod(x,2) == 0: - x = x - 1 - p = (m-x**2) // 2 - while not is_prime(p): - x = x - 2 - p = (m-x**2) // 2 - if x < 0: - # fall back to brute force - m = m + d - return [2**v*q for q in _brute_force_four_squares(m)] - y,z = two_squares(p) - return [2**v*q for q in [d,x,y+z,abs(y-z)]] - x = int(sqrt(m)) - p = m - x**2 - if p == 1: - return[2**v*q for q in [d,0,x,1]] - while not is_prime(p): - x = x - 1 - p = m - x**2 - if x < 0: - # fall back to brute force - m = m + d - return [2**v*q for q in _brute_force_four_squares(m)] - y,z = two_squares(p) - return [2**v*q for q in [d,x,y,z]] + from sage.rings.all import Integer + n = pari(n) + + if n <= 0: + if n == 0: + z = ZZ.zero() + return (z, z, z, z) + raise ValueError("%s is not a sum of 4 squares"%n) + + # First, remove all factors 4 from n + e = n.valuation(2)//2 + m = 2**e + N = n/(m*m) + m = Integer(m) + + # Subtract a suitable x^2 such that N - x^2 is 1,2,3,5,6 mod 8, + # which can then be written as a sum of 3 squares. + x = N.sqrtint() + y = N - x*x + if y >= 7 and (y % 4 == 0 or y % 8 == 7): + x -= 1 + y += 2*x + 1 + + a,b,c = three_squares(y) + + # Correct sorting is guaranteed by construction + x = Integer(x) + return (a*m, b*m, c*m, x*m) + +def sum_of_k_squares(k,n): + """ + Write the integer `n` as a sum of `k` integer squares if possible; + otherwise raise a ``ValueError``. + + INPUT: + + - ``k`` -- a non-negative integer + + - ``n`` -- an integer + + OUTPUT: a tuple `(x_1, ..., x_k)` of non-negative integers such that + their squares sum to `n`. + EXAMPLES:: + + sage: sum_of_k_squares(2, 9634) + (15, 97) + sage: sum_of_k_squares(3, 9634) + (0, 15, 97) + sage: sum_of_k_squares(4, 9634) + (1, 2, 5, 98) + sage: sum_of_k_squares(5, 9634) + (0, 1, 2, 5, 98) + sage: sum_of_k_squares(6, 11^1111-1) + (19215400822645944253860920437586326284, 37204645194585992174252915693267578306, 3473654819477394665857484221256136567800161086815834297092488779216863122, 5860191799617673633547572610351797996721850737768032876360978911074629287841061578270832330322236796556721252602860754789786937515870682024273948, 20457423294558182494001919812379023992538802203730791019728543439765347851316366537094696896669915675685581905102118246887673397020172285247862426612188418787649371716686651256443143210952163970564228423098202682066311189439731080552623884051737264415984619097656479060977602722566383385989, 311628095411678159849237738619458396497534696043580912225334269371611836910345930320700816649653412141574887113710604828156159177769285115652741014638785285820578943010943846225597311231847997461959204894255074229895666356909071243390280307709880906261008237873840245959883405303580405277298513108957483306488193844321589356441983980532251051786704380984788999660195252373574924026139168936921591652831237741973242604363696352878914129671292072201700073286987126265965322808664802662993006926302359371379531571194266134916767573373504566621665949840469229781956838744551367172353) + sage: sum_of_k_squares(7, 0) + (0, 0, 0, 0, 0, 0, 0) + sage: sum_of_k_squares(30,999999) + (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7, 44, 999) + sage: sum_of_k_squares(1, 9) + (3,) + sage: sum_of_k_squares(1, 10) + Traceback (most recent call last): + ... + ValueError: 10 is not a sum of 1 square + sage: sum_of_k_squares(1, -10) + Traceback (most recent call last): + ... + ValueError: -10 is not a sum of 1 square + sage: sum_of_k_squares(0, 9) + Traceback (most recent call last): + ... + ValueError: 9 is not a sum of 0 squares + sage: sum_of_k_squares(0, 0) + () + sage: sum_of_k_squares(7, -1) + Traceback (most recent call last): + ... + ValueError: -1 is not a sum of 7 squares + sage: sum_of_k_squares(-1, 0) + Traceback (most recent call last): + ... + ValueError: k = -1 must be non-negative + """ + from sage.rings.all import Integer + n = pari(n) + k = int(k) + + if k <= 4: + if k == 4: + return four_squares(n) + if k == 3: + return three_squares(n) + if k == 2: + return two_squares(n) + if k == 1: + if n >= 0: + x = n.sqrtint() + if n == x*x: + return (Integer(x),) + raise ValueError("%s is not a sum of 1 square"%n) + if k == 0: + if n == 0: + return tuple() + raise ValueError("%s is not a sum of 0 squares"%n) + raise ValueError("k = %s must be non-negative"%k) + + if n < 0: + raise ValueError("%s is not a sum of %s squares"%(n,k)) + + # Recursively subtract the largest square + t = [] + while k > 4: + x = n.sqrtint() + t.insert(0,Integer(x)) + n -= x*x + k -= 1 + + t = list(four_squares(n)) + t + return tuple(t) def subfactorial(n): r""" From 7a0f09406d13d2c95d97240e6a09725599b00bd5 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Sat, 10 May 2014 00:16:17 +0200 Subject: [PATCH 064/546] Introduced _cache_key for sage objects No meaningful hash function can be defined for elements which overwrite the operator == like the p-adics do since the contract on __hash__ would be broken: elements which compare equal would have different hash values. This would break caches and lead to subtle bugs. Still such elements should be cacheable for performance. As a workaround, such non-hashable elements can define a _cache_key which uniquely identifies them, and so fixes the "broken" == operator. --- .../rings/padics/padic_ZZ_pX_CR_element.pyx | 58 +++++++++++++++- src/sage/structure/sage_object.pyx | 69 +++++++++++++++++++ 2 files changed, 125 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx index 4b115f20a93..9580daff99e 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx @@ -163,15 +163,18 @@ NOTES:: AUTHORS: -- David Roe (2008-01-01) initial version +- David Roe (2008-01-01): initial version -- Robert Harron (2011-09) fixes/enhancements +- Robert Harron (2011-09): fixes/enhancements + +- Julian Rueth (2014-05-09): enable caching through ``_cache_key`` """ #***************************************************************************** # Copyright (C) 2008 David Roe # William Stein +# 2014 Julian Rueth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -469,6 +472,57 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): else: self._set_from_list_both(x, aprec, rprec) + def _cache_key(self): + r""" + Return a hashable key which uniquely identifies this element among all + elements in the parent. + + This enables caching for `p`-adic numbers which are not hashable. + + .. SEEALSO:: + + :meth:`sage.structure.sage_object.SageObject._cache_key` + + EXAMPLE: + + In the following example, ``a`` and ``b`` compare equal. They can not + have a meaningful hash value since then their hash value would have to + be the same:: + + sage: K. = Qq(9) + sage: b = a + O(3) + sage: a == b + True + sage: hash(a) + Traceback (most recent call last): + ... + TypeError: unhashable type: 'sage.rings.padics.padic_ZZ_pX_CR_element.pAdicZZpXCRElement' + + However, they should be cacheable, therefore they define a + ``_cache_key`` which is hashable and uniquely identifies them:: + + sage: a._cache_key() + (((0, 1),), 0, 20) + sage: b._cache_key() + (((0, 1),), 0, 1) + + TESTS: + + Check that zero values are handled correctly:: + + sage: K.zero()._cache_key() + 0 + sage: K(0,1)._cache_key() + (0, 1) + + """ + if self._is_exact_zero(): + return 0 + elif self._is_inexact_zero(): + return 0, self.valuation() + else: + return tuple(tuple(c) if isinstance(c,list) else c for c in self.unit_part().list()), self.valuation(), self.precision_relative() + cdef int _set_inexact_zero(self, long absprec) except -1: """ Sets ``self`` to be zero with valuation absprec. diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index 928978246a2..070bc71ccb5 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -221,6 +221,75 @@ cdef class SageObject: def __hash__(self): return hash(self.__repr__()) + def _cache_key(self): + r""" + Return a hashable key which uniquely identifies this objects for + caching. + + For most objects this will just be the object itself. However, some + immutable objects (such as `p`-adic numbers) can not implement a + reasonable hash function because their ``==`` operator has been + modified to return ``True`` for objects which might behave differently + in some computations:: + + sage: K. = Qq(9) + sage: b = a + O(3) + sage: c = a + 3 + sage: b + a + O(3) + sage: c + a + 3 + O(3^20) + sage: b == c + True + sage: b == a + True + sage: c == a + False + + If such objects defined a non-trivial hash function, this would break + caching in many places. However, such objects should still be + cacheable. This can be achieved by defining an appropriate + ``_cache_key``:: + + sage: hash(b) + Traceback (most recent call last): + ... + TypeError: unhashable type: 'sage.rings.padics.padic_ZZ_pX_CR_element.pAdicZZpXCRElement' + sage: @cached_method + ....: def f(x): return x==a + sage: f(b) + True + sage: f(c) # if b and c were hashable, this would return True + False + + sage: b._cache_key() + (((0, 1),), 0, 1) + sage: c._cache_key() + (((0, 1), (1,)), 0, 20) + + Note that such ``_cache_key`` often does not uniquely identify an + object in a strict sense:: + + sage: S. = Qq(4) + sage: d = a + O(2) + sage: b._cache_key() == d._cache_key() + True + + However, this kind of behaviour is common for many elements with + different parents. Special care has to be taken when mixing such + elements in caches:: + + sage: A = matrix([[1]],base_ring=GF(2)) + sage: A.set_immutable() + sage: B = matrix([[1]],base_ring=GF(3)) + sage: B.set_immutable() + sage: A == B + True + sage: hash(A) == hash(B) + True + + """ + return self ############################################################################# # DATABASE Related code From def8ae489294f459dcb73976a3c13172f62c6532 Mon Sep 17 00:00:00 2001 From: Andrey Novoseltsev Date: Sun, 17 Mar 2013 05:02:30 +0000 Subject: [PATCH 065/546] Add a module for interactive simplex method exploration. --- .../numerical/interactive_simplex_method.py | 3916 +++++++++++++++++ 1 file changed, 3916 insertions(+) create mode 100644 src/sage/numerical/interactive_simplex_method.py diff --git a/src/sage/numerical/interactive_simplex_method.py b/src/sage/numerical/interactive_simplex_method.py new file mode 100644 index 00000000000..e025421b77c --- /dev/null +++ b/src/sage/numerical/interactive_simplex_method.py @@ -0,0 +1,3916 @@ +r""" +Interactive Simplex Method + +The purpose of this module is to support learning and exploring of the simplex +method. While it does allow solving Linear Programming Problems (LPPs) in a +number of ways, it may require explicit (and correct!) description of steps and +is likely to be much slower than "regular" LP solvers. Therefore, do not +use this module if all you want is the result. If, however, you want to +learn how the simplex method works and see what happens in different situations +using different strategies, but don't want to deal with tedious arithmetic, +this module is for you! + +Historically it was created to complement Math 373 course on Mathematical +Programming and Optimization at the University of Alberta, Edmonton, Canada. + +AUTHORS: + +- Andrey Novoseltsev (2013-03-16): initial version. + +EXAMPLES: + +Most of the module functionality is demonstrated on the following problem. + +.. admonition:: Corn & Barley + + A farmer has 1000 acres available to grow corn and barley. + Corn has a net profit of 10 dollars per acre while barley has a net profit + of 5 dollars per acre. + The farmer has 1500 kg of fertilizer available with 3 kg per acre needed for + corn and 1 kg per acre needed for barley. + The farmer wants to maximize profit. + (Sometimes we also add one more constraint to make the initial dictionary + infeasible: the farmer has to use at least 40% of the available land.) + +Using variables `C` and `B` for land used to grow corn and barley respectively, +in acres, we can construct the following LP problem:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P + LP problem (use typeset mode to see details) + +It is recommended to copy-paste such examples into your own worksheet, so that +you can run these commands with typeset mode on and get + +.. math:: + + \begin{array}{l} + \begin{array}{lcrcrcl} + \max \!\!\!&\!\!\! \!\!\!&\!\!\! 10 C \!\!\!&\!\!\! + \!\!\!&\!\!\! 5 B \!\!\! \\ + \!\!\!&\!\!\! \!\!\!&\!\!\! C \!\!\!&\!\!\! + \!\!\!&\!\!\! B \!\!\!&\!\!\! \leq \!\!\!&\!\!\! 1000 \\ + \!\!\!&\!\!\! \!\!\!&\!\!\! 3 C \!\!\!&\!\!\! + \!\!\!&\!\!\! B \!\!\!&\!\!\! \leq \!\!\!&\!\!\! 1500 \\ + \end{array} \\ + C, B \geq 0 + \end{array} + +Since it has only two variables, we can solve it graphically:: + + sage: P.plot() # long time + + +The simplex method can be applied only to :class:`problems in standard form +`, which can be created either directly :: + + sage: LPProblemStandardForm(A, b, c, ["C", "B"]) + LP problem (use typeset mode to see details) + +or from an already constructed problem of "general type" :: + + sage: P = P.standard_form() + +In this case the problem does not require any modifications to be written in +standard form, but this step is still necessary to enable methods related to +the simplex method. + +The simplest way to use the simplex method is :: + + sage: P.run_simplex_method() + '...' + +(This method produces quite long formulas which have been omitted here.) +But, of course, it is much more fun to do most of the steps by hand. Let's start +by creating the initial dictionary:: + + sage: D = P.initial_dictionary() + sage: D + LP problem dictionary (use typeset mode to see details) + +Using typeset mode as recommended, you'll see + +.. math:: + + \renewcommand{\arraystretch}{1.5} + \begin{array}{|rcrcrcr|} + \hline + x_{3} \!\!\!&\!\!\! = \!\!\!&\!\!\! 1000 \!\!\!&\!\!\! - \!\!\!&\!\!\! C \!\!\!&\!\!\! - \!\!\!&\!\!\! B\\ + x_{4} \!\!\!&\!\!\! = \!\!\!&\!\!\! 1500 \!\!\!&\!\!\! - \!\!\!&\!\!\! 3 C \!\!\!&\!\!\! - \!\!\!&\!\!\! B\\ + \hline + z \!\!\!&\!\!\! = \!\!\!&\!\!\! 0 \!\!\!&\!\!\! + \!\!\!&\!\!\! 10 C \!\!\!&\!\!\! + \!\!\!&\!\!\! 5 B\\ + \hline + \end{array} + +With the initial or any other dictionary you can perform a number of checks:: + + sage: D.is_feasible() + True + sage: D.is_optimal() + False + +You can look at many of its pieces and associated data:: + + sage: D.basic_variables() + (x3, x4) + sage: D.basic_solution() + (0, 0) + sage: D.objective_value() + 0 + +Most importantly, you can perform steps of the simplex method by picking an +entering variable, a leaving variable, and updating the dictionary:: + + sage: D.enter("C") + sage: D.leave(4) + sage: D.update() + +If everything was done correctly, the new dictionary is still feasible and the +objective value did not decrease:: + + sage: D.is_feasible() + True + sage: D.objective_value() + 5000 + +If you are unsure about picking entering and leaving variables, you can use +helper methods that will try their best to tell you what are your next options:: + + sage: D.possible_entering() + [B] + sage: D.possible_leaving() + Traceback (most recent call last): + ... + ValueError: leaving variables can be determined + for feasible dictionaries with a set entering variable + or for dual feasible dictionaries + +It is also possible to obtain :meth:`feasible sets ` and +:meth:`final dictionaries ` of problems, +work with :class:`revised dictionaries `, and use the dual +simplex method! +""" + + +#***************************************************************************** +# Copyright (C) 2013 Andrey Novoseltsev +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + + +import re, sys + + +from copy import copy + + +from sage.geometry.all import Polyhedron +from sage.matrix.all import (column_matrix, + identity_matrix, + is_Matrix, + matrix, + random_matrix) +from sage.misc.all import (LatexExpr, + cached_function, + cached_method, + latex, + randint, + random) +from sage.misc.latex import EMBEDDED_MODE +from sage.modules.all import random_vector, vector +from sage.plot.all import Graphics, arrow, line, point, rainbow, text +from sage.rings.all import Infinity, PolynomialRing, QQ, RDF, ZZ +from sage.structure.all import SageObject +from sage.symbolic.all import SR + + +# We produce rather complicated LaTeX code which needs some tweaks to be +# displayed nicely by MathJax, which make it look slightly different from real +# LaTeX. We use our own variable as it may be convenient to override it. +# Hopefully, some day there will be no need in it at all and only "if" parts +# will have to be left. +generate_real_LaTeX = not EMBEDDED_MODE + + +def _assemble_arrayl(lines, stretch=None): + r""" + Return ``lines`` assembled in a left-justified array. + + INPUT: + + - ``lines`` -- a list of strings suitable for math mode typesetting; + + - ``stretch`` -- (default: None) if given, a command setting + ``\arraystretch`` to this value will be added before the array. + + OUTPUT: + + - a :class:`LatexExpr`. + + EXAMPLES:: + + sage: from sage.numerical.interactive_simplex_method \ + ... import _assemble_arrayl + sage: lines = ["1 + 1", "2"] + sage: print _assemble_arrayl(lines) + \begin{array}{l} + 1 + 1\\ + 2 + \end{array} + sage: print _assemble_arrayl(lines, 1.5) + \renewcommand{\arraystretch}{1.500000} + \begin{array}{l} + 1 + 1\\ + 2 + \end{array} + """ + # Even simple LP problems tend to generate long output, so we prohibit + # truncation in the notebook cells and hope for the best! + return LatexExpr(("" if generate_real_LaTeX else "%notruncate\n") + + ("" if stretch is None else + "\\renewcommand{\\arraystretch}{%f}\n" % stretch) + + "\\begin{array}{l}\n" + + "\\\\\n".join(lines) + + "\n\\end{array}") + + +def _latex_product(coefficients, variables, + separator=None, head=None, tail=None, + drop_plus=True, allow_empty=False): + r""" + Generate LaTeX code for a linear function. + + This function is intended for internal use by LaTeX methods of LP problems + and their dictionaries. + + INPUT: + + - ``coefficients`` -- a list of coefficients; + + - ``variables`` -- a list of variables; + + - ``separator`` -- (default: "&" with some extra space adjustment) a string + to be inserted between elements of the generated expression; + + - ``head`` -- either ``None`` (default) or a list of entries to be added to + the beginning of the output; + + - ``tail`` -- either ``None`` (default) or a list of entries to be added to + the end of the output; + + - ``drop_plus`` -- (default: ``True``) whether to drop the leading plus + sign or not; + + - ``allow_empty`` -- (default: ``False``) whether to allow empty output or + produce at least "0". + + OUTPUT: + + - a strings joining ``head``, each sign and coefficient-variable product, + and ``tail`` using ``separator``. Strings in ``head`` and ``tail`` are + used as is except for "<=", "==", and ">=", which are replaced by LaTeX + commands. Other elements in ``head`` in ``tail`` are processed by + :func:`latex`. + + TESTS:: + + sage: from sage.numerical.interactive_simplex_method import \ + ... _latex_product + sage: var("x, y") + (x, y) + sage: print _latex_product([-1, 3], [x, y]) + - & x & + & 3 \, y + """ + entries = [] + for c, v in zip(coefficients, variables): + t = latex(c * v) + if t == "0": + entries.extend(["", ""]) + elif t.startswith("-"): + entries.extend(["-", t[1:]]) + else: + entries.extend(["+", t]) + if drop_plus: # Don't start with + + for i, e in enumerate(entries): + if e: # The first non-empty + if e == "+": + entries[i] = "" + break + if not (allow_empty or any(entries)): # Return at least 0 + entries[-1] = "0" + latex_relations = {"<=": r"\leq", "==": "=", ">=": r"\geq"} + if head is not None: + for e in reversed(head): + if not isinstance(e, str): + e = latex(e) + elif e in latex_relations: + e = latex_relations[e] + entries.insert(0, e) + if tail is not None: + for e in tail: + if not isinstance(e, str): + e = latex(e) + elif e in latex_relations: + e = latex_relations[e] + entries.append(e) + if separator is None: + if generate_real_LaTeX: + separator = " & " + else: + separator = r" \!\!\!&\!\!\! " + return separator.join(entries) + + +@cached_function +def variable(R, v): + r""" + Interpret ``v`` as a variable of ``R``. + + INPUT: + + - ``R`` -- a polynomial ring; + + - ``v`` -- a variable of ``R`` or convertible into ``R``, a string + with the name of a variable of ``R`` or an index of a variable in + ``R``. + + OUTPUT: + + - a variable of ``R``. + + EXAMPLES:: + + sage: from sage.numerical.interactive_simplex_method \ + ... import variable + sage: R = PolynomialRing(QQ, "x3, y5, x5, y") + sage: R.inject_variables() + Defining x3, y5, x5, y + sage: variable(R, "x3") + x3 + sage: variable(R, x3) + x3 + sage: variable(R, 3) + x3 + sage: variable(R, 0) + Traceback (most recent call last): + ... + ValueError: there is no variable with the given index + sage: variable(R, 5) + Traceback (most recent call last): + ... + ValueError: the given index is ambiguous + sage: variable(R, 2 * x3) + Traceback (most recent call last): + ... + ValueError: cannot interpret given data as a variable + sage: variable(R, "z") + Traceback (most recent call last): + ... + ValueError: cannot interpret given data as a variable + """ + if v in ZZ: + v = str(v) + tail = re.compile(r"\d+$") + matches = [] + for g in R.gens(): + match = tail.search(str(g)) + if match is not None and match.group() == v: + matches.append(g) + if not matches: + raise ValueError("there is no variable with the given index") + if len(matches) > 1: + raise ValueError("the given index is ambiguous") + return matches[0] + else: + try: + v = R(v) + if v in R.gens(): + return v + except TypeError: + pass + raise ValueError("cannot interpret given data as a variable") + + +class LPProblem(SageObject): + r""" + Construct an LP (Linear Programming) problem. + + This class supports LP problems with "variables on the left" constraints. + + INPUT: + + - ``A`` -- a matrix of constraint coefficients; + + - ``b`` -- a vector of constraint constant terms; + + - ``c`` -- a vector of objective coefficients; + + - ``x`` -- (default: "x") a vector of decision variables or a string giving + the base name; + + - ``constraint_type`` -- (default: "<=") a string specifying constraint + type(s): either "<=", or ">=", or "==", or a list of them; + + - ``variable_type`` -- (default: ">=") a string specifying variable type(s): + either ">=", or "<=", or "" (empty string), or a list of them; + + - ``problem_type`` -- (default: "max") a string specifying the problem type: + "max", "min", "-max", or "-min"; + + - ``prefix`` -- (default: parameter ``x`` if it was given as a string, + string "x" otherwise) a string giving the base name of automatically + created variables in :meth:`standard_form`. + + OUTPUT: + + - an :class:`LPProblem`. + + EXAMPLES: + + We will construct the following problem: + + .. math:: + + \begin{array}{l} + \begin{array}{lcrcrcl} + \max \!\!\!&\!\!\! \!\!\!&\!\!\! 10 C \!\!\!&\!\!\! + \!\!\!&\!\!\! 5 B \!\!\! \\ + \!\!\!&\!\!\! \!\!\!&\!\!\! C \!\!\!&\!\!\! + \!\!\!&\!\!\! B \!\!\!&\!\!\! \leq \!\!\!&\!\!\! 1000 \\ + \!\!\!&\!\!\! \!\!\!&\!\!\! 3 C \!\!\!&\!\!\! + \!\!\!&\!\!\! B \!\!\!&\!\!\! \leq \!\!\!&\!\!\! 1500 \\ + \end{array} \\ + C, B \geq 0 + \end{array} + + :: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + + Same problem, but more explicitly:: + + sage: P = LPProblem(A, b, c, ["C", "B"], + ... constraint_type="<=", variable_type=">=") + + Even more explicitly:: + + sage: P = LPProblem(A, b, c, ["C", "B"], problem_type="max", + ... constraint_type=["<=", "<="], variable_type=[">=", ">="]) + + Using the last form you should be able to represent any LP problem, as long + as all like terms are collected and in constraints variables and constants + are on different sides. + """ + + def __init__(self, A, b, c, x="x", + constraint_type="<=", variable_type=">=", problem_type="max", + prefix="x"): + r""" + See :class:`LPProblem` for documentation. + + TESTS:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: TestSuite(P).run() + """ + super(LPProblem, self).__init__() + if not is_Matrix(A): + A = matrix(A) + b = vector(b) + c = vector(c) + F = vector(A.list() + list(b) + list(c)).base_ring().fraction_field() + A = A.base_extend(F) + A.set_immutable() + m, n = A.nrows(), A.ncols() + b = b.base_extend(F) + b.set_immutable() + if b.degree() != m: + raise ValueError("A and b have incompatible dimensions") + c = c.base_extend(F) + c.set_immutable() + if c.degree() != n: + raise ValueError("A and c have incompatible dimensions") + if isinstance(x, str): + prefix = x + x = ["{}{:d}".format(x, i) for i in range(1, n+1)] + else: + x = map(str, x) + if len(x) != n: + raise ValueError("A and x have incompatible dimensions") + R = PolynomialRing(F, x, order="neglex") + x = vector(R, R.gens()) # All variables as a vector + self._Abcx = A, b, c, x + + if constraint_type in ["<=", ">=", "=="]: + constraint_type = (constraint_type, ) * m + else: + constraint_type = tuple(constraint_type) + if any(ct not in ["<=", ">=", "=="] for ct in constraint_type): + raise ValueError("unknown constraint type") + if len(constraint_type) != m: + raise ValueError("wrong number of constraint types") + self._constraint_types = constraint_type + + if variable_type in ["<=", ">=", ""]: + variable_type = (variable_type, ) * n + else: + variable_type = tuple(variable_type) + if any(vt not in ["<=", ">=", ""] for vt in variable_type): + raise ValueError("unknown variable type") + if len(variable_type) != n: + raise ValueError("wrong number of variable types") + self._variable_types = variable_type + + if problem_type.startswith("-"): + self._is_negative = True + problem_type = problem_type[1:] + else: + self._is_negative = False + if problem_type not in ["max", "min"]: + raise ValueError("unknown problem type") + self._problem_type = problem_type + + self._prefix = prefix + + def __eq__(self, other): + r""" + Check if two LP problems are equal. + + INPUT: + + - ``other`` -- anything. + + OUTPUT: + + - ``True`` if ``other`` is an :class:`LPProblem` with all details the + same as ``self``, ``False`` otherwise. + + TESTS:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P2 = LPProblem(A, b, c, ["C", "B"]) + sage: P == P2 + True + sage: P3 = LPProblem(A, c, b, ["C", "B"]) + sage: P == P3 + False + """ + return (isinstance(other, LPProblem) and + self.Abcx() == other.Abcx() and + self._problem_type == other._problem_type and + self._is_negative == other._is_negative and + self._constraint_types == other._constraint_types and + self._variable_types == other._variable_types and + self._prefix == other._prefix) + + def _latex_(self): + r""" + Return a LaTeX representation of ``self``. + + OUTPUT: + + - a string. + + TESTS:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: print P._latex_() + \begin{array}{l} \setlength{\arraycolsep}{0.125em} + \begin{array}{lcrcrcl} + \max & & 10 C & + & 5 B\\ + & & C & + & B & \leq & 1000 \\ + & & 3 C & + & B & \leq & 1500 \\ + \end{array} \\ + C, B \geq 0 + \end{array} + """ + A, b, c, x = self.Abcx() + lines = [] + lines.append(r"\begin{array}{l}") + if generate_real_LaTeX: + lines[-1] += r" \setlength{\arraycolsep}{0.125em}" + lines.append(r"\begin{array}{l" + "cr" * len(x) + "cl}") + head = [r"{} \{}".format("- " if self._is_negative else "", + self._problem_type)] + lines.append(_latex_product(c, x, head=head) + + (r"\\" if generate_real_LaTeX else r" \!\!\! \\")) + for Ai, ri, bi in zip(A.rows(), self._constraint_types, b): + lines.append(_latex_product(Ai, x, head=[""], tail=[ri, bi]) + + r" \\") + lines.append(r"\end{array} \\") + if set(self._variable_types) == set([">="]): + lines.append(r"{} \geq 0".format(", ".join(map(latex, x)))) + else: + lines.append(r",\ ".join(r"{} {} 0".format( + latex(xj), r"\geq" if vt == ">=" else r"\leq") + for xj, vt in zip(x, self._variable_types) if vt)) + lines.append(r"\end{array}") + return "\n".join(lines) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + OUTPUT: + + - a string. + + TESTS:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: print P._repr_() + LP problem (use typeset mode to see details) + """ + return "LP problem (use typeset mode to see details)" + + @cached_method + def _solve(self): + r""" + Return an optimal solution and the optimal value of ``self``. + + OUTPUT: + + - a pair consisting of a vector and a number. If the problem is + infeasible, both components are ``None``. If the problem is unbounded, + the first component is ``None`` and the second is `\pm \infty`. + + This function uses "brute force" solution technique of evaluating the + objective at all vertices of the feasible set and taking into account + its rays and lines. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P._solve() + ((250, 750), 6250) + """ + F = self.feasible_set() + R = self.base_ring() + A, b, c, x = self._Abcx + if F.n_vertices() == 0: + return (None, None) + elif c.is_zero(): + M, S = 0, F.vertices()[0] + elif self._problem_type == "max": + if any(c * vector(R, ray) > 0 for ray in F.rays()) or \ + any(c * vector(R, line) != 0 for line in F.lines()): + M, S = Infinity, None + else: + M, S = max((c * vector(R, v), v) for v in F.vertices()) + elif self._problem_type == "min": + if any(c * vector(R, ray) < 0 for ray in F.rays()) or \ + any(c * vector(R, line) != 0 for line in F.lines()): + M, S = -Infinity, None + else: + M, S = min((c * vector(R, v), v) for v in F.vertices()) + if self._is_negative: + M = - M + if S is not None: + S = vector(R, S) + S.set_immutable() + return S, M + + def Abcx(self): + r""" + Return `A`, `b`, `c`, and `x` of ``self`` as a tuple. + + OUTPUT: + + - a tuple. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P.Abcx() + ( + [1 1] + [3 1], (1000, 1500), (10, 5), (C, B) + ) + """ + return self._Abcx + + def base_ring(self): + r""" + Return the base ring of ``self``. + + .. note:: + + The base ring of LP problems is always a field. + + OUTPUT: + + - a ring. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P.base_ring() + Rational Field + + sage: c = (10, 5.) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P.base_ring() + Real Field with 53 bits of precision + """ + return self._Abcx[0].base_ring() + + def constant_terms(self): + r""" + Return constant terms of constraints of ``self``, i.e. `b`. + + OUTPUT: + + - a vector. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P.constant_terms() + (1000, 1500) + sage: P.b() + (1000, 1500) + """ + return self._Abcx[1] + + def constraint_coefficients(self): + r""" + Return coefficients of constraints of ``self``, i.e. `A`. + + OUTPUT: + + - a matrix. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P.constraint_coefficients() + [1 1] + [3 1] + sage: P.A() + [1 1] + [3 1] + """ + return self._Abcx[0] + + def decision_variables(self): + r""" + Return decision variables of ``self``, i.e. `x`. + + OUTPUT: + + - a vector. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P.decision_variables() + (C, B) + sage: P.x() + (C, B) + """ + return self._Abcx[3] + + def dual(self, y=None): + r""" + Construct the dual LP problem for ``self``. + + INPUT: + + - ``y`` -- (default: "x" if the prefix of ``self`` is "y", "y" + otherwise) a vector of dual decision variables or a string giving the + base name. + + OUTPUT: + + - an :class:`LPProblem`. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: DP = P.dual() + sage: DP.b() == P.c() + True + sage: DP.dual(["C", "B"]) == P + True + """ + A, c, b, x = self.Abcx() + A = A.transpose() + if y is None: + y = "x" if self._prefix == "y" else "y" + problem_type = "min" if self._problem_type == "max" else "max" + constraint_type = [] + for vt in self._variable_types: + if (vt == ">=" and problem_type == "min" or + vt == "<=" and problem_type == "max"): + constraint_type.append(">=") + elif (vt == "<=" and problem_type == "min" or + vt == ">=" and problem_type == "max"): + constraint_type.append("<=") + else: + constraint_type.append("==") + variable_type = [] + for ct in self._constraint_types: + if (ct == ">=" and problem_type == "min" or + ct == "<=" and problem_type == "max"): + variable_type.append("<=") + elif (ct == "<=" and problem_type == "min" or + ct == ">=" and problem_type == "max"): + variable_type.append(">=") + else: + variable_type.append("") + if self._is_negative: + problem_type = "-" + problem_type + return LPProblem(A, b, c, y, + constraint_type, variable_type, problem_type) + + @cached_method + def feasible_set(self): + r""" + Return the feasible set of ``self``. + + OUTPUT: + + - a :mod:`Polyhedron `. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P.feasible_set() + A 2-dimensional polyhedron in QQ^2 + defined as the convex hull of 4 vertices + """ + ieqs = [] + eqns = [] + for a, r, b in zip(self.A().rows(), self._constraint_types, self.b()): + if r == "<=": + ieqs.append([b] + list(-a)) + elif r == ">=": + ieqs.append([-b] + list(a)) + else: + eqns.append([-b] + list(a)) + for n, r in zip(identity_matrix(self.n()).rows(), self._variable_types): + if r == "<=": + ieqs.append([0] + list(-n)) + elif r == ">=": + ieqs.append([0] + list(n)) + if self.base_ring() is QQ: + R = QQ + else: + R = RDF + ieqs = [map(R, ieq) for ieq in ieqs] + eqns = [map(R, eqn) for eqn in eqns] + return Polyhedron(ieqs=ieqs, eqns=eqns, base_ring=R) + + def is_bounded(self): + r""" + Check if ``self`` is bounded. + + OUTPUT: + + - ``True`` is ``self`` is bounded, ``False`` otherwise. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P.is_bounded() + True + """ + return self._solve()[0] is not None + + def is_feasible(self): + r""" + Check if ``self`` is feasible. + + OUTPUT: + + - ``True`` is ``self`` is feasible, ``False`` otherwise. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P.is_feasible() + True + """ + return self._solve()[1] is not None + + def n_constraints(self): + r""" + Return the number of constraints of ``self``, i.e. `m`. + + OUTPUT: + + - an integer. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P.n_constraints() + 2 + sage: P.m() + 2 + """ + return self._Abcx[0].nrows() + + def n_variables(self): + r""" + Return the number of decision variables of ``self``, i.e. `n`. + + OUTPUT: + + - an integer. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P.n_variables() + 2 + sage: P.n() + 2 + """ + return self._Abcx[0].ncols() + + def objective_coefficients(self): + r""" + Return coefficients of the objective of ``self``, i.e. `c`. + + OUTPUT: + + - a vector. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P.objective_coefficients() + (10, 5) + sage: P.c() + (10, 5) + """ + return self._Abcx[2] + + def optimal_solution(self): + r""" + Return **an** optimal solution of ``self``. + + OUTPUT: + + - a vector or ``None`` if there are no optimal solutions. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P.optimal_solution() + (250, 750) + """ + return self._solve()[0] + + def optimal_value(self): + r""" + Return the optimal value for ``self``. + + OUTPUT: + + - a number if the problem is bounded, `\pm \infty` if it is unbounded, + or ``None`` if it is infeasible. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P.optimal_value() + 6250 + """ + return self._solve()[1] + + def plot(self, *args, **kwds): + r""" + Return a plot for solving ``self`` graphically. + + INPUT: + + - same as for :meth:`plot_feasible_set`. + + OUTPUT: + + - a plot. + + This only works for problems with two decision variables. On the plot + the black arrow indicates the direction of growth of the objective. The + lines perpendicular to it are level curves of the objective. If there + are optimal solutions, the arrow originates in one of them and the + corresponding level curve is solid: all points of the feasible set on it + are optimal solutions. Otherwise the arrow is placed in the center. If + the problem is infeasible or the objective is zero, a plot of the + feasible set only is returned. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: p = P.plot() + sage: p.show() # long time + + In this case the plot works better with the following axes ranges:: + + sage: p = P.plot(0, 1000, 0, 1500) + sage: p.show() # long time + + TESTS: + + We check that zero objective can be dealt with:: + + sage: LPProblem(A, b, (0, 0), ["C", "B"]).plot() + """ + FP = self.plot_feasible_set(*args, **kwds) + c = self.c().n().change_ring(QQ) + if c.is_zero(): + return FP + xmin = FP.xmin() + xmax = FP.xmax() + ymin = FP.ymin() + ymax = FP.ymax() + xmin, xmax, ymin, ymax = map(QQ, [xmin, xmax, ymin, ymax]) + start = self.optimal_solution() + start = vector(QQ, start.n() if start is not None + else [xmin + (xmax-xmin)/2, ymin + (ymax-ymin)/2]) + length = min(xmax - xmin, ymax - ymin) / 5 + end = start + (c * length / c.norm()).n().change_ring(QQ) + result = FP + point(start, color="black", size=50, zorder=10) + result += arrow(start, end, color="black", zorder=10) + ieqs = [(xmax, -1, 0), (- xmin, 1, 0), + (ymax, 0, -1), (- ymin, 0, 1)] + box = Polyhedron(ieqs=ieqs) + d = vector([c[1], -c[0]]) + for i in range(-10, 11): + level = Polyhedron(vertices=[start + i*(end-start)], lines=[d]) + level = box.intersection(level) + if level.vertices(): + if i == 0 and self.is_bounded(): + result += line(level.vertices(), color="black", + thickness=2) + else: + result += line(level.vertices(), color="black", + linestyle="--") + result.set_axes_range(xmin, xmax, ymin, ymax) + result.axes_labels(FP.axes_labels()) #FIXME: should be preserved! + return result + + def plot_feasible_set(self, xmin=None, xmax=None, ymin=None, ymax=None, + alpha=0.2): + r""" + Return a plot of the feasible set of ``self``. + + INPUT: + + - ``xmin``, ``xmax``, ``ymin``, ``ymax`` -- bounds for the axes, if + not given, an attempt will be made to pick reasonable values; + + - ``alpha`` -- (default: 0.2) determines how opaque are shadows. + + OUTPUT: + + - a plot. + + This only works for a problem with two decision variables. The plot + shows boundaries of constraints with a shadow on one side for + inequalities. If the :meth:`feasible_set` is not empty and at least part + of it is in the given boundaries, it will be shaded gray and `F` will be + placed in its middle. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: p = P.plot_feasible_set() + sage: p.show() # long time + + In this case the plot works better with the following axes ranges:: + + sage: p = P.plot_feasible_set(0, 1000, 0, 1500) + sage: p.show() # long time + """ + if self.n() != 2: + raise ValueError("only problems with 2 variables can be plotted") + A, b, c, x = self.Abcx() + if self.base_ring() is not QQ: + # Either we use QQ or crash + A = A.n().change_ring(QQ) + b = b.n().change_ring(QQ) + F = self.feasible_set() + if ymax is None: + ymax = max(map(abs, b) + [v[1] for v in F.vertices()]) + if ymin is None: + ymin = min([-ymax/4.0] + [v[1] for v in F.vertices()]) + if xmax is None: + xmax = max([1.5*ymax] + [v[0] for v in F.vertices()]) + if xmin is None: + xmin = min([-xmax/4.0] + [v[0] for v in F.vertices()]) + xmin, xmax, ymin, ymax = map(QQ, [xmin, xmax, ymin, ymax]) + pad = max(xmax - xmin, ymax - ymin) / 20 + ieqs = [(xmax, -1, 0), (- xmin, 1, 0), + (ymax, 0, -1), (- ymin, 0, 1)] + box = Polyhedron(ieqs=ieqs) + F = box.intersection(F) + result = Graphics() + colors = rainbow(self.m() + 2) + for Ai, ri, bi, color in zip(A.rows(), self._constraint_types, + b, colors[:-2]): + border = box.intersection(Polyhedron(eqns=[[-bi] + list(Ai)])) + vertices = border.vertices() + if not vertices: + continue + label = r"${}$".format(_latex_product(Ai, x, " ", tail=[ri, bi])) + result += line(vertices, color=color, legend_label=label) + if ri == "<=": + ieqs = [[bi] + list(-Ai), [-bi+pad*Ai.norm().n()] + list(Ai)] + elif ri == ">=": + ieqs = [[-bi] + list(Ai), [bi+pad*Ai.norm().n()] + list(-Ai)] + else: + continue + ieqs = map(lambda ieq: map(QQ, ieq), ieqs) + halfplane = box.intersection(Polyhedron(ieqs=ieqs)) + result += halfplane.render_solid(alpha=alpha, color=color) + # Same for variables, but no legend + for ni, ri, color in zip((QQ**2).gens(), self._variable_types, + colors[-2:]): + border = box.intersection(Polyhedron(eqns=[[0] + list(ni)])) + if not border.vertices(): + continue + if ri == "<=": + ieqs = [[0] + list(-ni), [pad] + list(ni)] + elif ri == ">=": + ieqs = [[0] + list(ni), [pad] + list(-ni)] + else: + continue + ieqs = map(lambda ieq: map(QQ, ieq), ieqs) + halfplane = box.intersection(Polyhedron(ieqs=ieqs)) + result += halfplane.render_solid(alpha=alpha, color=color) + if F.vertices(): + result += F.render_solid(alpha=alpha, color="gray") + result += text("$F$", F.center(), + fontsize=20, color="black", zorder=5) + result.set_axes_range(xmin, xmax, ymin, ymax) + result.axes_labels(map(lambda xi: "${}$".format(latex(xi)), x)) + result.legend(True) + result.set_legend_options(fancybox=True, handlelength=1.5, loc=1, + shadow=True) + result._extra_kwds["aspect_ratio"] = 1 + result.set_aspect_ratio(1) + return result + + def standard_form(self): + r""" + Construct the LP problem in standard form equivalent to ``self``. + + OUTPUT: + + - an :class:`LPProblemStandardForm`. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: DP = P.dual() + sage: DPSF = DP.standard_form() + sage: DPSF.b() + (-10, -5) + """ + A, b, c, x = self.Abcx() + if not all(ct == "<=" for ct in self._constraint_types): + newA = [] + newb = [] + for ct, Ai, bi in zip(self._constraint_types, A, b): + if ct in ["<=", "=="]: + newA.append(Ai) + newb.append(bi) + if ct in [">=", "=="]: + newA.append(-Ai) + newb.append(-bi) + A = matrix(newA) + b = vector(newb) + if not all(vt == ">=" for vt in self._variable_types): + newA = [] + newc = [] + newx = [] + for vt, Aj, cj, xj in zip(self._variable_types, A.columns(), c, x): + xj = str(xj) + if vt in [">=", ""]: + newA.append(Aj) + newc.append(cj) + if vt == ">=": + newx.append(xj) + if vt == "": + newx.append(xj + "_p") + if vt in ["<=", ""]: + newA.append(-Aj) + newc.append(-cj) + newx.append(xj + "_n") + A = column_matrix(newA) + c = vector(newc) + x = newx + is_negative = self._is_negative + if self._problem_type == "min": + is_negative = not is_negative + c = - c + problem_type = "-max" if is_negative else "max" + return LPProblemStandardForm(A, b, c, x, problem_type, + self._prefix, self._prefix + "0") + + # Aliases for the standard notation + A = constraint_coefficients + b = constant_terms + c = objective_coefficients + x = decision_variables + m = n_constraints + n = n_variables + + +class LPProblemStandardForm(LPProblem): + r""" + Construct an LP (Linear Programming) problem in standard form. + + The used standard form is: + + .. math:: + + \begin{array}{l} + \pm \max cx \\ + Ax \leq b \\ + x \geq 0 + \end{array} + + INPUT: + + - ``A`` -- a matrix of constraint coefficients; + + - ``b`` -- a vector of constraint constant terms; + + - ``c`` -- a vector of objective coefficients; + + - ``x`` -- (default: "x") a vector of decision variables or a string giving + the base name; + + - ``problem_type`` -- (default: "max") a string specifying the problem type: + either "max" or "-max"; + + - ``slack_variables`` -- (default: same as ``x`` parameter, if it was given + as a string, otherwise string "x") a vector of slack variables or a sting + giving the base name; + + - ``auxiliary_variable`` -- (default: same as ``x`` parameter with adjoined + "0" if it was given as a string, otherwise "x0") the auxiliary variable + name, expected to be the same as the first decision variable for auxiliary + problems; + + - ``objective`` -- (default: "z") the objective variable (used for the + initial dictionary). + + OUTPUT: + + - an :class:`LPProblemStandardForm`. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + + Unlike :class:`LPProblem`, this class does not allow you to adjust types of + constraints (they are always "<=") and variables (they are always ">="), and + the problem type may only be "max" or "-max". You may give custom names to + slack and auxiliary variables, but in most cases defaults should work:: + + sage: P.decision_variables() + (x1, x2) + sage: P.slack_variables() + (x3, x4) + """ + + def __init__(self, A, b, c, x="x", problem_type="max", + slack_variables=None, auxiliary_variable=None, objective="z"): + r""" + See :class:`StandardFormLPP` for documentation. + + TESTS:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: TestSuite(P).run() + """ + if problem_type not in ("max", "-max"): + raise ValueError("problems in standard form must be of (negative) " + "maximization type") + super(LPProblemStandardForm, self).__init__(A, b, c, x, + problem_type=problem_type) + n, m = self.n(), self.m() + if slack_variables is None: + slack_variables = self._prefix + if isinstance(slack_variables, str): + slack_variables = ["{}{:d}".format(slack_variables, i) + for i in range(n + 1, n + m + 1)] + else: + slack_variables = map(str, slack_variables) + if len(slack_variables) != m: + raise ValueError("wrong number of slack variables") + if auxiliary_variable is None: + auxiliary_variable = self._prefix + "0" + names = [str(auxiliary_variable)] + map(str, self.x()) + slack_variables + if names[0] == names[1]: + names.pop(0) + R = PolynomialRing(self.base_ring(), names, order="neglex") + self._R = R + self._objective = objective + + def auxiliary_problem(self): + r""" + Construct the auxiliary problem for ``self``. + + OUTPUT: + + - an :class:`LP problem in standard form `. + + The auxiliary problem with the auxiliary variable `x_0` is + + .. math:: + + \begin{array}{l} + \max - x_0 \\ + - x_0 + A_i x \leq b_i \text{ for all } i \\ + x \geq 0 + \end{array} + + Such problems are used when the :meth:`initial_dictionary` is + infeasible. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1], [-1, -1]) + sage: b = (1000, 1500, -400) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: AP = P.auxiliary_problem() + """ + X = self.coordinate_ring().gens() + m, n = self.m(), self.n() + if len(X) == m + n: + raise ValueError("auxiliary variable is already among decision " + "ones") + F = self.base_ring() + A = column_matrix(F, [-1] * m).augment(self.A()) + c = vector(F, [-1] + [0] * n) + return LPProblemStandardForm(A, self.b(), c, X[:-m], + slack_variables=X[-m:], + auxiliary_variable=X[0], + objective="w") + + def auxiliary_variable(self): + r""" + Return the auxiliary variable of ``self``. + + Note that the auxiliary variable may or may not be among + :meth:`~LPProblem.decision_variables`. + + OUTPUT: + + - a variable of the :meth:`coordinate_ring` of ``self``. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1], [-1, -1]) + sage: b = (1000, 1500, -400) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: P.auxiliary_variable() + x0 + sage: P.decision_variables() + (x1, x2) + sage: AP = P.auxiliary_problem() + sage: AP.auxiliary_variable() + x0 + sage: AP.decision_variables() + (x0, x1, x2) + """ + return self._R.gen(0) + + def coordinate_ring(self): + r""" + Return the coordinate ring of ``self``. + + OUTPUT: + + - a polynomial ring over the :meth:`~LPProblem.base_ring` of ``self`` in + the :meth:`auxiliary_variable`, :meth:`~LPProblem.decision_variables`, + and :meth:`slack_variables` with "neglex" order. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1], [-1, -1]) + sage: b = (1000, 1500, -400) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: P.coordinate_ring() + Multivariate Polynomial Ring in x0, x1, x2, x3, x4, x5 + over Rational Field + sage: P.base_ring() + Rational Field + sage: P.auxiliary_variable() + x0 + sage: P.decision_variables() + (x1, x2) + sage: P.slack_variables() + (x3, x4, x5) + """ + return self._R + + def dictionary(self, *x_B): + r""" + Construct a dictionary for ``self`` with given basic variables. + + INPUT: + + - basic variables for the dictionary to be constructed. + + OUTPUT: + + - a :class:`dictionary `. + + .. NOTE:: + + This is a synonym for ``self.revised_dictionary(x_B).dictionary()``, + but basic variables are mandatory. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.dictionary("x1", "x2") + sage: D.basic_variables() + (x1, x2) + """ + if not x_B: + raise ValueError("basic variables must be given explicitly") + return self.revised_dictionary(*x_B).dictionary() + + def feasible_dictionary(self, auxiliary_dictionary): + r""" + Construct a feasible dictionary for ``self``. + + INPUT: + + - ``auxiliary_dictionary`` -- an optimal dictionary for the + :meth:`auxiliary_problem` of ``self`` with the optimal value `0` and + a non-basic auxiliary variable. + + OUTPUT: + + - a feasible :class:`dictionary ` for ``self``. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1], [-1, -1]) + sage: b = (1000, 1500, -400) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: AP = P.auxiliary_problem() + sage: D = AP.initial_dictionary() + sage: D.enter(0) + sage: D.leave(5) + sage: D.update() + sage: D.enter(1) + sage: D.leave(0) + sage: D.update() + sage: D.is_optimal() + True + sage: D.objective_value() + 0 + sage: D.basic_solution() + (0, 400, 0) + sage: D = P.feasible_dictionary(D) + sage: D.is_optimal() + False + sage: D.is_feasible() + True + sage: D.objective_value() + 4000 + sage: D.basic_solution() + (400, 0) + """ + if not auxiliary_dictionary.is_optimal(): + raise ValueError("the auxiliary dictionary must be optimal") + if auxiliary_dictionary.objective_value() != 0: + raise ValueError("the objective value of the auxiliary dictionary " + "must be 0") + x0 = self.auxiliary_variable() + if x0 not in auxiliary_dictionary.nonbasic_variables(): + raise ValueError("the auxiliary variable must be non-basic") + A, b, c, v, B, N, z = auxiliary_dictionary._AbcvBNz + B = tuple(B) + N = tuple(N) + k = N.index(x0) + N = N[:k] + N[k+1:] + n = len(c) + if c[k] != -1 or list(c).count(0) != n - 1: + raise ValueError("the auxiliary variable must be the only one in " + "the objective (with coefficient -1)") + A = A.matrix_from_columns(range(k) + range(k + 1, n)) + b = copy(b) + c = vector(ZZ, n - 1) + for cj, xj in zip(*self.Abcx()[-2:]): + if xj in N: + c[N.index(xj)] += cj + else: + i = B.index(xj) + c -= cj * A[i] + v += cj * b[i] + B = map(self._R, B) + N = map(self._R, N) + return LPDictionary(A, b, c, v, B, N, self._objective) + + def final_dictionary(self): + r""" + Return the final dictionary of the simplex method applied to ``self``. + + See :meth:`run_simplex_method` for the description of possibilities. + + OUTPUT: + + - a :class:`dictionary `. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.final_dictionary() + sage: D.is_optimal() + True + + TESTS:: + + sage: P.final_dictionary() is P.final_dictionary() + False + """ + try: + D = self._final_dictionary + # Since dictionaries are "highly mutable", forget the returned one. + del self._final_dictionary + return D + except AttributeError: + self.run_simplex_method() + return self.final_dictionary() + + def final_revised_dictionary(self): + r""" + Return the final dictionary of the revised simplex method applied to ``self``. + + See :meth:`run_revised_simplex_method` for the description of + possibilities. + + OUTPUT: + + - a :class:`revised dictionary `. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.final_revised_dictionary() + sage: D.is_optimal() + True + + TESTS:: + + sage: P.final_revised_dictionary() is P.final_revised_dictionary() + False + """ + try: + D = self._final_revised_dictionary + # Since dictionaries are "highly mutable", forget the returned one. + del self._final_revised_dictionary + return D + except AttributeError: + self.run_revised_simplex_method() + return self.final_revised_dictionary() + + def initial_dictionary(self): + r""" + Construct the initial dictionary of ``self``. + + The initial dictionary "defines" :meth:`slack_variables` in terms of the + :meth:`~LPProblem.decision_variables`, i.e. it has slack variables as + basic ones. + + OUTPUT: + + - a :class:`dictionary `. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + """ + A, b, c, x = self.Abcx() + x = self._R.gens() + m, n = self.m(), self.n() + return LPDictionary(A, b, c, 0, x[-m:], x[-m-n:-m], self._objective) + + def inject_variables(self, scope=None, verbose=True): + r""" + Inject variables of ``self`` into ``scope``. + + INPUT: + + - ``scope`` -- namespace (default: global); + + - ``verbose`` -- if ``True`` (default), names of injected variables + will be printed. + + OUTPUT: + + - none. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: P.inject_variables() + Defining x0, x1, x2, x3, x4 + sage: 3*x1 + x2 + x2 + 3*x1 + """ + if scope is None: + # scope = globals() does not work well, + # instead we "borrow" this code from sage.misc.misc.inject_variable + depth = 0 + while True: + scope = sys._getframe(depth).f_globals + if (scope["__name__"] == "__main__" + and scope["__package__"] is None): + break + depth += 1 + try: + self._R.inject_variables(scope, verbose) + except AttributeError: + pass + + def revised_dictionary(self, *x_B): + r""" + Construct a revised dictionary for ``self``. + + INPUT: + + - basic variables for the dictionary to be constructed. If not given, + :meth:`slack_variables` will be used, perhaps with the + :meth:`auxiliary_variable` to + give a feasible dictionary. + + OUTPUT: + + - a :class:`revised dictionary `. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary("x1", "x2") + sage: D.basic_variables() + (x1, x2) + + If basic variables are not given the initial dictionary is constructed:: + + sage: P.revised_dictionary().basic_variables() + (x3, x4) + sage: P.initial_dictionary().basic_variables() + (x3, x4) + + Unless it is infeasible, in which case a feasible dictionary for the + auxiliary problem is constructed:: + + sage: A = ([1, 1], [3, 1], [-1,-1]) + sage: b = (1000, 1500, -400) + sage: P = LPProblemStandardForm(A, b, c) + sage: P.initial_dictionary().is_feasible() + False + sage: P.revised_dictionary().basic_variables() + (x3, x4, x0) + """ + if not x_B: + x_B = list(self.slack_variables()) + bm = min(self.b()) + if bm < 0: + x_B[self.b().list().index(bm)] = self.auxiliary_variable() + return LPRevisedDictionary(self, x_B) + + def run_revised_simplex_method(self): + r""" + Apply the revised simplex method to solve ``self`` and show the steps. + + OUTPUT: + + - a string with `\LaTeX` code of intermediate dictionaries. + + .. note:: + + You can access the :meth:`final_revised_dictionary`, which can be + one of the following: + + - an optimal dictionary with the :meth:`auxiliary_variable` among + :meth:`~LPRevisedDictionary.basic_variables` and a non-zero + optimal value indicating + that ``self`` is infeasible; + + - a non-optimal dictionary that has marked entering + variable for which there is no choice of the leaving variable, + indicating that ``self`` is unbounded; + + - an optimal dictionary. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1], [-1, -1]) + sage: b = (1000, 1500, -400) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: P.run_revised_simplex_method() + \renewcommand{\arraystretch}{1.500000} + \begin{array}{l} + ... + \text{Entering: $x_{1}$. Leaving: $x_{0}$.}\\ + ... + \text{Entering: $x_{5}$. Leaving: $x_{4}$.}\\ + ... + \text{Entering: $x_{2}$. Leaving: $x_{3}$.}\\ + ... + \text{The optimal value: $6250$. An optimal solution: $\left(250,\,750\right)$.} + \end{array} + """ + result = [] + d = self.revised_dictionary() + while not d.is_optimal(): + entering, leaving = min(d.possible_simplex_method_steps()) + d.enter(entering) + if leaving: + leaving = min(leaving) + d.leave(leaving) + result.append(latex(d)) + result.append(r"\text{{Entering: ${}$. Leaving: ${}$.}}" + .format(latex(entering), latex(leaving))) + result.append("") + result.append( + r"B_\mathrm{new}^{-1} = E^{-1} B_\mathrm{old}^{-1} = " + + latex(d.E_inverse()) + latex(d.B_inverse())) + result.append("") + d.update() + else: + result.append(latex(d)) + result.append(r"\text{{The problem is unbounded in the " + r"${}$ direction.}}".format(latex(entering))) + break + if d.is_optimal(): + result.append(latex(d)) + if self.auxiliary_variable() in d.basic_variables(): + result.append(r"\text{The problem is infeasible.}") + else: + v = d.objective_value() + if self._is_negative: + v = - v + result.append((r"\text{{The optimal value: ${}$. " + "An optimal solution: ${}$.}}").format( + latex(v), latex(d.basic_solution()))) + self._final_revised_dictionary = d + return _assemble_arrayl(result, 1.5) + + def run_simplex_method(self): + r""" + Apply the simplex method to solve ``self`` and show the steps. + + OUTPUT: + + - a string with `\LaTeX` code of intermediate dictionaries. + + .. note:: + + You can access the :meth:`final_dictionary`, which can be one of the + following: + + - an optimal dictionary for the :meth:`auxiliary_problem` with a + non-zero optimal value indicating that ``self`` is infeasible; + + - a non-optimal dictionary for ``self`` that has marked entering + variable for which there is no choice of the leaving variable, + indicating that ``self`` is unbounded; + + - an optimal dictionary for ``self``. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1], [-1, -1]) + sage: b = (1000, 1500, -400) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: P.run_simplex_method() # not tested + + You should use the typeset mode as the command above generates long + `\LaTeX` code:: + + sage: print P.run_simplex_method() + \begin{gather*} + ... + \text{The initial dictionary is infeasible, solving auxiliary problem.}\displaybreak[0]\\ + ... + \text{Entering: $x_{0}$. Leaving: $x_{5}$.}\displaybreak[0]\\ + ... + \text{Entering: $x_{1}$. Leaving: $x_{0}$.}\displaybreak[0]\\ + ... + \text{Back to the original problem.}\displaybreak[0]\\ + ... + \text{Entering: $x_{5}$. Leaving: $x_{4}$.}\displaybreak[0]\\ + ... + \text{Entering: $x_{2}$. Leaving: $x_{3}$.}\displaybreak[0]\\ + ... + \text{The optimal value: $6250$. An optimal solution: $\left(250,\,750\right)$.} + \end{gather*} + """ + def step(entering, leaving): + result.append(r"\text{{Entering: ${}$. Leaving: ${}$.}}" + .format(latex(entering), latex(leaving))) + result.append(d.ELLUL(entering, leaving)) + + result = [] + d = self.initial_dictionary() + result.append(latex(d)) + if d.is_feasible(): + is_feasible = True + else: + result.append(r"\text{The initial dictionary is infeasible, " + "solving auxiliary problem.}") + d = self.auxiliary_problem().initial_dictionary() + result.append(latex(d)) + _, entering = min(zip(d.objective_coefficients(), + d.nonbasic_variables())) + _, leaving = min(zip(d.constant_terms(), d.basic_variables())) + step(entering, leaving) + while not d.is_optimal(): + entering, leaving = min(d.possible_simplex_method_steps()) + step(entering, min(leaving)) + is_feasible = d.objective_value() == 0 + if is_feasible: + result.append(r"\text{Back to the original problem.}") + d = self.feasible_dictionary(d) + result.append(latex(d)) + else: + result.append(r"\text{The original problem is infeasible.}") + if is_feasible: + while not d.is_optimal(): + entering, leaving = min(d.possible_simplex_method_steps()) + if leaving: + step(entering, min(leaving)) + else: + d.enter(entering) + result.append(r"\text{{The problem is unbounded in the " + r"${}$ direction.}}".format(latex(entering))) + result.append(latex(d)) + break + if d.is_optimal(): + v = d.objective_value() + if self._is_negative: + v = - v + result.append((r"\text{{The optimal value: ${}$. " + "An optimal solution: ${}$.}}").format( + latex(v), latex(d.basic_solution()))) + self._final_dictionary = d + if generate_real_LaTeX: + # This will have to be used via \sagestr, as line&display breaks + # don't get along with reference substitution without wrapping. + return ("\\begin{gather*}\n\\allowdisplaybreaks\n" + + "\\displaybreak[0]\\\\\n".join(result) + + "\n\\end{gather*}") + return _assemble_arrayl(result, 1.5) + + def slack_variables(self): + r""" + Return slack variables of ``self``. + + Slack variables are differences between the constant terms and left hand + sides of the constraints. + + If you want to give custom names to slack variables, you have to do so + during construction of the problem. + + OUTPUT: + + - a tuple. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: P.slack_variables() + (x3, x4) + sage: P = LPProblemStandardForm(A, b, c, ["C", "B"], + ... slack_variables=["L", "F"]) + sage: P.slack_variables() + (L, F) + """ + return self._R.gens()[-self.m():] + + +class LPAbstractDictionary(SageObject): + r""" + Abstract base class for dictionaries for LP problems. + + Instantiating this class directly is meaningless, see :class:`LPDictionary` + and :class:`LPRevisedDictionary` for useful extensions. + """ + + def __init__(self): + r""" + Initialize internal fields for entering and leaving variables. + + TESTS:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() # indirect doctest + """ + super(LPAbstractDictionary, self).__init__() + self._entering = None + self._leaving = None + + def _repr_(self): + r""" + Return a string representation of ``self``. + + OUTPUT: + + - a string. + + TESTS:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: print D._repr_() + LP problem dictionary (use typeset mode to see details) + sage: D = P.revised_dictionary() + sage: print D._repr_() + LP problem dictionary (use typeset mode to see details) + """ + return "LP problem dictionary (use typeset mode to see details)" + + def base_ring(self): + r""" + Return the base ring of ``self``, i.e. the ring of coefficients. + + OUTPUT: + + - a ring. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.base_ring() + Rational Field + sage: D = P.revised_dictionary() + sage: D.base_ring() + Rational Field + """ + return self.coordinate_ring().base_ring() + + def basic_solution(self, include_slack_variables=False): + r""" + Return the basic solution of ``self``. + + The basic solution associated to a dictionary is obtained by setting to + zero all :meth:`~LPDictionary.nonbasic_variables`, in which case + :meth:`~LPDictionary.basic_variables` have to be equal to + :meth:`~LPDictionary.constant_terms` in equations. + It may refer to values of :meth:`~LPProblem.decision_variables` only or + include :meth:`~LPProblemStandardForm.slack_variables` as well. + + INPUT: + + - ``include_slack_variables`` -- (default: ``False``) if ``True``, + values of slack variables will be appended at the end. + + OUTPUT: + + - a vector. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.basic_solution() + (0, 0) + sage: D.basic_solution(True) + (0, 0, 1000, 1500) + sage: D = P.revised_dictionary() + sage: D.basic_solution() + (0, 0) + sage: D.basic_solution(True) + (0, 0, 1000, 1500) + """ + vv = zip(self.basic_variables(), self.constant_terms()) + N = self.nonbasic_variables() + vv += [(v, 0) for v in N] + vv.sort() # We use neglex order + v = [value for _, value in vv] + return vector(self.base_ring(), + v if include_slack_variables else v[:len(N)]) + + def coordinate_ring(self): + r""" + Return the coordinate ring of ``self``. + + OUTPUT: + + - a polynomial ring in + :meth:`~LPProblemStandardForm.auxiliary_variable`, + :meth:`~LPProblem.decision_variables`, and + :meth:`~LPProblemStandardForm.slack_variables` of ``self`` over the + :meth:`base_ring`. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.coordinate_ring() + Multivariate Polynomial Ring in x0, x1, x2, x3, x4 + over Rational Field + sage: D = P.revised_dictionary() + sage: D.coordinate_ring() + Multivariate Polynomial Ring in x0, x1, x2, x3, x4 + over Rational Field + """ + return self.basic_variables()[0].parent() + + def dual_ratios(self): + r""" + Return ratios used to determine the entering variable based on leaving. + + OUTPUT: + + - a list of pairs `(r_j, x_j)` where `x_j` is a non-basic variable and + `r_j = c_j / a_{ij}` is the ratio of the objective coefficient `c_j` + to the coefficient `a_{ij}` of `x_j` in the relation for the leaving + variable `x_i`: + + .. math:: + + x_i = b_i - \dots - a_{ij} x_j - \dots. + + The order of pairs matches the order of + :meth:`~LPDictionary.nonbasic_variables`, + but only `x_j` with negative `a_{ij}` are considered. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1], [-1, -1]) + sage: b = (1000, 1500, -400) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.dictionary(2, 3, 5) + sage: D.leave(3) + sage: D.dual_ratios() + [(5/2, x1), (5, x4)] + sage: D = P.revised_dictionary(2, 3, 5) + sage: D.leave(3) + sage: D.dual_ratios() + [(5/2, x1), (5, x4)] + """ + return [(c / a, x) for c, a, x in zip(self.objective_coefficients(), + self.leaving_coefficients(), + self.nonbasic_variables()) if a < 0] + + def enter(self, v): + r""" + Set ``v`` as the entering variable of ``self``. + + INPUT: + + - ``v`` -- a non-basic variable of ``self``, can be given as a string, + an actual variable, or an integer interpreted as the index of a + variable. + + OUTPUT: + + - none, but the selected variable will be used as entering by methods + that require an entering variable and the corresponding column will be + typeset in green. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.enter("x1") + + We can also use indices of variables:: + + sage: D.enter(1) + + Or variable names without quotes after injecting them:: + + sage: P.inject_variables() + Defining x0, x1, x2, x3, x4 + sage: D.enter(x1) + + The same works for revised dictionaries as well:: + + sage: D = P.revised_dictionary() + sage: D.enter(x1) + """ + v = variable(self.coordinate_ring(), v) + if v not in self.nonbasic_variables(): + raise ValueError("entering variable must be non-basic") + self._entering = v + + def is_dual_feasible(self): + r""" + Check if ``self`` is dual feasible. + + OUTPUT: + + - ``True`` if all :meth:`~LPDictionary.objective_coefficients` are + non-positive, ``False`` otherwise. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.is_dual_feasible() + False + sage: D = P.revised_dictionary() + sage: D.is_dual_feasible() + False + """ + return all(ci <= 0 for ci in self.objective_coefficients()) + + def is_feasible(self): + r""" + Check if ``self`` is feasible. + + OUTPUT: + + - ``True`` if all :meth:`~LPDictionary.constant_terms` are non-negative, + ``False`` otherwise. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.is_feasible() + True + sage: D = P.revised_dictionary() + sage: D.is_feasible() + True + """ + return all(bi >= 0 for bi in self.constant_terms()) + + def is_optimal(self): + r""" + Check if ``self`` is optimal. + + OUTPUT: + + - ``True`` if ``self`` :meth:`is_feasible` and :meth:`is_dual_feasible` + (i.e. all :meth:`~LPDictionary.constant_terms` are non-negative and + all :meth:`~LPDictionary.objective_coefficients` are non-positive), + ``False`` otherwise. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.is_optimal() + False + sage: D = P.revised_dictionary() + sage: D.is_optimal() + False + sage: D = P.revised_dictionary(1, 2) + sage: D.is_optimal() + True + """ + return self.is_feasible() and self.is_dual_feasible() + + def leave(self, v): + r""" + Set ``v`` as the leaving variable of ``self``. + + INPUT: + + - ``v`` -- a basic variable of ``self``, can be given as a string, an + actual variable, or an integer interpreted as the index of a variable. + + OUTPUT: + + - none, but the selected variable will be used as leaving by methods + that require a leaving variable and the corresponding row will be + typeset in red. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.leave("x4") + + We can also use indices of variables:: + + sage: D.leave(4) + + Or variable names without quotes after injecting them:: + + sage: P.inject_variables() + Defining x0, x1, x2, x3, x4 + sage: D.leave(x4) + + The same works for revised dictionaries as well:: + + sage: D = P.revised_dictionary() + sage: D.leave(x4) + """ + v = variable(self.coordinate_ring(), v) + if v not in self.basic_variables(): + raise ValueError("leaving variable must be basic") + self._leaving = v + + def possible_dual_simplex_method_steps(self): + r""" + Return possible dual simplex method steps for ``self``. + + OUTPUT: + + - a list of pairs ``(leaving, entering)``, where ``leaving`` is a + basic variable that may :meth:`leave` and ``entering`` is a list of + non-basic variables that may :meth:`enter` when ``leaving`` leaves. + Note that ``entering`` may be empty, indicating that the problem is + infeasible (since the dual one is unbounded). + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.dictionary(2, 3) + sage: D.possible_dual_simplex_method_steps() + [(x3, [x1])] + sage: D = P.revised_dictionary(2, 3) + sage: D.possible_dual_simplex_method_steps() + [(x3, [x1])] + """ + if not self.is_dual_feasible(): + raise ValueError("dual simplex method steps are applicable to " + "dual feasible dictionaries only") + steps = [] + old_entering = self._entering + self._entering = None + old_leaving = self._leaving + for l in self.possible_leaving(): + self.leave(l) + steps.append((l, self.possible_entering())) + self._entering = old_entering + self._leaving = old_leaving + return steps + + def possible_entering(self): + r""" + Return possible entering variables for ``self``. + + OUTPUT: + + - a list of non-basic variables of ``self`` that can :meth:`enter` on + the next step of the (dual) simplex method. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.possible_entering() + [x1, x2] + sage: D = P.revised_dictionary() + sage: D.possible_entering() + [x1, x2] + """ + if self.is_dual_feasible() and self._leaving is not None: + ratios = self.dual_ratios() + if not ratios: + return [] + min_ratio = min(ratios)[0] + return [v for r, v in ratios if r == min_ratio] + if self.is_feasible(): + return [v for c, v in zip(self.objective_coefficients(), + self.nonbasic_variables()) if c > 0] + raise ValueError("entering variables can be determined for feasible " + "dictionaries or for dual feasible dictionaries " + "with a set leaving variable") + + def possible_leaving(self): + r""" + Return possible leaving variables for ``self``. + + OUTPUT: + + - a list of basic variables of ``self`` that can :meth:`leave` on + the next step of the (dual) simplex method. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.enter(1) + sage: D.possible_leaving() + [x4] + sage: D = P.revised_dictionary() + sage: D.enter(1) + sage: D.possible_leaving() + [x4] + """ + if self.is_feasible() and self._entering is not None: + ratios = self.ratios() + if not ratios: + return [] + min_ratio = min(ratios)[0] + return [v for r, v in ratios if r == min_ratio] + if self.is_dual_feasible(): + return [v for b, v in zip(self.constant_terms(), + self.basic_variables()) if b < 0] + raise ValueError("leaving variables can be determined for feasible " + "dictionaries with a set entering variable " + "or for dual feasible dictionaries") + + def possible_simplex_method_steps(self): + r""" + Return possible simplex method steps for ``self``. + + OUTPUT: + + - a list of pairs ``(entering, leaving)``, where ``entering`` is a + non-basic variable that may :meth:`enter` and ``leaving`` is a list of + basic variables that may :meth:`leave` when ``entering`` enters. Note + that ``leaving`` may be empty, indicating that the problem is + unbounded. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.possible_simplex_method_steps() + [(x1, [x4]), (x2, [x3])] + sage: D = P.revised_dictionary() + sage: D.possible_simplex_method_steps() + [(x1, [x4]), (x2, [x3])] + """ + if not self.is_feasible(): + raise ValueError("simplex method steps are applicable to feasible " + "dictionaries only") + steps = [] + old_entering = self._entering + old_leaving = self._leaving + self._leaving = None + for e in self.possible_entering(): + self.enter(e) + steps.append((e, self.possible_leaving())) + self._entering = old_entering + self._leaving = old_leaving + return steps + + def ratios(self): + r""" + Return ratios used to determine the leaving variable based on entering. + + OUTPUT: + + - a list of pairs `(r_i, x_i)` where `x_i` is a basic variable and + `r_i = b_i / a_{ik}` is the ratio of the constant term `b_i` to the + coefficient `a_{ik}` of the entering variable `x_k` in the relation + for `x_i`: + + .. math:: + + x_i = b_i - \dots - a_{ik} x_k - \dots. + + The order of pairs matches the order of + :meth:`~LPDictionary.basic_variables`, + but only `x_i` with positive `a_{ik}` are considered. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.enter(1) + sage: D.ratios() + [(1000, x3), (500, x4)] + sage: D = P.revised_dictionary() + sage: D.enter(1) + sage: D.ratios() + [(1000, x3), (500, x4)] + """ + return [(b / a, x) for b, a, x in zip(self.constant_terms(), + self.entering_coefficients(), + self.basic_variables()) if a > 0] + + +class LPDictionary(LPAbstractDictionary): + r""" + Construct a dictionary for an LP problem. + + A dictionary consists of the following data: + + .. math:: + + \begin{array}{|l|} + \hline + x_B = b - A x_N\\ + \hline + z = z^* + c x_N\\ + \hline + \end{array} + + INPUT: + + - ``A`` -- a matrix of relation coefficients; + + - ``b`` -- a vector of relation constant terms; + + - ``c`` -- a vector of objective coefficients; + + - ``objective_value`` -- current value of the objective `z^*`; + + - ``basic_variables`` -- a list of basic variables `x_B`; + + - ``nonbasic_variables`` -- a list of non-basic variables `x_N`; + + - ``objective_variable`` -- an objective variable `z`. + + OUTPUT: + + - a :class:`dictionary for an LP problem `. + + .. note:: + + This constructor does not check correctness of input, as it is intended + to be used internally by :class:`LPProblemStandardForm`. + + EXAMPLES: + + The intended way to use this class is indirect:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D + LP problem dictionary (use typeset mode to see details) + + But if you want you can create a dictionary without starting with an LP + problem, here is construction of the same dictionary as above:: + + sage: A = matrix(QQ, ([1, 1], [3, 1])) + sage: b = vector(QQ, (1000, 1500)) + sage: c = vector(QQ, (10, 5)) + sage: R = PolynomialRing(QQ, "x1, x2, x3, x4", order="neglex") + sage: from sage.numerical.interactive_simplex_method \ + ... import LPDictionary + sage: D2 = LPDictionary(A, b, c, 0, R.gens()[2:], R.gens()[:2], "z") + sage: D2 == D + True + """ + + def __init__(self, A, b, c, objective_value, + basic_variables, nonbasic_variables, objective_variable): + r""" + See :class:`LPDictionary` for documentation. + + TESTS::: + + sage: A = matrix(QQ, ([1, 1], [3, 1])) + sage: b = vector(QQ, (1000, 1500)) + sage: c = vector(QQ, (10, 5)) + sage: R = PolynomialRing(QQ, "x1, x2, x3, x4", order="neglex") + sage: from sage.numerical.interactive_simplex_method \ + ... import LPDictionary + sage: D = LPDictionary(A, b, c, 0, R.gens()[2:], R.gens()[:2], "z") + sage: TestSuite(D).run() + """ + super(LPDictionary, self).__init__() + # We are going to change stuff while LPProblem has immutable data. + A = copy(A) + b = copy(b) + c = copy(c) + B = vector(basic_variables) + N = vector(nonbasic_variables) + self._AbcvBNz = [A, b, c, objective_value, B, N, SR(objective_variable)] + + def __eq__(self, other): + r""" + Check if two LP problem dictionaries are equal. + + INPUT: + + - ``other`` -- anything. + + OUTPUT: + + - ``True`` if ``other`` is an :class:`LPDictionary` with all details the + same as ``self``, ``False`` otherwise. + + TESTS:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + + sage: A = matrix(QQ, ([1, 1], [3, 1])) + sage: b = vector(QQ, (1000, 1500)) + sage: c = vector(QQ, (10, 5)) + sage: R = PolynomialRing(QQ, "x1, x2, x3, x4", order="neglex") + sage: from sage.numerical.interactive_simplex_method \ + ... import LPDictionary + sage: D2 = LPDictionary(A, b, c, 0, R.gens()[2:], R.gens()[:2], "z") + sage: D2 == D + True + + sage: D3 = LPDictionary(A, b, c, 0, R.gens()[2:], R.gens()[:2], "w") + sage: D2 == D3 + False + """ + return (isinstance(other, LPDictionary) and + self._AbcvBNz == other._AbcvBNz) + + def _latex_(self): + r""" + Return a LaTeX representation of ``self``. + + OUTPUT: + + - a string. + + TESTS:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: print D._latex_() + \renewcommand{\arraystretch}{1.5} \setlength{\arraycolsep}{0.125em} + \begin{array}{|rcrcrcr|} + \hline + x_{3} & = & 1000 & - & x_{1} & - & x_{2}\\ + x_{4} & = & 1500 & - & 3 x_{1} & - & x_{2}\\ + \hline + z & = & 0 & + & 10 x_{1} & + & 5 x_{2}\\ + \hline + \end{array} + """ + A, b, c, v, B, N, z = self._AbcvBNz + n = len(N) + lines = [] + lines.append(r"\renewcommand{\arraystretch}{1.5}") + if generate_real_LaTeX: + lines[-1] += r" \setlength{\arraycolsep}{0.125em}" +# else: +# lines[-1] += r"\require{color}" + lines.append(r"\begin{array}{|rcr%s|}" % ("cr"*len(N))) + lines.append(r"\hline") + for xi, bi, Ai in zip(B, b, A.rows()): + lines.append(_latex_product(-Ai,N, head=[xi, "=", bi], + drop_plus=False, allow_empty=True) + r"\\") + lines.append(r"\hline") + lines.append(_latex_product(c, N, head=[z, "=", v], + drop_plus=False, allow_empty=True) + r"\\") + lines.append(r"\hline") + lines.append(r"\end{array}") + latex.add_package_to_preamble_if_available("color") + if self._entering is not None: + # Highlight the entering variable column + e = 2 * tuple(N).index(self._entering) + 4 + for i, line in enumerate(lines): + line = line.split("&") + if len(line) > 1: + if not generate_real_LaTeX: + line[e] = ("{" + line[e] + "}").replace(r"\\}", r"}\\") + line[e] = r"\color{green}" + line[e] + lines[i] = "&".join(line) + if self._leaving is not None: + # Highlight the leaving variable row + l = tuple(B).index(self._leaving) + 3 + line = lines[l].split("&") + for i, term in enumerate(line): + if not generate_real_LaTeX: + term = ("{" + term + "}").replace(r"\\}", r"}\\") + line[i] = r"\color{red}" + term + line = "&".join(line) + if generate_real_LaTeX: + line = line.replace(r"\color{red}\color{green}", + r"\color{blue}") + else: + line = line.replace(r"\color{red}{\color{green}", + r"\color{blue}{") + lines[l] = line + return "\n".join(lines) + + def ELLUL(self, entering, leaving): + r""" + Perform the Enter-Leave-LaTeX-Update-LaTeX step sequence on ``self``. + + INPUT: + + - ``entering`` -- the entering variable; + + - ``leaving`` -- the leaving variable. + + OUTPUT: + + - a string with LaTeX code for ``self`` before and after update. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.ELLUL("x1", "x4") + \renewcommand{\arraystretch}{1.5} \setlength{\arraycolsep}{0.125em} + \begin{array}{|rcrcrcr|} + \hline + x_{3} & = & 1000 & - &\color{green} x_{1} & - & x_{2}\\ + \color{red}x_{4} &\color{red} = &\color{red} 1500 &\color{red} - &\color{blue} 3 x_{1} &\color{red} - &\color{red} x_{2}\\ + \hline + z & = & 0 & + &\color{green} 10 x_{1} & + & 5 x_{2}\\ + \hline + \multicolumn{2}{c}{}\\[-3ex] + \hline + x_{3} & = & 500 & + & \frac{1}{3} x_{4} & - & \frac{2}{3} x_{2}\\ + x_{1} & = & 500 & - & \frac{1}{3} x_{4} & - & \frac{1}{3} x_{2}\\ + \hline + z & = & 5000 & - & \frac{10}{3} x_{4} & + & \frac{5}{3} x_{2}\\ + \hline + \end{array} + + This is how the above output looks when rendered: + + .. math:: + + \renewcommand{\arraystretch}{1.5} + \begin{array}{|rcrcrcr|} + \hline + x_{3} \!\!\!&\!\!\! = \!\!\!&\!\!\! 1000 \!\!\!&\!\!\! - \!\!\!&\color{green}{\!\!\! x_{1} \!\!\!}&\!\!\! - \!\!\!&\!\!\! x_{2}\\ + \color{red}{x_{4} \!\!\!}&\color{red}{\!\!\! = \!\!\!}&\color{red}{\!\!\! 1500 \!\!\!}&\color{red}{\!\!\! - \!\!\!}&\color{blue}{{\!\!\! 3 x_{1} \!\!\!}}&\color{red}{\!\!\! - \!\!\!}&\color{red}{\!\!\! x_{2}}\\ + \hline + z \!\!\!&\!\!\! = \!\!\!&\!\!\! 0 \!\!\!&\!\!\! + \!\!\!&\color{green}{\!\!\! 10 x_{1} \!\!\!}&\!\!\! + \!\!\!&\!\!\! 5 x_{2}\\ + \hline + \\ + \hline + x_{3} \!\!\!&\!\!\! = \!\!\!&\!\!\! 500 \!\!\!&\!\!\! + \!\!\!&\!\!\! \frac{1}{3} x_{4} \!\!\!&\!\!\! - \!\!\!&\!\!\! \frac{2}{3} x_{2}\\ + x_{1} \!\!\!&\!\!\! = \!\!\!&\!\!\! 500 \!\!\!&\!\!\! - \!\!\!&\!\!\! \frac{1}{3} x_{4} \!\!\!&\!\!\! - \!\!\!&\!\!\! \frac{1}{3} x_{2}\\ + \hline + z \!\!\!&\!\!\! = \!\!\!&\!\!\! 5000 \!\!\!&\!\!\! - \!\!\!&\!\!\! \frac{10}{3} x_{4} \!\!\!&\!\!\! + \!\!\!&\!\!\! \frac{5}{3} x_{2}\\ + \hline + \end{array} + + The column of the entering variable is green, while the row of the + leaving variable is red in the original dictionary state on the top. The + new state after the update step is shown on the bottom. + """ + self.enter(entering) + self.leave(leaving) + result = latex(self).rsplit("\n", 1)[0] # Remove \end{array} + # Make an empty line in the array + if generate_real_LaTeX: + result += "\n" r"\multicolumn{2}{c}{}\\[-3ex]" "\n" + else: + result += "\n\\\\\n" + self.update() + result += latex(self).split("\n", 2)[2] # Remove array header + return LatexExpr(result) + + def basic_variables(self): + r""" + Return the basic variables of ``self``. + + OUTPUT: + + - a vector. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.basic_variables() + (x3, x4) + """ + return self._AbcvBNz[4] + + def constant_terms(self): + r""" + Return the constant terms of relations of ``self``. + + OUTPUT: + + - a vector. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.constant_terms() + (1000, 1500) + """ + return self._AbcvBNz[1] + + def entering_coefficients(self): + r""" + Return coefficients of the entering variable. + + OUTPUT: + + - a vector. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.enter(1) + sage: D.entering_coefficients() + (1, 3) + """ + if self._entering is None: + raise ValueError("entering variable must be chosen to compute " + "its coefficients") + k = tuple(self.nonbasic_variables()).index(self._entering) + return self._AbcvBNz[0].column(k) + + def leaving_coefficients(self): + r""" + Return coefficients of the leaving variable. + + OUTPUT: + + - a vector. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.dictionary(2, 3) + sage: D.leave(3) + sage: D.leaving_coefficients() + (-2, -1) + """ + if self._leaving is None: + raise ValueError("leaving variable must be chosen to compute " + "its coefficients") + i = tuple(self.basic_variables()).index(self._leaving) + return self._AbcvBNz[0][i] + + def nonbasic_variables(self): + r""" + Return non-basic variables of ``self``. + + OUTPUT: + + - a vector. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.nonbasic_variables() + (x1, x2) + """ + return self._AbcvBNz[5] + + def objective_coefficients(self): + r""" + Return coefficients of the objective of ``self``. + + OUTPUT: + + - a vector. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.objective_coefficients() + (10, 5) + """ + return self._AbcvBNz[2] + + def objective_value(self): + r""" + Return the value of the objective at the + :meth:`~LPAbstractDictionary.basic_solution` of ``self``. + + OUTPUT: + + - a number. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.objective_value() + 0 + """ + return self._AbcvBNz[3] + + def update(self): + r""" + Update ``self`` using previously set entering and leaving variables. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.initial_dictionary() + sage: D.objective_value() + 0 + sage: D.enter("x1") + sage: D.leave("x4") + sage: D.update() + sage: D.objective_value() + 5000 + """ + A, b, c, v, B, N, z = self._AbcvBNz + entering = self._entering + if entering is None: + raise ValueError("entering variable must be set before updating") + leaving = self._leaving + if leaving is None: + raise ValueError("leaving variable must be set before updating") + l = tuple(B).index(leaving) + e = tuple(N).index(entering) + Ale = A[l, e] + if Ale == 0: + raise ValueError("incompatible choice of entering and leaving " + "variables") + # Variables + B[l] = entering + N[e] = leaving + # "The Changing Relation" + b[l] /= Ale + A[l] /= Ale + A[l, e] = 1 / Ale + # Other relations + for i in range(A.nrows()): + if i != l: + Aie = A[i, e] + A[i, e] = 0 + b[i] -= Aie * b[l] + A[i] -= Aie * A[l] + # Objective + ce = c[e] + c[e] = 0 + self._AbcvBNz[2] = c - ce * A[l] + self._AbcvBNz[3] += ce * b[l] + self._entering = None + self._leaving = None + + +def random_dictionary(m, n, bound=5, special_probability=0.2): + r""" + Construct a random dictionary. + + INPUT: + + - ``m`` -- the number of constraints/basic variables; + + - ``n`` -- the number of decision/non-basic variables; + + - ``bound`` -- (default: 5) a bound on dictionary entries; + + - ``special_probability`` -- (default: 0.2) probability of constructing a + potentially infeasible or potentially optimal dictionary. + + OUTPUT: + + - an :class:`LP problem dictionary `. + + EXAMPLES:: + + sage: from sage.numerical.interactive_simplex_method \ + ... import random_dictionary + sage: random_dictionary(3, 4) + LP problem dictionary (use typeset mode to see details) + """ + A = random_matrix(ZZ, m, n, x=-bound, y=bound).change_ring(QQ) + if special_probability < random(): + b = random_vector(ZZ, m, x=0, y=bound).change_ring(QQ) + else: # Allow infeasible dictionary + b = random_vector(ZZ, m, x=-bound, y=bound).change_ring(QQ) + if special_probability < random(): + c = random_vector(ZZ, n, x=-bound, y=bound).change_ring(QQ) + else: # Make dual feasible dictionary + c = random_vector(ZZ, n, x=-bound, y=0).change_ring(QQ) + x_N = list(PolynomialRing(QQ, "x", m + n + 1, order="neglex").gens()) + x_N.pop(0) + x_B = [] + for i in range(m): + x_B.append(x_N.pop(randint(0, n + m - i - 1))) + return LPDictionary(A, b, c, randint(-bound, bound), x_B, x_N, "z") + + +class LPRevisedDictionary(LPAbstractDictionary): + r""" + Construct a revised dictionary for an LP problem. + + INPUT: + + - ``problem`` -- an :class:`LP problem in standard form + `; + + - ``basic_variables`` -- a list of basic variables or their indices. + + OUTPUT: + + - a :class:`revised dictionary for an LP problem `. + + A revised dictionary encodes the same relations as a + :class:`regular dictionary `, but stores only what is + "necessary to efficiently compute data for the simplex method". + + Let the original problem be + + .. math:: + + \begin{array}{l} + \pm \max cx \\ + Ax \leq b \\ + x \geq 0 + \end{array} + + Let `\bar{x}` be the vector of :meth:`~LPProblem.decision_variables` `x` + followed by the :meth:`~LPProblemStandardForm.slack_variables`. + Let `\bar{c}` be the vector of :meth:`~LPProblem.objective_coefficients` `c` + followed by zeroes for all slack variables. + Let `\bar{A} = (A | I)` be the matrix of + :meth:`~LPProblem.constraint_coefficients` `A` augmented by the identity + matrix as columns corresponding to the slack variables. Then the problem + above can be written as + + .. math:: + + \begin{array}{l} + \pm \max \bar{c} \bar{x} \\ + \bar{A} \bar{x} = b \\ + \bar{x} \geq 0 + \end{array} + + and any dictionary is a system of equations equivalent to + `\bar{A} \bar{x} = b`, but resolved for :meth:`basic_variables` `x_B` in + terms of :meth:`nonbasic_variables` `x_N` together with the expression for + the objective in terms of `x_N`. Let :meth:`c_B` and :meth:`c_N` be vectors + "splitting `\bar{c}` into basic and non-basic parts". Let :meth:`B` and + :meth:`A_N` be the splitting of `\bar{A}`. Then the corresponding dictionary + is + + .. math:: + + \begin{array}{|l|} + \hline + x_B = B^{-1} b - B^{-1} A_N x_N\\ + \hline + z = y b + \left(c_N - y^T A_N\right) x_N\\ + \hline + \end{array} + + where `y = c_B^T B^{-1}`. To proceed with the simplex method, it is not + necessary to compute all entries of this dictionary. On the other hand, any + entry is easy to compute, if you know `B^{-1}`, so we keep track of it + through the update steps. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: from sage.numerical.interactive_simplex_method \ + ... import LPRevisedDictionary + sage: D = LPRevisedDictionary(P, [1, 2]) + sage: D.basic_variables() + (x1, x2) + sage: D + LP problem dictionary (use typeset mode to see details) + + The same dictionary can be constructed through the problem:: + + sage: P.revised_dictionary(1, 2) == D + True + + When this dictionary is typeset, you will see two tables like these ones: + + .. math:: + + \renewcommand{\arraystretch}{1.500000} + \begin{array}{l} + \begin{array}{l|r|rr||r||r} + x_B & c_B & & \!\!\!\!\!\!\!\! B^{-1} & y & B^{-1} b \\ + \hline + x_{1} & 10 & -\frac{1}{2} & \frac{1}{2} & \frac{5}{2} & 250 \\ + x_{2} & 5 & \frac{3}{2} & -\frac{1}{2} & \frac{5}{2} & 750 \\ + \end{array}\\ + \\ + \begin{array}{r|rr} + x_N & x_{3} & x_{4} \\ + \hline + c_N^T & 0 & 0 \\ + y^T A_N & \frac{5}{2} & \frac{5}{2} \\ + \hline + c_N^T - y^T A_N & -\frac{5}{2} & -\frac{5}{2} \\ + \end{array} + \end{array} + + More details will be shown if entering and leaving variables are set, but in + any case the top table shows `B^{-1}` and a few extra columns, while the + bottom one shows several rows: these are related to columns and rows of + dictionary entries. + """ + + def __init__(self, problem, basic_variables): + r""" + See :class:`LPRevisedDictionary` for documentation. + + TESTS::: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: from sage.numerical.interactive_simplex_method \ + ... import LPRevisedDictionary + sage: D = LPRevisedDictionary(P, [1, 2]) + sage: TestSuite(D).run() + """ + if problem.auxiliary_variable() == problem.decision_variables()[0]: + raise ValueError("revised dictionaries should not be constructed " + "for auxiliary problems") + super(LPRevisedDictionary, self).__init__() + self._problem = problem + R = problem.coordinate_ring() + self._x_B = vector(R, [variable(R, v) for v in basic_variables]) + + def __eq__(self, other): + r""" + Check if two revised LP problem dictionaries are equal. + + INPUT: + + - ``other`` -- anything. + + OUTPUT: + + - ``True`` if ``other`` is an :class:`LPRevisedDictionary` for the same + :class:`LPProblemStandardForm` with the same :meth:`basic_variables`, + ``False`` otherwise. + + TESTS:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: from sage.numerical.interactive_simplex_method \ + ... import LPRevisedDictionary + sage: D1 = LPRevisedDictionary(P, [1, 2]) + sage: D2 = LPRevisedDictionary(P, [1, 2]) + sage: D1 is D2 + False + sage: D1 == D2 + True + sage: D3 = LPRevisedDictionary(P, [2, 0]) + sage: D1 == D3 + False + """ + return (isinstance(other, LPRevisedDictionary) and + self._problem == other._problem and + self._x_B == other._x_B) + + def _latex_(self): + r""" + Return a LaTeX representation of ``self``. + + OUTPUT: + + - a string. + + TESTS:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary() + sage: D.enter(1) + sage: D.leave(3) + sage: print D._latex_() + \renewcommand{\arraystretch}{1.500000} + \begin{array}{l} + \begin{array}{l|r|rr||r||r|r|r} + x_B & c_B & \multicolumn{2}{c||}{B^{-1}} & y & B^{-1} b & B^{-1} A_{x_{1}} & \hbox{Ratio} \\ + \hline + \color{red} x_{3} & \color{red} 0 & \color{red} 1 & \color{red} 0 & 0 & \color{red} 1000 & \color{red} 1 & \color{red} 1000 \\ + x_{4} & 0 & 0 & 1 & 0 & 1500 & 3 & 500 \\ + \end{array}\\ + \\ + \begin{array}{r|rr} + x_N & \color{green} x_{1} & x_{2} \\ + \hline + c_N^T & \color{green} 10 & 5 \\ + \hline + y^T A_N & \color{green} 0 & 0 \\ + \hline + c_N^T - y^T A_N & \color{green} 10 & 5 \\ + \end{array} + \end{array} + """ + latex.add_package_to_preamble_if_available("color") + x_B = self._x_B + m = len(x_B) + entering = self._entering + leaving = self._leaving + show_ratios = entering is not None and self.is_feasible() + if leaving is not None: + l = x_B.list().index(leaving) + lines = [] + lines.append(r"\begin{array}{l|r|%s||r||r%s%s}" % ("r"*m, + "|r" if entering is not None else "", "|r" if show_ratios else "")) + headers = ["x_B", "c_B"] + if generate_real_LaTeX: + headers.append(r"\multicolumn{%d}{c||}{B^{-1}}" % m) + else: + headers.extend([""] * (m//2)) + headers.append(r"\!\!\!\!\!\!\!\! B^{-1}") + headers.extend([""] * ((m-1)//2)) + headers.extend(["y", "B^{-1} b"]) + if entering is not None: + headers.append("B^{-1} A_{%s}" % latex(entering)) + if show_ratios: + headers.append(r"\hbox{Ratio}") + lines.append(" & ".join(headers) + r" \\") + lines.append(r"\hline") + Bi = self.B_inverse() + c_B = self.c_B() + y = self.y() + Bib = self.constant_terms() + if entering is not None: + Biae = self.entering_coefficients() + if show_ratios: + ratios = self.ratios() + for i in range(m): + entries = [x_B[i], c_B[i]] + entries.extend(Bi.row(i)) + entries.extend([y[i], Bib[i]]) + if entering is not None: + entries.append(Biae[i]) + if show_ratios: + if ratios and ratios[0][1] == x_B[i]: + entries.append(ratios.pop(0)[0]) + terms = map(latex, entries) + if leaving is not None and i == l: + for j, t in enumerate(terms): + if j == m + 2: + continue + if not generate_real_LaTeX: + t = "{" + t + "}" + terms[j] = r"\color{red} " + t + lines.append(" & ".join(terms) + r" \\") + lines.append(r"\end{array}") + top = "\n".join(lines) + + def make_line(header, terms): + terms = map(latex, terms) + if entering is not None: + t = terms[k] + if not generate_real_LaTeX: + t = "{" + t + "}" + terms[k] = r"\color{green} " + t + lines.append(" & ".join([header] + terms) + r" \\") + + lines = [] + x_N = self.x_N() + if entering is not None: + k = x_N.list().index(entering) + lines.append(r"\begin{array}{r|" + "r" * len(x_N) + "}") + make_line("x_N", x_N) + lines.append(r"\hline") + make_line("c_N^T", self.c_N()) + lines.append(r"\hline") + make_line("y^T A_N", y * self.A_N()) + lines.append(r"\hline") + make_line("c_N^T - y^T A_N", self.objective_coefficients()) + if leaving is not None and self.is_dual_feasible(): + lines.append(r"\hline") + make_line("B^{-1}_{%s} A_N" % latex(leaving), + self.leaving_coefficients()) + lines.append(r"\hline") + ratios = self.dual_ratios() + make_line(r"\hbox{Ratio}", [ratios.pop(0)[0] + if ratios and ratios[0][1] == x else "" + for x in x_N]) + lines.append(r"\end{array}") + bottom = "\n".join(lines) + return _assemble_arrayl([top, "", bottom], 1.5) + + def A(self, v): + r""" + Return the column of constraint coefficients corresponding to ``v``. + + INPUT: + + - ``v`` -- a variable, its name, or its index. + + OUTPUT: + + - a vector. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary() + sage: D.A(1) + (1, 3) + sage: D.A(0) + (-1, -1) + sage: D.A("x3") + (1, 0) + """ + P = self.problem() + R = P.coordinate_ring() + v = variable(R, v) + k = R.gens().index(v) + R = R.base_ring() + m, n = P.m(), P.n() + if k == 0: + return vector(R, [-1] * m) + elif k <= n: + return P.A().column(k - 1) + else: + return identity_matrix(R, m).column(k - n - 1) + + def A_N(self): + r""" + Return the `A_N` matrix, constraint coefficients of non-basic variables. + + OUTPUT: + + - a matrix. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary() + sage: D.A_N() + [1 1] + [3 1] + """ + return column_matrix(self.problem().base_ring(), + [self.A(x) for x in self.x_N()]) + + def B(self): + r""" + Return the `B` matrix, i.e. constraint coefficients of basic variables. + + OUTPUT: + + - a matrix. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary(1, 2) + sage: D.B() + [1 1] + [3 1] + """ + return column_matrix(self.problem().base_ring(), + [self.A(x) for x in self._x_B]) + + def B_inverse(self): + r""" + Return the inverse of the :meth:`B` matrix. + + This inverse matrix is stored and computed during dictionary update in + a more efficient way than generic inversion. + + OUTPUT: + + - a matrix. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary(1, 2) + sage: D.B_inverse() + [-1/2 1/2] + [ 3/2 -1/2] + """ + try: + return self._B_inverse + except AttributeError: + self._B_inverse = self.B().inverse() + return self._B_inverse + + def E(self): + r""" + Return the eta matrix between ``self`` and the next dictionary. + + OUTPUT: + + - a matrix. + + If `B_\mathrm{old}` is the current matrix `B` and `B_\mathrm{new}` is + the `B` matrix of the next dictionary (after the update step), then + `B_\mathrm{new} = B_\mathrm{old} E`. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary() + sage: D.enter(1) + sage: D.leave(4) + sage: D.E() + [1 1] + [0 3] + """ + if self._entering is None: + raise ValueError("entering variable must be set to compute the " + "eta matrix") + leaving = self._leaving + if leaving is None: + raise ValueError("leaving variable must be set to compute the " + "eta matrix") + l = self._x_B.list().index(leaving) + E = identity_matrix(self.base_ring(), self.problem().m()) + E.set_column(l, self.entering_coefficients()) + return E + + def E_inverse(self): + r""" + Return the inverse of the matrix :meth:`E`. + + This inverse matrix is computed in a more efficient way than generic + inversion. + + OUTPUT: + + - a matrix. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary() + sage: D.enter(1) + sage: D.leave(4) + sage: D.E_inverse() + [ 1 -1/3] + [ 0 1/3] + """ + E = self.E() + l = self._x_B.list().index(self._leaving) + d = E[l, l] + if d == 0: + raise ValueError("eta matrix is not invertible due to incompatible " + "choice of entering and leaving variables") + E.set_col_to_multiple_of_col(l, l, -1/d) + E[l, l] = 1 / d + return E + + def basic_indices(self): + r""" + Return the basic indices of ``self``. + + .. note:: + + Basic indices are indices of :meth:`basic_variables` in the list of + generators of the :meth:`~LPProblemStandardForm.coordinate_ring` of + the :meth:`problem` of ``self``, they may not coincide with the + indices of variables which are parts of their names. (They will for + the default indexed names.) + + OUTPUT: + + - a list. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary() + sage: D.basic_indices() + [3, 4] + """ + gens = self.coordinate_ring().gens() + return [gens.index(x) for x in self._x_B] + + def basic_variables(self): + r""" + Return the basic variables of ``self``. + + OUTPUT: + + - a vector. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary() + sage: D.basic_variables() + (x3, x4) + """ + return vector(self._x_B[0].parent(), self._x_B) + + def c_B(self): + r""" + Return the `c_B` vector, objective coefficients of basic variables. + + OUTPUT: + + - a vector. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary(1, 2) + sage: D.c_B() + (10, 5) + """ + P = self.problem() + R = self.base_ring() + BB = self.basic_indices() + if 0 in BB: + c_B = vector(R, P.m()) + c_B[BB.index(0)] = -1 + return c_B + else: + c_D = P.c() + n = P.n() + return vector(R, [c_D[k - 1] if k <= n else 0 for k in BB]) + + def c_N(self): + r""" + Return the `c_N` vector, objective coefficients of non-basic variables. + + OUTPUT: + + - a vector. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary() + sage: D.c_N() + (10, 5) + """ + P = self.problem() + n = P.n() + R = P.base_ring() + if 0 in self.basic_indices(): + return vector(R, n + 1) + else: + c_D = P.c() + return vector(R, (c_D[k - 1] if k <= n else 0 + for k in self.nonbasic_indices())) + + def constant_terms(self): + r""" + Return constant terms in the relations of ``self``. + + OUTPUT: + + - a vector. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary() + sage: D.constant_terms() + (1000, 1500) + """ + return self.B_inverse() * self.problem().b() + + def dictionary(self): + r""" + Return a regular LP dictionary matching ``self``. + + OUTPUT: + + - an :class:`LP dictionary `. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1], [-1, -1]) + sage: b = (1000, 1500, -400) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary() + sage: D.dictionary() + LP problem dictionary (use typeset mode to see details) + """ + D = LPDictionary(self.B_inverse() * self.A_N(), + self.constant_terms(), + self.objective_coefficients(), + self.objective_value(), + self.basic_variables(), + self.nonbasic_variables(), + "z") + D._entering = self._entering + D._leaving = self._leaving + return D + + def entering_coefficients(self): + r""" + Return coefficients of the entering variable. + + OUTPUT: + + - a vector. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary() + sage: D.enter(1) + sage: D.entering_coefficients() + (1, 3) + """ + if self._entering is None: + raise ValueError("entering variable must be chosen to compute " + "its coefficients") + return self.B_inverse() * self.A(self._entering) + + def leaving_coefficients(self): + r""" + Return coefficients of the leaving variable. + + OUTPUT: + + - a vector. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary(2, 3) + sage: D.leave(3) + sage: D.leaving_coefficients() + (-2, -1) + """ + if self._leaving is None: + raise ValueError("leaving variable must be chosen to compute " + "its coefficients") + i = self.basic_variables().list().index(self._leaving) + return self.B_inverse()[i] * self.A_N() + + def nonbasic_indices(self): + r""" + Return the non-basic indices of ``self``. + + .. note:: + + Non-basic indices are indices of :meth:`nonbasic_variables` in the + list of generators of the + :meth:`~LPProblemStandardForm.coordinate_ring` of the + :meth:`problem` of ``self``, they may not coincide with the indices + of variables which are parts of their names. (They will for the + default indexed names.) + + OUTPUT: + + - a list. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary() + sage: D.nonbasic_indices() + [1, 2] + """ + gens = self.coordinate_ring().gens() + return [gens.index(x) for x in self.x_N()] + + def nonbasic_variables(self): + r""" + Return non-basic variables of ``self``. + + OUTPUT: + + - a vector. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary() + sage: D.nonbasic_variables() + (x1, x2) + """ + R = self.coordinate_ring() + return vector(R, [xi for xi in R.gens()[1:] if xi not in self._x_B]) + + def objective_coefficients(self): + r""" + Return coefficients of the objective of ``self``. + + OUTPUT: + + - a vector. + + These are coefficients of non-basic variables when basic variables are + eliminated. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary() + sage: D.objective_coefficients() + (10, 5) + """ + return self.c_N() - self.y() * self.A_N() + + def objective_value(self): + r""" + Return the value of the objective at the basic solution of ``self``. + + OUTPUT: + + - a number. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary() + sage: D.objective_value() + 0 + """ + return self.y() * self.problem().b() + + def problem(self): + r""" + Return the original problem. + + OUTPUT: + + - an :class:`LP problem in standard form `. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary() + sage: D.problem() is P + True + """ + return self._problem + + def update(self): + r""" + Update ``self`` using previously set entering and leaving variables. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary() + sage: D.objective_value() + 0 + sage: D.enter("x1") + sage: D.leave("x4") + sage: D.update() + sage: D.objective_value() + 5000 + """ + # Update the inverse of B first, in case it is impossible + self._B_inverse = self.E_inverse() * self.B_inverse() + # Now update the rest and clear settings + self._x_B[self._x_B.list().index(self._leaving)] = self._entering + self._entering = None + self._leaving = None + + def y(self): + r""" + Return the `y` vector, the product of :meth:`c_B` and :meth:`B_inverse`. + + OUTPUT: + + - a vector. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = LPProblemStandardForm(A, b, c) + sage: D = P.revised_dictionary() + sage: D.y() + (0, 0) + """ + return self.c_B() * self.B_inverse() + + # Aliases for the standard notation + x_B = basic_variables + x_N = nonbasic_variables From e4aaa9ffffd7b58ee87ef78ebb51b20cc08b3d06 Mon Sep 17 00:00:00 2001 From: Andrey Novoseltsev Date: Sun, 17 Mar 2013 05:05:58 +0000 Subject: [PATCH 066/546] Bind interactive simplex method module into documentation and global namespace. --- src/doc/en/reference/numerical/index.rst | 1 + src/sage/numerical/all.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/doc/en/reference/numerical/index.rst b/src/doc/en/reference/numerical/index.rst index b331ca23637..911c20a9961 100644 --- a/src/doc/en/reference/numerical/index.rst +++ b/src/doc/en/reference/numerical/index.rst @@ -8,6 +8,7 @@ Numerical Optimization sage/numerical/mip sage/numerical/linear_functions sage/numerical/optimize + sage/numerical/interactive_simplex_method LP Solver backends ------------------ diff --git a/src/sage/numerical/all.py b/src/sage/numerical/all.py index a7a55508c26..220b1e90102 100644 --- a/src/sage/numerical/all.py +++ b/src/sage/numerical/all.py @@ -9,3 +9,7 @@ minimize_constrained) from sage.numerical.mip import MixedIntegerLinearProgram from sage.numerical.backends.generic_backend import default_mip_solver + +from sage.misc.lazy_import import lazy_import +lazy_import("sage.numerical.interactive_simplex_method", + ["LPProblem", "LPProblemStandardForm"]) From 1a5d3d59a7f32639f87ee8929fb6549e8850cbd4 Mon Sep 17 00:00:00 2001 From: Andrey Novoseltsev Date: Mon, 9 Sep 2013 23:06:51 +0000 Subject: [PATCH 067/546] Adjustments to the new interactive simplex method module. --- .../numerical/interactive_simplex_method.py | 78 +++++++++++-------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/src/sage/numerical/interactive_simplex_method.py b/src/sage/numerical/interactive_simplex_method.py index e025421b77c..998df7cb47a 100644 --- a/src/sage/numerical/interactive_simplex_method.py +++ b/src/sage/numerical/interactive_simplex_method.py @@ -287,13 +287,13 @@ def _latex_product(coefficients, variables, """ entries = [] for c, v in zip(coefficients, variables): - t = latex(c * v) - if t == "0": + if c == 0: entries.extend(["", ""]) - elif t.startswith("-"): - entries.extend(["-", t[1:]]) - else: - entries.extend(["+", t]) + continue + sign = "-" if c < 0 else "+" + c = abs(c) + t = latex(v) if c == 1 else latex(c) + " " + latex(v) + entries.extend([sign, t]) if drop_plus: # Don't start with + for i, e in enumerate(entries): if e: # The first non-empty @@ -321,7 +321,7 @@ def _latex_product(coefficients, variables, if generate_real_LaTeX: separator = " & " else: - separator = r" \!\!\!&\!\!\! " + separator = r" \mspace{-6mu}&\mspace{-6mu} " return separator.join(entries) @@ -416,14 +416,20 @@ class LPProblem(SageObject): type(s): either "<=", or ">=", or "==", or a list of them; - ``variable_type`` -- (default: ">=") a string specifying variable type(s): - either ">=", or "<=", or "" (empty string), or a list of them; + either ">=", or "<=", or "" (empty string), or a list of them, + corresponding, respectively, to non-negative, non-positive, and free + variables; - ``problem_type`` -- (default: "max") a string specifying the problem type: "max", "min", "-max", or "-min"; - ``prefix`` -- (default: parameter ``x`` if it was given as a string, string "x" otherwise) a string giving the base name of automatically - created variables in :meth:`standard_form`. + created variables in :meth:`standard_form`; + + - ``base_ring`` -- (default: the fraction field of a common ring for all + input coefficients) a field to which all input coefficients will be + converted. OUTPUT: @@ -468,7 +474,7 @@ class LPProblem(SageObject): def __init__(self, A, b, c, x="x", constraint_type="<=", variable_type=">=", problem_type="max", - prefix="x"): + prefix="x", base_ring=None): r""" See :class:`LPProblem` for documentation. @@ -485,15 +491,17 @@ def __init__(self, A, b, c, x="x", A = matrix(A) b = vector(b) c = vector(c) - F = vector(A.list() + list(b) + list(c)).base_ring().fraction_field() - A = A.base_extend(F) + if base_ring is None: + base_ring = vector(A.list() + list(b) + list(c)).base_ring() + base_ring = base_ring.fraction_field() + A = A.change_ring(base_ring) A.set_immutable() m, n = A.nrows(), A.ncols() - b = b.base_extend(F) + b = b.change_ring(base_ring) b.set_immutable() if b.degree() != m: raise ValueError("A and b have incompatible dimensions") - c = c.base_extend(F) + c = c.change_ring(base_ring) c.set_immutable() if c.degree() != n: raise ValueError("A and c have incompatible dimensions") @@ -504,7 +512,7 @@ def __init__(self, A, b, c, x="x", x = map(str, x) if len(x) != n: raise ValueError("A and x have incompatible dimensions") - R = PolynomialRing(F, x, order="neglex") + R = PolynomialRing(base_ring, x, order="neglex") x = vector(R, R.gens()) # All variables as a vector self._Abcx = A, b, c, x @@ -606,7 +614,7 @@ def _latex_(self): head = [r"{} \{}".format("- " if self._is_negative else "", self._problem_type)] lines.append(_latex_product(c, x, head=head) + - (r"\\" if generate_real_LaTeX else r" \!\!\! \\")) + (r"\\" if generate_real_LaTeX else r" \mspace{-6mu} \\")) for Ai, ri, bi in zip(A.rows(), self._constraint_types, b): lines.append(_latex_product(Ai, x, head=[""], tail=[ri, bi]) + r" \\") @@ -1328,7 +1336,11 @@ class LPProblemStandardForm(LPProblem): problems; - ``objective`` -- (default: "z") the objective variable (used for the - initial dictionary). + initial dictionary); + + - ``base_ring`` -- (default: the fraction field of a common ring for all + input coefficients) a field to which all input coefficients will be + converted. OUTPUT: @@ -1353,7 +1365,8 @@ class LPProblemStandardForm(LPProblem): """ def __init__(self, A, b, c, x="x", problem_type="max", - slack_variables=None, auxiliary_variable=None, objective="z"): + slack_variables=None, auxiliary_variable=None, objective="z", + base_ring=None): r""" See :class:`StandardFormLPP` for documentation. @@ -1369,7 +1382,8 @@ def __init__(self, A, b, c, x="x", problem_type="max", raise ValueError("problems in standard form must be of (negative) " "maximization type") super(LPProblemStandardForm, self).__init__(A, b, c, x, - problem_type=problem_type) + problem_type=problem_type, + base_ring=base_ring) n, m = self.n(), self.m() if slack_variables is None: slack_variables = self._prefix @@ -1387,6 +1401,9 @@ def __init__(self, A, b, c, x="x", problem_type="max", names.pop(0) R = PolynomialRing(self.base_ring(), names, order="neglex") self._R = R + x = vector(R.gens()[-n-m:-m]) + x.set_immutable() + self._Abcx = self._Abcx[:-1] + (x, ) self._objective = objective def auxiliary_problem(self): @@ -1565,23 +1582,19 @@ def feasible_dictionary(self, auxiliary_dictionary): sage: D.basic_solution() (400, 0) """ - if not auxiliary_dictionary.is_optimal(): - raise ValueError("the auxiliary dictionary must be optimal") - if auxiliary_dictionary.objective_value() != 0: - raise ValueError("the objective value of the auxiliary dictionary " - "must be 0") + # It is good to have sanity checks in this function, but they are a bit + # problematic with numerical dictionaries, so we do only few. x0 = self.auxiliary_variable() if x0 not in auxiliary_dictionary.nonbasic_variables(): raise ValueError("the auxiliary variable must be non-basic") + if not auxiliary_dictionary.is_feasible(): + raise ValueError("the auxiliary dictionary must be feasible") A, b, c, v, B, N, z = auxiliary_dictionary._AbcvBNz B = tuple(B) N = tuple(N) k = N.index(x0) N = N[:k] + N[k+1:] n = len(c) - if c[k] != -1 or list(c).count(0) != n - 1: - raise ValueError("the auxiliary variable must be the only one in " - "the objective (with coefficient -1)") A = A.matrix_from_columns(range(k) + range(k + 1, n)) b = copy(b) c = vector(ZZ, n - 1) @@ -1926,14 +1939,15 @@ def step(entering, leaving): "solving auxiliary problem.}") d = self.auxiliary_problem().initial_dictionary() result.append(latex(d)) - _, entering = min(zip(d.objective_coefficients(), - d.nonbasic_variables())) + x0 = self.auxiliary_variable() _, leaving = min(zip(d.constant_terms(), d.basic_variables())) - step(entering, leaving) - while not d.is_optimal(): + step(x0, leaving) + # while not d.is_optimal(): + # either optimality check should handle rounding errors or + while not (d.is_optimal() or x0 in d.nonbasic_variables()): entering, leaving = min(d.possible_simplex_method_steps()) step(entering, min(leaving)) - is_feasible = d.objective_value() == 0 + is_feasible = x0 in d.nonbasic_variables() if is_feasible: result.append(r"\text{Back to the original problem.}") d = self.feasible_dictionary(d) From a413b46f1764e742cce660c75fee65b0cd2520f4 Mon Sep 17 00:00:00 2001 From: Andrey Novoseltsev Date: Thu, 1 May 2014 15:36:38 -0600 Subject: [PATCH 068/546] Make it work with current Sage. --- .../numerical/interactive_simplex_method.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/sage/numerical/interactive_simplex_method.py b/src/sage/numerical/interactive_simplex_method.py index 998df7cb47a..d87ecca2053 100644 --- a/src/sage/numerical/interactive_simplex_method.py +++ b/src/sage/numerical/interactive_simplex_method.py @@ -171,7 +171,6 @@ from sage.geometry.all import Polyhedron from sage.matrix.all import (column_matrix, identity_matrix, - is_Matrix, matrix, random_matrix) from sage.misc.all import (LatexExpr, @@ -181,6 +180,7 @@ randint, random) from sage.misc.latex import EMBEDDED_MODE +from sage.misc.misc import get_main_globals from sage.modules.all import random_vector, vector from sage.plot.all import Graphics, arrow, line, point, rainbow, text from sage.rings.all import Infinity, PolynomialRing, QQ, RDF, ZZ @@ -283,7 +283,7 @@ def _latex_product(coefficients, variables, sage: var("x, y") (x, y) sage: print _latex_product([-1, 3], [x, y]) - - & x & + & 3 \, y + - & x & + & 3 y """ entries = [] for c, v in zip(coefficients, variables): @@ -487,8 +487,7 @@ def __init__(self, A, b, c, x="x", sage: TestSuite(P).run() """ super(LPProblem, self).__init__() - if not is_Matrix(A): - A = matrix(A) + A = matrix(A) b = vector(b) c = vector(c) if base_ring is None: @@ -1730,15 +1729,7 @@ def inject_variables(self, scope=None, verbose=True): x2 + 3*x1 """ if scope is None: - # scope = globals() does not work well, - # instead we "borrow" this code from sage.misc.misc.inject_variable - depth = 0 - while True: - scope = sys._getframe(depth).f_globals - if (scope["__name__"] == "__main__" - and scope["__package__"] is None): - break - depth += 1 + scope = get_main_globals() try: self._R.inject_variables(scope, verbose) except AttributeError: @@ -2705,7 +2696,6 @@ def _latex_(self): \end{array} """ A, b, c, v, B, N, z = self._AbcvBNz - n = len(N) lines = [] lines.append(r"\renewcommand{\arraystretch}{1.5}") if generate_real_LaTeX: From 477529e629a70dd237cc3728c21eca3438d4e715 Mon Sep 17 00:00:00 2001 From: Andrey Novoseltsev Date: Thu, 1 May 2014 16:18:14 -0600 Subject: [PATCH 069/546] Change default variables to free to match #15521. --- .../numerical/interactive_simplex_method.py | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/sage/numerical/interactive_simplex_method.py b/src/sage/numerical/interactive_simplex_method.py index d87ecca2053..1d7c94eb6a4 100644 --- a/src/sage/numerical/interactive_simplex_method.py +++ b/src/sage/numerical/interactive_simplex_method.py @@ -38,7 +38,7 @@ sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P LP problem (use typeset mode to see details) @@ -58,7 +58,7 @@ Since it has only two variables, we can solve it graphically:: - sage: P.plot() # long time + sage: P.plot() The simplex method can be applied only to :class:`problems in standard form @@ -415,7 +415,7 @@ class LPProblem(SageObject): - ``constraint_type`` -- (default: "<=") a string specifying constraint type(s): either "<=", or ">=", or "==", or a list of them; - - ``variable_type`` -- (default: ">=") a string specifying variable type(s): + - ``variable_type`` -- (default: "") a string specifying variable type(s): either ">=", or "<=", or "" (empty string), or a list of them, corresponding, respectively, to non-negative, non-positive, and free variables; @@ -455,7 +455,7 @@ class LPProblem(SageObject): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") Same problem, but more explicitly:: @@ -473,7 +473,7 @@ class LPProblem(SageObject): """ def __init__(self, A, b, c, x="x", - constraint_type="<=", variable_type=">=", problem_type="max", + constraint_type="<=", variable_type="", problem_type="max", prefix="x", base_ring=None): r""" See :class:`LPProblem` for documentation. @@ -483,7 +483,7 @@ def __init__(self, A, b, c, x="x", sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: TestSuite(P).run() """ super(LPProblem, self).__init__() @@ -564,11 +564,11 @@ def __eq__(self, other): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) - sage: P2 = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") + sage: P2 = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P == P2 True - sage: P3 = LPProblem(A, c, b, ["C", "B"]) + sage: P3 = LPProblem(A, c, b, ["C", "B"], variable_type=">=") sage: P == P3 False """ @@ -593,7 +593,7 @@ def _latex_(self): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: print P._latex_() \begin{array}{l} \setlength{\arraycolsep}{0.125em} \begin{array}{lcrcrcl} @@ -640,7 +640,7 @@ def _repr_(self): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: print P._repr_() LP problem (use typeset mode to see details) """ @@ -666,7 +666,7 @@ def _solve(self): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P._solve() ((250, 750), 6250) """ @@ -709,7 +709,7 @@ def Abcx(self): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P.Abcx() ( [1 1] @@ -735,12 +735,12 @@ def base_ring(self): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P.base_ring() Rational Field sage: c = (10, 5.) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P.base_ring() Real Field with 53 bits of precision """ @@ -759,7 +759,7 @@ def constant_terms(self): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P.constant_terms() (1000, 1500) sage: P.b() @@ -780,7 +780,7 @@ def constraint_coefficients(self): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P.constraint_coefficients() [1 1] [3 1] @@ -803,7 +803,7 @@ def decision_variables(self): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P.decision_variables() (C, B) sage: P.x() @@ -830,7 +830,7 @@ def dual(self, y=None): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: DP = P.dual() sage: DP.b() == P.c() True @@ -881,7 +881,7 @@ def feasible_set(self): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P.feasible_set() A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices @@ -921,7 +921,7 @@ def is_bounded(self): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P.is_bounded() True """ @@ -940,7 +940,7 @@ def is_feasible(self): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P.is_feasible() True """ @@ -959,7 +959,7 @@ def n_constraints(self): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P.n_constraints() 2 sage: P.m() @@ -980,7 +980,7 @@ def n_variables(self): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P.n_variables() 2 sage: P.n() @@ -1001,7 +1001,7 @@ def objective_coefficients(self): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P.objective_coefficients() (10, 5) sage: P.c() @@ -1022,7 +1022,7 @@ def optimal_solution(self): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P.optimal_solution() (250, 750) """ @@ -1042,7 +1042,7 @@ def optimal_value(self): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P.optimal_value() 6250 """ @@ -1074,20 +1074,20 @@ def plot(self, *args, **kwds): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: p = P.plot() - sage: p.show() # long time + sage: p.show() In this case the plot works better with the following axes ranges:: sage: p = P.plot(0, 1000, 0, 1500) - sage: p.show() # long time + sage: p.show() TESTS: We check that zero objective can be dealt with:: - sage: LPProblem(A, b, (0, 0), ["C", "B"]).plot() + sage: LPProblem(A, b, (0, 0), ["C", "B"], variable_type=">=").plot() """ FP = self.plot_feasible_set(*args, **kwds) c = self.c().n().change_ring(QQ) @@ -1150,14 +1150,14 @@ def plot_feasible_set(self, xmin=None, xmax=None, ymin=None, ymax=None, sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: p = P.plot_feasible_set() - sage: p.show() # long time + sage: p.show() In this case the plot works better with the following axes ranges:: sage: p = P.plot_feasible_set(0, 1000, 0, 1500) - sage: p.show() # long time + sage: p.show() """ if self.n() != 2: raise ValueError("only problems with 2 variables can be plotted") @@ -1241,7 +1241,7 @@ def standard_form(self): sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = LPProblem(A, b, c, ["C", "B"]) + sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: DP = P.dual() sage: DPSF = DP.standard_form() sage: DPSF.b() From af649c1becf52c414da9b3ea16773a91839e7d31 Mon Sep 17 00:00:00 2001 From: Andrey Novoseltsev Date: Fri, 2 May 2014 15:47:23 -0600 Subject: [PATCH 070/546] Don't rely on LPProblem defaults in LPProblemStandardForm. --- src/sage/numerical/interactive_simplex_method.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/numerical/interactive_simplex_method.py b/src/sage/numerical/interactive_simplex_method.py index 1d7c94eb6a4..72325c71aa5 100644 --- a/src/sage/numerical/interactive_simplex_method.py +++ b/src/sage/numerical/interactive_simplex_method.py @@ -1382,6 +1382,8 @@ def __init__(self, A, b, c, x="x", problem_type="max", "maximization type") super(LPProblemStandardForm, self).__init__(A, b, c, x, problem_type=problem_type, + constraint_type="<=", + variable_type=">=", base_ring=base_ring) n, m = self.n(), self.m() if slack_variables is None: From 916f5909ddacc40557e4120d9969628502482c8e Mon Sep 17 00:00:00 2001 From: Andrey Novoseltsev Date: Fri, 2 May 2014 16:04:41 -0600 Subject: [PATCH 071/546] Be more careful with detecting "negative" coefficients for LaTeX. --- src/sage/numerical/interactive_simplex_method.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/numerical/interactive_simplex_method.py b/src/sage/numerical/interactive_simplex_method.py index 72325c71aa5..cf0307dacf6 100644 --- a/src/sage/numerical/interactive_simplex_method.py +++ b/src/sage/numerical/interactive_simplex_method.py @@ -290,8 +290,10 @@ def _latex_product(coefficients, variables, if c == 0: entries.extend(["", ""]) continue - sign = "-" if c < 0 else "+" - c = abs(c) + sign = "+" + if latex(c).strip().startswith("-"): + sign = "-" + c = - c t = latex(v) if c == 1 else latex(c) + " " + latex(v) entries.extend([sign, t]) if drop_plus: # Don't start with + From 798aaf89e1156b92ea27e775d3b9e2207c350658 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Sat, 10 May 2014 00:56:55 +0200 Subject: [PATCH 072/546] Implemented _cache_key() for polynomials This allows caching of polynomials with unhashable coefficients. --- .../rings/polynomial/polynomial_element.pyx | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index b1f47c32e5c..f2944c881bc 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -13,8 +13,9 @@ AUTHORS: - Simon King: Use a faster way of conversion from the base ring. -- Julian Rueth (2012-05-25): Fixed is_squarefree() for imperfect fields. - Fixed division without remainder over QQbar. +- Julian Rueth (2012-05-25,2014-05-09): Fixed is_squarefree() for imperfect + fields, fixed division without remainder over QQbar, added ``_cache_key`` + for polynomials with unhashable coefficients - Simon King (2013-10): Implement copying of :class:`PolynomialBaseringInjection`. @@ -829,6 +830,39 @@ cdef class Polynomial(CommutativeAlgebraElement): def __iter__(self): return iter(self.list()) + def _cache_key(self): + """ + Return a key which uniquely identifies this element among all elements + in the parent. + + .. SEEALSO:: + + :meth:`sage.structure.sage_object.SageObject._cache_key` + + EXAMPLES: + + This enables caching for polynomials with unhashable coefficients such + as `p`-adics:: + + sage: K. = Qq(4) + sage: R. = K[] + sage: f = x + sage: hash(f) + Traceback (most recent call last): + ... + TypeError: unhashable type: 'sage.rings.padics.padic_ZZ_pX_CR_element.pAdicZZpXCRElement' + sage: f._cache_key() + (0, 1 + O(2^20)) + + sage: @cached_function + ....: def foo(t): return t + ....: + sage: foo(x) + (1 + O(2^20))*x + + """ + return tuple(self) + # you may have to replicate this boilerplate code in derived classes if you override # __richcmp__. The python documentation at http://docs.python.org/api/type-structs.html # explains how __richcmp__, __hash__, and __cmp__ are tied together. From 877302ec5744433ccdd079552a7aaf1300a438ad Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Sat, 10 May 2014 00:57:15 +0200 Subject: [PATCH 073/546] Enable caching for non-hashable objects Objects whose == operator is broken by design (such as p-adics) should still be cacheable if they define a _cache_key. This enables caching in cached_function/method for such objects. --- src/sage/misc/cachefunc.pyx | 253 ++++++++++++++++++++++++++++++++---- 1 file changed, 225 insertions(+), 28 deletions(-) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index 048d7110671..4a04354ebe6 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -3,14 +3,15 @@ Cached Functions and Methods AUTHORS: -- William Stein (inspired by conversation with Justin Walker). -- Mike Hansen (added doctests and made it work with class methods). -- Willem Jan Palenstijn (add CachedMethodCaller for binding cached - methods to instances). -- Tom Boothby (added DiskCachedFunction). -- Simon King (improved performance, more doctests, cython version, - CachedMethodCallerNoArgs, weak cached function, cached special methods). -- Julian Rueth (2014-03-19): added ``key`` parameter +- William Stein: initial version, (inspired by conversation with Justin Walker) +- Mike Hansen: added doctests and made it work with class methods. +- Willem Jan Palenstijn: add CachedMethodCaller for binding cached methods to + instances. +- Tom Boothby: added DiskCachedFunction. +- Simon King: improved performance, more doctests, cython version, + CachedMethodCallerNoArgs, weak cached function, cached special methods. +- Julian Rueth (2014-03-19, 2014-05-09): added ``key`` parameter, allow caching + for unhashable elements EXAMPLES: @@ -465,6 +466,44 @@ def _cached_function_unpickle(module,name): """ return getattr(__import__(module, fromlist=['']),name) +def _cache_key(o): + r""" + Helper function to return a hashable key for ``o`` which can be used for + caching. + + This function is intended for objects which are not hashable such as + `p`-adic numbers. The difference from calling an object's ``_cache_key`` + method directly, is that it also works for tuples and unpacks them + recursively. + + EXAMPLES:: + + sage: from sage.misc.cachefunc import _cache_key + sage: K. = Qq(9) + sage: a = K(1); a + 1 + O(3^20) + sage: _cache_key(a) + (((1,),), 0, 20) + + This function works if ``o`` is a tuple. In this case it unpacks its + entries recursively:: + + sage: o = (1, 2, (3, a)) + sage: _cache_key(o) + (1, 2, (3, (((1,),), 0, 20))) + + .. SEEALSO:: + + :meth:`sage.structure.sage_object.SageObject._cache_key` + + """ + if isinstance(o, sage.structure.sage_object.SageObject): + o = o._cache_key() + if isinstance(o,tuple): + return tuple(_cache_key(item) for item in o) + else: + return o + cdef class CachedFunction(object): """ Create a cached version of a function, which only recomputes @@ -487,7 +526,8 @@ cdef class CachedFunction(object): def f(...): .... - The inputs to the function must be hashable. + The inputs to the function must be hashable or they must define + :meth:`sage.structure.sage_object.SageObject._cache_key`. EXAMPLES:: @@ -543,7 +583,8 @@ cdef class CachedFunction(object): def f(...): .... - The inputs to the function must be hashable. + The inputs to the function must be hashable or they must define + :meth:`sage.structure.sage_object.SageObject._cache_key`. TESTS:: @@ -775,6 +816,25 @@ cdef class CachedFunction(object): sage: a is number_of_partitions(10^5) True + Check that :trac:`16316` has been fixed, i.e., caching works for + objects which are not hashable:: + + sage: @cached_function + ....: def f(x): return x + sage: K. = Qq(4) + sage: x = K(1,1); x + 1 + O(2) + sage: y = K(1,2); y + 1 + O(2^2) + sage: x==y + True + sage: f(x) + 1 + O(2) + sage: f(y) + 1 + O(2^2) + sage: f.cache + {(((((1,),), 0, 1),), ()): 1 + O(2), (((((1,),), 0, 2),), ()): 1 + O(2^2)} + """ # We shortcut a common case of no arguments if args or kwds: @@ -790,7 +850,11 @@ cdef class CachedFunction(object): k = self._default_key = self._fix_to_pos() try: - return (self.cache)[k] + try: + return (self.cache)[k] + except TypeError: # k is not hashable + k = _cache_key(k) + return (self.cache)[k] except KeyError: w = self.f(*args, **kwds) self.cache[k] = w @@ -828,10 +892,32 @@ cdef class CachedFunction(object): 6 sage: a.f.is_in_cache(3,y=0) True + + TESTS: + + Check that :trac:`16316` has been fixed, i.e., caching works for + objects which are not hashable:: + + sage: @cached_function + ....: def f(x): return x + sage: K. = Qq(4) + sage: x = K(1,1); x + 1 + O(2) + sage: f.is_in_cache(x) + False + sage: f(x) + 1 + O(2) + sage: f.is_in_cache(x) + True + """ if self._argument_fixer is None: self.argfix_init() - return self._fix_to_pos(*args, **kwds) in (self.cache) + k = self._fix_to_pos(*args, **kwds) + try: + return k in (self.cache) + except TypeError: # k is not hashable + return _cache_key(k) in (self.cache) def set_cache(self, value, *args, **kwds): """ @@ -851,6 +937,20 @@ cdef class CachedFunction(object): sage: g(5) 17 + TESTS: + + Check that :trac:`16316` has been fixed, i.e., caching works for + objects which are not hashable:: + + sage: @cached_function + ....: def f(x): return x + sage: K. = Qq(4) + sage: x = K(1,1); x + 1 + O(2) + sage: f.set_cache(x,x) + sage: f.is_in_cache(x) + True + DEVELOPER NOTE: Is there a way to use the following intuitive syntax? @@ -863,7 +963,11 @@ cdef class CachedFunction(object): """ if self._argument_fixer is None: self.argfix_init() - (self.cache)[self._fix_to_pos(*args, **kwds)] = value + k = self._fix_to_pos(*args, **kwds) + try: + (self.cache)[k] = value + except TypeError: # k is not hashable + (self.cache)[_cache_key(k)] = value def get_key(self, *args, **kwds): """ @@ -997,7 +1101,8 @@ cdef class WeakCachedFunction(CachedFunction): """ def __init__(self, f, classmethod=False, name=None, key=None): """ - The inputs to the function must be hashable. + The inputs to the function must be hashable or they must define + :meth:`sage.structure.sage_object.SageObject._cache_key`. The outputs to the function must be weakly referenceable. TESTS:: @@ -1057,6 +1162,27 @@ cdef class WeakCachedFunction(CachedFunction): sage: a = f() doing a computation + Check that :trac:`16316` has been fixed, i.e., caching works for + objects which are not hashable:: + + sage: from sage.misc.cachefunc import weak_cached_function + sage: @weak_cached_function + ....: def f(x): return x + sage: K. = Qq(4) + sage: R. = K[] + sage: x = t + K(1,1); x + (1 + O(2^20))*t + 1 + O(2) + sage: y = t + K(1,2); y + (1 + O(2^20))*t + 1 + O(2^2) + sage: x==y + True + sage: f(x) + (1 + O(2^20))*t + 1 + O(2) + sage: f(y) + (1 + O(2^20))*t + 1 + O(2^2) + sage: list(f.cache.keys()) + [((((((1,),), 0, 2), (((1,),), 0, 20)),), ()), ((((((1,),), 0, 1), (((1,),), 0, 20)),), ())] + """ # We shortcut a common case of no arguments if args or kwds: @@ -1072,7 +1198,11 @@ cdef class WeakCachedFunction(CachedFunction): k = self._default_key = self._fix_to_pos() try: - return self.cache[k] + try: + return self.cache[k] + except TypeError: # k is not hashable + k = _cache_key(k) + return self.cache[k] except KeyError: w = self.f(*args, **kwds) self.cache[k] = w @@ -1108,10 +1238,31 @@ cdef class WeakCachedFunction(CachedFunction): sage: f.is_in_cache(5) False + TESTS: + + Check that :trac:`16316` has been fixed, i.e., caching works for + objects which are not hashable:: + + sage: from sage.misc.cachefunc import weak_cached_function + sage: @weak_cached_function + ....: def f(x): return x + sage: K. = Qq(4) + sage: R. = K[] + sage: f.is_in_cache(t) + False + sage: f(t) + (1 + O(2^20))*t + sage: f.is_in_cache(t) + True + """ if self._argument_fixer is None: self.argfix_init() - return self._fix_to_pos(*args, **kwds) in self.cache + k = self._fix_to_pos(*args, **kwds) + try: + return k in self.cache + except TypeError: # k is not hashable + return _cache_key(k) in self.cache def set_cache(self, value, *args, **kwds): """ @@ -1133,11 +1284,28 @@ cdef class WeakCachedFunction(CachedFunction): sage: f(5) Integer Ring + TESTS: + + Check that :trac:`16316` has been fixed, i.e., caching works for + objects which are not hashable:: + + sage: from sage.misc.cachefunc import weak_cached_function + sage: @weak_cached_function + ....: def f(x): return x + sage: K. = Qq(4) + sage: R. = K[] + sage: f.set_cache(t,t) + sage: f.is_in_cache(t) + True + """ if self._argument_fixer is None: self.argfix_init() - self.cache[self._fix_to_pos(*args, **kwds)] = value - + k = self._fix_to_pos(*args, **kwds) + try: + self.cache[k] = value + except TypeError: # k is not hashable + self.cache[_cache_key(k)] = value weak_cached_function = decorator_keywords(WeakCachedFunction) @@ -1545,6 +1713,28 @@ cdef class CachedMethodCaller(CachedFunction): sage: b = Foo(3) sage: a.f(b.f) 2 + + Check that :trac:`16316` has been fixed, i.e., caching works for + objects which are not hashable:: + + sage: K. = Qq(4) + sage: class A(object): + ....: @cached_method + ....: def f(self, x): return x + sage: a=A() + sage: x = K(1,1); x + 1 + O(2) + sage: y = K(1,2); y + 1 + O(2^2) + sage: x==y + True + sage: a.f(x) + 1 + O(2) + sage: a.f(y) + 1 + O(2^2) + sage: a.f.cache + {(((((1,),), 0, 1),), ()): 1 + O(2), (((((1,),), 0, 2),), ()): 1 + O(2^2)} + """ if self._instance is None: # cached method bound to a class @@ -1586,7 +1776,11 @@ cdef class CachedMethodCaller(CachedFunction): else: k = self._default_key = self._fix_to_pos() try: - return cache[k] + try: + return cache[k] + except TypeError: # k is not hashable + k = _cache_key(k) + return cache[k] except KeyError: w = self._cachedmethod._instance_call(self._instance, *args, **kwds) cache[k] = w @@ -2036,9 +2230,10 @@ cdef class CachedMethod(object): .. NOTE:: - For proper behavior, the method must be a pure function - (no side effects). Arguments to the method must be hashable - or transformed into something hashable using ``key``. + For proper behavior, the method must be a pure function (no side + effects). Arguments to the method must be hashable or transformed into + something hashable using ``key`` or they must define + :meth:`sage.structure.sage_object.SageObject._cache_key`. EXAMPLES:: @@ -2534,15 +2729,17 @@ cdef class CachedInParentMethod(CachedMethod): This way of caching works only if - the instances *have* a parent, and - - the instances are hashable (they are part of the cache key). + - the instances are hashable (they are part of the cache key) or they + define :meth:`sage.structure.sage_object.SageObject._cache_key` NOTE: - For proper behavior, the method must be a pure function (no side - effects). If this decorator is used on a method, it will have - identical output on equal elements. This is since the element is - part of the hash key. Arguments to the method and the instance - it is assigned to must be hashable. + For proper behavior, the method must be a pure function (no side effects). + If this decorator is used on a method, it will have identical output on + equal elements. This is since the element is part of the hash key. + Arguments to the method must be hashable or define + :meth:`sage.structure.sage_object.SageObject._cache_key`. The instance it + is assigned to must be hashable. Examples can be found at :mod:`~sage.misc.cachefunc`. From ba9befadb9ee1e26966c9eee9edbdfe86c85fd14 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Sat, 10 May 2014 01:16:27 +0200 Subject: [PATCH 074/546] Enabled caching for unhashable objects in factories --- src/sage/structure/factory.pyx | 40 ++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/sage/structure/factory.pyx b/src/sage/structure/factory.pyx index 676f83e210d..86982537016 100644 --- a/src/sage/structure/factory.pyx +++ b/src/sage/structure/factory.pyx @@ -37,13 +37,15 @@ easier to use than a factory. AUTHORS: -- Robert Bradshaw (2008), initial version. -- Simon King (2013), extended documentation. +- Robert Bradshaw (2008): initial version. +- Simon King (2013): extended documentation. +- Julian Rueth (2014-05-09): use ``_cache_key`` if parameters are unhashable """ #***************************************************************************** # Copyright (C) 2008 Robert Bradshaw +# 2014 Julian Rueth # # Distributed under the terms of the GNU General Public License (GPL) # @@ -383,23 +385,37 @@ cdef class UniqueFactory(SageObject): sage: test_factory.get_object(3.0, 'a', {}) is test_factory.get_object(3.0, 'b', {}) Making object b False + + TESTS: + + Check that :trac:`16317` has been fixed, i.e., caching works for + unhashable objects:: + + sage: K. = Qq(4) + sage: test_factory.get_object(3.0, (K(1), 'c'), {}) is test_factory.get_object(3.0, (K(1), 'c'), {}) + Making object (1 + O(2^20), 'c') + True + """ + cache_key = key try: - return self._cache[version, key] + try: + return self._cache[version, cache_key] + except TypeError: # key is unhashable + from sage.misc.cachefunc import _cache_key + cache_key = _cache_key(cache_key) + return self._cache[version, cache_key] except KeyError: pass obj = self.create_object(version, key, **extra_args) - self._cache[version, key] = obj + self._cache[version, cache_key] = obj try: - other_keys = self.other_keys(key, obj) - for key in other_keys: + for key in self.other_keys(key, obj): try: - obj = self._cache[version, key] - break - except KeyError: - pass - for key in other_keys: - self._cache[version, key] = obj + self._cache[version, key] = obj + except TypeError: # key is unhashable + from sage.misc.cachefunc import _cache_key + self._cache[version, _cache_key(key)] = obj obj._factory_data = self, version, key, extra_args if obj.__class__.__reduce__.__objclass__ is object: # replace the generic object __reduce__ to use this one From 91baa4e6fe9799b614028d7f8338a00a8afb00ed Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Sat, 10 May 2014 01:49:23 +0200 Subject: [PATCH 075/546] Improved caching in monsky_washnitzer.py --- .../elliptic_curves/monsky_washnitzer.py | 96 +++++++++---------- 1 file changed, 45 insertions(+), 51 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/monsky_washnitzer.py b/src/sage/schemes/elliptic_curves/monsky_washnitzer.py index 2d710be5492..5f1a1daec24 100644 --- a/src/sage/schemes/elliptic_curves/monsky_washnitzer.py +++ b/src/sage/schemes/elliptic_curves/monsky_washnitzer.py @@ -34,12 +34,16 @@ algorithms - Robert Bradshaw (2007-04): generalization to hyperelliptic curves + +- Julian Rueth (2014-05-09): improved caching + """ #***************************************************************************** # Copyright (C) 2006 William Stein # 2006 Robert Bradshaw # 2006 David Harvey +# 2014 Julian Rueth # # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ @@ -56,6 +60,8 @@ from sage.modules.all import vector from sage.rings.ring import CommutativeAlgebra from sage.structure.element import CommutativeAlgebraElement +from sage.structure.unique_representation import UniqueRepresentation +from sage.misc.cachefunc import cached_method from sage.rings.infinity import Infinity from sage.rings.arith import binomial, integer_ceil as ceil @@ -1828,44 +1834,30 @@ def matrix_of_frobenius_hyperelliptic(Q, p=None, prec=None, M=None): return M.transpose(), [f for f, a in reduced] -# For uniqueness (as many of the non-trivial calculations are cached along the way). - -_special_ring_cache = {} -_mw_cache = {} - - -def SpecialHyperellipticQuotientRing(*args): - if args in _special_ring_cache: - R = _special_ring_cache[args]() - if R is not None: - return R - R = SpecialHyperellipticQuotientRing_class(*args) - _special_ring_cache[args] = weakref.ref(R) - return R - - -def MonskyWashnitzerDifferentialRing(base_ring): - if base_ring in _mw_cache: - R = _mw_cache[base_ring]() - if R is not None: - return R +class SpecialHyperellipticQuotientRing(UniqueRepresentation, CommutativeAlgebra): + _p = None - R = MonskyWashnitzerDifferentialRing_class(base_ring) - _mw_cache[base_ring] = weakref.ref(R) - return R + def __init__(self, Q, R=None, invert_y=True): + r""" + Initialization. + TESTS: -class SpecialHyperellipticQuotientRing_class(CommutativeAlgebra): + Check that caching works:: - _p = None + sage: R. = QQ['x'] + sage: E = HyperellipticCurve(x^5-3*x+1) + sage: from sage.schemes.elliptic_curves.monsky_washnitzer import SpecialHyperellipticQuotientRing + sage: SpecialHyperellipticQuotientRing(E) is SpecialHyperellipticQuotientRing(E) + True - def __init__(self, Q, R=None, invert_y=True): + """ if R is None: R = Q.base_ring() # Trac ticket #9138: CommutativeAlgebra.__init__ must not be # done so early. It tries to register a coercion, but that - # requires the hash bein available. But the hash, in its + # requires the hash being available. But the hash, in its # default implementation, relies on the string representation, # which is not available at this point. #CommutativeAlgebra.__init__(self, R) # moved to below. @@ -2169,7 +2161,7 @@ def is_field(self, proof=True): False """ return False - +SpecialHyperellipticQuotientRing_class = SpecialHyperellipticQuotientRing class SpecialHyperellipticQuotientElement(CommutativeAlgebraElement): @@ -2599,16 +2591,27 @@ def coeffs(self, R=None): coeffs = transpose_list(coeffs) return [V(a) for a in coeffs], y_offset - -class MonskyWashnitzerDifferentialRing_class(Module): - +class MonskyWashnitzerDifferentialRing(UniqueRepresentation, Module): + r""" + A ring of Monsky--Washnitzer differentials over ``base_ring``. + """ def __init__(self, base_ring): r""" - Class for the ring of Monsky--Washnitzer differentials over a given - base ring. + Initialization. + + TESTS: + + Check that caching works:: + + sage: R. = QQ['x'] + sage: E = HyperellipticCurve(x^5-3*x+1) + sage: from sage.schemes.elliptic_curves.monsky_washnitzer import SpecialHyperellipticQuotientRing, MonskyWashnitzerDifferentialRing + sage: S = SpecialHyperellipticQuotientRing(E) + sage: MonskyWashnitzerDifferentialRing(S) is MonskyWashnitzerDifferentialRing(S) + True + """ Module.__init__(self, base_ring) - self._cache = {} def invariant_differential(self): """ @@ -2724,6 +2727,7 @@ def Q(self): """ return self.base_ring().Q() + @cached_method def x_to_p(self, p): """ Returns and caches `x^p`, reduced via the relations coming from the @@ -2741,13 +2745,9 @@ def x_to_p(self, p): sage: MW.x_to_p(101) is MW.x_to_p(101) True """ - try: - return self._cache["x_to_p", p] - except KeyError: - x_to_p = self.base_ring().x() ** p - self._cache["x_to_p", p] = x_to_p - return x_to_p + return self.base_ring().x() ** p + @cached_method def frob_Q(self, p): """ Returns and caches `Q(x^p)`, which is used in computing the image of @@ -2765,13 +2765,7 @@ def frob_Q(self, p): sage: MW.frob_Q(11) is MW.frob_Q(11) True """ - try: - return self._cache["frobQ", p] - except KeyError: - x_to_p = self.x_to_p(p) - frobQ = self.base_ring()._Q.change_ring(self.base_ring())(x_to_p) - self._cache["frobQ", p] = frobQ - return frobQ + return self.base_ring()._Q.change_ring(self.base_ring())(self.x_to_p(p)) def frob_invariant_differential(self, prec, p): r""" @@ -2813,7 +2807,7 @@ def frob_invariant_differential(self, prec, p): x_to_p = x*x_to_p_less_1 # cache for future use - self._cache["x_to_p", p] = x_to_p + self.x_to_p.set_cache(p, x_to_p) prof("frob_Q") a = self.frob_Q(p) >> 2*p # frobQ * y^{-2p} @@ -2921,7 +2915,7 @@ def helper_matrix(self): else: self._helper_matrix = ~A return self._helper_matrix - +MonskyWashnitzerDifferentialRing_class = MonskyWashnitzerDifferentialRing class MonskyWashnitzerDifferential(ModuleElement): @@ -2933,7 +2927,7 @@ def __init__(self, parent, val=0, offset=0): INPUT: - ``parent`` -- Monsky-Washnitzer differential ring (instance of class - :class:`~MonskyWashnitzerDifferentialRing_class` + :class:`~MonskyWashnitzerDifferentialRing` - ``val`` -- element of the base ring, or list of coefficients From 9c6749db3c97ebf62556702cf8d942c8a0f07649 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sat, 10 May 2014 10:40:43 +0200 Subject: [PATCH 076/546] doctest coverage --- src/sage/functions/hypergeometric.py | 87 +++++++++++++++++----------- 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/src/sage/functions/hypergeometric.py b/src/sage/functions/hypergeometric.py index 41497fb5914..b45b397250b 100644 --- a/src/sage/functions/hypergeometric.py +++ b/src/sage/functions/hypergeometric.py @@ -193,35 +193,16 @@ class Hypergeometric(BuiltinFunction): \frac{(a_1)_n\dots(a_p)_n}{(b_1)_n\dots(b_q)_n} \, \frac{z^n}{n!},` where `(x)_n` is the rising factorial. - - INPUT: - - - ``a`` -- a list or tuple of parameters - - ``b`` -- a list or tuple of parameters - - ``z`` -- a number or symbolic expression - - EXAMPLES:: - - sage: hypergeometric([], [], 1) - hypergeometric((), (), 1) - sage: hypergeometric([], [1], 1) - hypergeometric((), (1,), 1) - sage: hypergeometric([2, 3], [1], 1) - hypergeometric((2, 3), (1,), 1) - sage: hypergeometric([], [], x) - hypergeometric((), (), x) - sage: hypergeometric([x], [], x^2) - hypergeometric((x,), (), x^2) - - The only simplification that is done automatically is returning 1 if ``z`` - is 0:: - - sage: hypergeometric([], [], 0) - 1 - - For other simplifications use the ``simplify_hypergeometric`` method. """ def __init__(self): + """ + Initialize class. + + EXAMPLES:: + + sage: maxima(hypergeometric) + hypergeometric + """ BuiltinFunction.__init__(self, 'hypergeometric', nargs=3, conversions={'mathematica': 'HypergeometricPFQ', @@ -229,6 +210,31 @@ def __init__(self): 'sympy': 'hyper'}) def __call__(self, a, b, z, **kwargs): + """ + Return symbolic hypergeometric function expression. + + INPUT: + + - ``a`` -- a list or tuple of parameters + - ``b`` -- a list or tuple of parameters + - ``z`` -- a number or symbolic expression + + EXAMPLES:: + + sage: hypergeometric([], [], 1) + hypergeometric((), (), 1) + sage: hypergeometric([], [1], 1) + hypergeometric((), (1,), 1) + sage: hypergeometric([2, 3], [1], 1) + hypergeometric((2, 3), (1,), 1) + sage: hypergeometric([], [], x) + hypergeometric((), (), x) + sage: hypergeometric([x], [], x^2) + hypergeometric((x,), (), x^2) + + The only simplification that is done automatically is returning 1 if ``z`` + is 0. For other simplifications use the ``simplify_hypergeometric`` method. + """ return BuiltinFunction.__call__(self, SR._force_pyobject(a), SR._force_pyobject(b), @@ -249,6 +255,12 @@ def _print_latex_(self, a, b, z): r"{} \right)").format(len(a), len(b), aa, bb, z) def _eval_(self, a, b, z, **kwargs): + """ + EXAMPLES:: + + sage: hypergeometric([], [], 0) + 1 + """ if not isinstance(a,tuple) or not isinstance(b,tuple): raise ValueError('First two parameters must be of type list.') coercion_model = get_coercion_model() @@ -590,14 +602,6 @@ def deflated(cls, self, a, b, z): EXAMPLES:: - sage: x = hypergeometric([5], [4], 3) - sage: y = x.deflated() - sage: y - 7/4*hypergeometric((), (), 3) - sage: x.n(); y.n() - 35.1496896155784 - 35.1496896155784 - sage: x = hypergeometric([6, 1], [3, 4, 5], 10) sage: y = x.deflated() sage: y @@ -618,6 +622,19 @@ def deflated(cls, self, a, b, z): return sum(map(prod, self._deflated())) def _deflated(cls, self, a, b, z): + """ + Private helper to return list of deflated terms. + + EXAMPLES:: + + sage: x = hypergeometric([5], [4], 3) + sage: y = x.deflated() + sage: y + 7/4*hypergeometric((), (), 3) + sage: x.n(); y.n() + 35.1496896155784 + 35.1496896155784 + """ new = self.eliminate_parameters() aa = new.operands()[0].operands() bb = new.operands()[1].operands() From 90f339fae0a02a54f664940c7bd799e6138a2c30 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 10 May 2014 09:52:53 -0700 Subject: [PATCH 077/546] Doing some of the changes Nicolas requested. --- src/sage/combinat/free_module.py | 70 +++---------------- ...indexed_group.py => indexed_free_group.py} | 52 +++----------- ...dexed_monoid.py => indexed_free_monoid.py} | 47 ++----------- .../{misc => structure}/indexed_generators.py | 41 ++++++++++- 4 files changed, 63 insertions(+), 147 deletions(-) rename src/sage/groups/{indexed_group.py => indexed_free_group.py} (88%) rename src/sage/monoids/{indexed_monoid.py => indexed_free_monoid.py} (94%) rename src/sage/{misc => structure}/indexed_generators.py (92%) diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index 107ff495b01..e31656425b0 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -13,6 +13,7 @@ from sage.structure.element import Element from sage.structure.parent import Parent from sage.structure.sage_object import have_same_parent +from sage.structure.indexed_generators import IndexedGenerators from sage.modules.free_module_element import vector from sage.misc.misc import repr_lincomb from sage.modules.module import Module @@ -29,7 +30,6 @@ from sage.combinat.dict_addition import dict_addition, dict_linear_combination from sage.sets.family import Family from sage.misc.ascii_art import AsciiArt, empty_ascii_art -from sage.misc.indexed_generators import IndexedGenerators # TODO: move the content of this class to CombinatorialFreeModule.Element and ModulesWithBasis.Element class CombinatorialFreeModuleElement(Element): @@ -997,67 +997,13 @@ class CombinatorialFreeModule(UniqueRepresentation, Module, IndexedGenerators): default None, in which case use the "category of modules with basis" over the base ring ``R``) - Options controlling the printing of elements: - - - ``prefix`` - string, prefix used for printing elements of this - module (optional, default 'B'). With the default, a monomial - indexed by 'a' would be printed as ``B['a']``. - - - ``latex_prefix`` - string or None, prefix used in the LaTeX - representation of elements (optional, default None). If this is - anything except the empty string, it prints the index as a - subscript. If this is None, it uses the setting for ``prefix``, - so if ``prefix`` is set to "B", then a monomial indexed by 'a' - would be printed as ``B_{a}``. If this is the empty string, then - don't print monomials as subscripts: the monomial indexed by 'a' - would be printed as ``a``, or as ``[a]`` if ``latex_bracket`` is - True. - - - ``bracket`` - None, bool, string, or list or tuple of - strings (optional, default None): if None, use the value of the - attribute ``self._repr_option_bracket``, which has default value - True. (``self._repr_option_bracket`` is available for backwards - compatibility. Users should set ``bracket`` instead. If - ``bracket`` is set to anything except None, it overrides - the value of ``self._repr_option_bracket``.) If False, do not - include brackets when printing elements: a monomial indexed by - 'a' would be printed as ``B'a'``, and a monomial indexed by - (1,2,3) would be printed as ``B(1,2,3)``. If True, use "[" and - "]" as brackets. If it is one of "[", "(", or "{", use it and - its partner as brackets. If it is any other string, use it as - both brackets. If it is a list or tuple of strings, use the - first entry as the left bracket and the second entry as the - right bracket. - - - ``latex_bracket`` - bool, string, or list or tuple of strings - (optional, default False): if False, do not include brackets in - the LaTeX representation of elements. This option is only - relevant if ``latex_prefix`` is the empty string; otherwise, - brackets are not used regardless. If True, use "\\left[" and - "\\right]" as brackets. If this is one of "[", "(", "\\{", "|", - or "||", use it and its partner, prepended with "\\left" and - "\\right", as brackets. If this is any other string, use it as - both brackets. If this is a list or tuple of strings, use the - first entry as the left bracket and the second entry as the - right bracket. - - - ``scalar_mult`` - string to use for scalar multiplication in - the print representation (optional, default "*") - - - ``latex_scalar_mult`` - string or None (optional, default None), - string to use for scalar multiplication in the latex - representation. If None, use the empty string if ``scalar_mult`` - is set to "*", otherwise use the value of ``scalar_mult``. - - - ``tensor_symbol`` - string or None (optional, default None), - string to use for tensor product in the print representation. If - None, use the ``sage.categories.tensor.symbol``. - - - ``generator_cmp`` - a comparison function (optional, default cmp), - to use for sorting elements in the output of elements - - .. note:: These print options may also be accessed and modified using the - :meth:`print_options` method, after the module has been defined. + For the options controlling the printing of elements, see + :class:`~sage.structure.indexed_generators.IndexedGenerators`. + + .. NOTE:: + + These print options may also be accessed and modified using the + :meth:`print_options` method, after the module has been defined. EXAMPLES: diff --git a/src/sage/groups/indexed_group.py b/src/sage/groups/indexed_free_group.py similarity index 88% rename from src/sage/groups/indexed_group.py rename to src/sage/groups/indexed_free_group.py index f9bee271699..9c3d812f9b9 100644 --- a/src/sage/groups/indexed_group.py +++ b/src/sage/groups/indexed_free_group.py @@ -22,8 +22,8 @@ from copy import copy from sage.categories.groups import Groups from sage.groups.group import Group, AbelianGroup -from sage.monoids.indexed_monoid import IndexedMonoidElement, IndexedFreeMonoid, \ - IndexedFreeAbelianMonoid +from sage.monoids.indexed_free_monoid import (IndexedMonoidElement, + IndexedFreeMonoid, IndexedFreeAbelianMonoid) from sage.misc.cachefunc import cached_method from sage.rings.integer import Integer from sage.rings.infinity import infinity @@ -180,7 +180,7 @@ def __len__(self): sage: len(elt) 7 """ - return sum(abs(exp) for gen,exp in self._sorted_items()) + return sum(abs(exp) for gen,exp in self._monomial) length = __len__ @@ -199,9 +199,9 @@ def _mul_(self, other): sage: (a*b^-2*d^2) * (d^-2*b^2*a^-1) 1 """ - if len(self._monomial) == 0: + if not self._monomial: return other - if len(other._monomial) == 0: + if not other._monomial: return self ret = list(self._monomial) @@ -226,43 +226,8 @@ def __invert__(self): sage: x * ~x 1 """ - return self.__class__(self.parent(), tuple((x[0], -x[1]) for x in reversed(self._monomial))) - - def __pow__(self, n): - """ - Raise ``self`` to the `n`-th power. - - EXAMPLES:: - - sage: F = FreeGroup(index_set=ZZ) - sage: a,b,c,d,e = [F.gen(i) for i in range(5)] - sage: x = a*b^2*e*a^-1; x - F[0]*F[1]^2*F[4]*F[0]^-1 - sage: x^3 - F[0]*F[1]^2*F[4]*F[1]^2*F[4]*F[1]^2*F[4]*F[0]^-1 - sage: x^0 - 1 - sage: x^-3 - F[0]*F[4]^-1*F[1]^-2*F[4]^-1*F[1]^-2*F[4]^-1*F[1]^-2*F[0]^-1 - """ - if not isinstance(n, (int, long, Integer)): - raise TypeError("Argument n (= {}) must be an integer".format(n)) - if n == 0: - return self.parent().one() - if n == 1: - return self - if n == -1: - return ~self - if len(self._monomial) == 1: - gen,exp = self._monomial[0] - return self.__class__(self.parent(), ((gen, exp*n),)) - if n < 0: - self = ~self - n = -n - ret = self - for i in range(n-1): - ret *= self - return ret + return self.__class__(self.parent(), + tuple((x[0], -x[1]) for x in reversed(self._monomial))) def to_word_list(self): """ @@ -279,7 +244,8 @@ def to_word_list(self): [(0, 1), (1, 1), (1, 1), (4, 1), (0, -1)] """ sign = lambda x: 1 if x > 0 else -1 # It is never 0 - return [ (k, sign(e)) for k,e in self._sorted_items() for dummy in range(abs(e))] + return [ (k, sign(e)) for k,e in self._sorted_items() + for dummy in range(abs(e))] class IndexedFreeAbelianGroup(IndexedFreeAbelianMonoid, AbelianGroup): """ diff --git a/src/sage/monoids/indexed_monoid.py b/src/sage/monoids/indexed_free_monoid.py similarity index 94% rename from src/sage/monoids/indexed_monoid.py rename to src/sage/monoids/indexed_free_monoid.py index fce88fbfa61..3e7d3f441a6 100644 --- a/src/sage/monoids/indexed_monoid.py +++ b/src/sage/monoids/indexed_free_monoid.py @@ -16,10 +16,10 @@ from copy import copy from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method -from sage.misc.indexed_generators import IndexedGenerators from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.structure.element import MonoidElement +from sage.structure.indexed_generators import IndexedGenerators from sage.categories.monoids import Monoids from sage.categories.poor_man_map import PoorManMap @@ -73,7 +73,7 @@ def __init__(self, F, x): @abstract_method def _sorted_items(self): """ - Return the items (i.e terms) of ``self``, sorted for printing. + Return the sorted items (i.e factors) of ``self``. EXAMPLES:: @@ -340,7 +340,7 @@ def __len__(self): sage: len(elt) 7 """ - return sum(exp for gen,exp in self._sorted_items()) + return sum(exp for gen,exp in self._monomial) length = __len__ @@ -453,7 +453,7 @@ def __init__(self, F, x): def _sorted_items(self): """ - Return the items (i.e terms) of ``self``, sorted for printing. + Return the sorted items (i.e factors) of ``self``. EXAMPLES:: @@ -486,9 +486,9 @@ def _mul_(self, other): sage: (a*b^2*d^2) * (d^4*b*e) F[0]*F[1]^2*F[3]^6*F[1]*F[4] """ - if len(self._monomial) == 0: + if not self._monomial: return other - if len(other._monomial) == 0: + if not other._monomial: return self ret = list(self._monomial) @@ -498,37 +498,6 @@ def _mul_(self, other): ret += rhs return self.__class__(self.parent(), tuple(ret)) - def __pow__(self, n): - """ - Raise ``self`` to the power of ``n``. - - EXAMPLES:: - - sage: F = FreeMonoid(index_set=ZZ) - sage: a,b,c,d,e = [F.gen(i) for i in range(5)] - sage: x = a*b^2*e*d*a; x - F[0]*F[1]^2*F[4]*F[3]*F[0] - sage: x^3 - F[0]*F[1]^2*F[4]*F[3]*F[0]^2*F[1]^2*F[4]*F[3]*F[0]^2*F[1]^2*F[4]*F[3]*F[0] - sage: x^0 - 1 - """ - if not isinstance(n, (int, long, Integer)): - raise TypeError("Argument n (= {}) must be an integer".format(n)) - if n < 0: - raise ValueError("Argument n (= {}) must be positive".format(n)) - if n == 1: - return self - if n == 0: - return self.parent().one() - if len(self._monomial) == 1: - gen,exp = self._monomial[0] - return self.__class__(self.parent(), ((gen, exp*n),)) - ret = self - for i in range(n-1): - ret *= self - return ret - class IndexedFreeAbelianMonoidElement(IndexedMonoidElement): """ An element of an indexed free abelian monoid. @@ -551,7 +520,7 @@ def __init__(self, F, x): def _sorted_items(self): """ - Return the items (i.e terms) of ``self``, sorted for printing. + Return the sorted items (i.e factors) of ``self``. EXAMPLES:: @@ -743,8 +712,6 @@ def _element_constructor_(self, x=None): """ if x is None or x == 1: return self.one() - if isinstance(x, IndexedFreeAbelianMonoidElement) and x.parent() is self: - return x if x in self._indices: return self.gens()[x] return self.element_class(self, x) diff --git a/src/sage/misc/indexed_generators.py b/src/sage/structure/indexed_generators.py similarity index 92% rename from src/sage/misc/indexed_generators.py rename to src/sage/structure/indexed_generators.py index f32fdcc0b69..ff5465e0d4d 100644 --- a/src/sage/misc/indexed_generators.py +++ b/src/sage/structure/indexed_generators.py @@ -10,7 +10,7 @@ from sage.rings.all import Integer -class IndexedGenerators: +class IndexedGenerators(object): r""" Abstract base class for parents whose elements consist of generators indexed by an arbitrary set. @@ -78,10 +78,44 @@ class IndexedGenerators: These print options may also be accessed and modified using the :meth:`print_options` method, after the parent has been defined. + + EXAMPLES: + + We demonstrate a variety of the input options:: + + sage: from sage.structure.indexed_generators import IndexedGenerators + sage: I = IndexedGenerators(ZZ, prefix='A') + sage: I._repr_generator(2) + 'A[2]' + sage: I._latex_generator(2) + 'A_{2}' + + sage: I = IndexedGenerators(ZZ, bracket='(') + sage: I._repr_generator(2) + 'x(2)' + sage: I._latex_generator(2) + 'x_{2}' + + sage: I = IndexedGenerators(ZZ, prefix="", latex_bracket='(') + sage: I._repr_generator(2) + '[2]' + sage: I._latex_generator(2) + \left( 2 \right) + + sage: I = IndexedGenerators(ZZ, bracket=['|', '>']) + sage: I._repr_generator(2) + 'x|2>' """ def __init__(self, indices, prefix="x", **kwds): """ Initialize ``self``. + + EXAMPLES: + + This is a mixin class, so don't need pickling equality:: + + sage: I = sage.structure.indexed_generators.IndexedGenerators(ZZ) + sage: TestSuite(I).run(skip='_test_pickling') """ self._indices = indices @@ -172,7 +206,10 @@ def print_options(self, **kwds): TESTS:: sage: sorted(F.print_options().items()) - [('bracket', '('), ('latex_bracket', False), ('latex_prefix', None), ('latex_scalar_mult', None), ('generator_cmp', ), ('prefix', 'x'), ('scalar_mult', '*'), ('tensor_symbol', None)] + [('bracket', '('), ('generator_cmp', ), + ('latex_bracket', False), ('latex_prefix', None), + ('latex_scalar_mult', None), ('prefix', 'x'), + ('scalar_mult', '*'), ('tensor_symbol', None)] sage: F.print_options(bracket='[') # reset """ # don't just use kwds.get(...) because I want to distinguish From 46246faecc4d27cfe9b54ac62e9ef36f15ec06fa Mon Sep 17 00:00:00 2001 From: Martin Albrecht Date: Sat, 10 May 2014 18:41:43 +0100 Subject: [PATCH 078/546] fix magma doctest failures --- src/sage/matrix/benchmark.py | 2 ++ src/sage/rings/integer_ring.pyx | 4 ++-- src/sage/rings/polynomial/multi_polynomial_sequence.py | 4 ++-- src/sage/symbolic/expression.pyx | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sage/matrix/benchmark.py b/src/sage/matrix/benchmark.py index 0fe5142f5af..636c5904118 100644 --- a/src/sage/matrix/benchmark.py +++ b/src/sage/matrix/benchmark.py @@ -21,6 +21,8 @@ from sage.misc.misc import alarm, cancel_alarm, cputime from sage.ext.c_lib import AlarmInterrupt +from sage.interfaces.all import magma + verbose = False timeout = 60 diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx index e6ffe15909c..00d5ee0713f 100644 --- a/src/sage/rings/integer_ring.pyx +++ b/src/sage/rings/integer_ring.pyx @@ -1238,8 +1238,8 @@ cdef class IntegerRing_class(PrincipalIdealDomain): EXAMPLES:: - sage: magma(ZZ) # optional - magma - Integer Ring # indirect doctest + sage: magma(ZZ) # indirect doctest, optional - magma + Integer Ring """ return 'IntegerRing()' diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index 47fb3877601..adc8d8c367b 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -757,8 +757,8 @@ def _magma_init_(self, magma): sage: F,s = sr.polynomial_system() sage: F.set_immutable() sage: magma(F) # indirect doctest; optional - magma - Ideal of Polynomial ring of rank 20 over GF(2) - Order: Graded Reverse Lexicographical + Ideal of Boolean polynomial ring of rank 20 over GF(2) + Order: Graded Lexicographical (bit vector word) Variables: k100, k101, k102, k103, x100, x101, x102, x103, w100, w101, w102, w103, s000, s001, s002, s003, k000, k001, k002, k003 Basis: [ diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 16c4fc4f2a8..92e6dc08a85 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -657,7 +657,7 @@ cdef class Expression(CommutativeRingElement): sage: f._magma_init_(magma) '"sin(cos(x^2) + log(x))"' sage: magma(f) # optional - magma - sin(log(x) + cos(x^2)) + sin(cos(x^2) + log(x)) sage: magma(f).Type() # optional - magma MonStgElt """ From 1b5d36de0d5dac98537311e8c95dcb62d8bf2d80 Mon Sep 17 00:00:00 2001 From: Martin Albrecht Date: Sun, 11 May 2014 11:04:26 +0100 Subject: [PATCH 079/546] magma error reporting changed --- src/sage/interfaces/magma.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index bc094d19a2b..c6ee1f012ae 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -183,7 +183,6 @@ ... TypeError: Error evaluating Magma code. ... - Runtime error in '*': Bad argument types Argument types given: RngUPolElt[RngInt], FldRatElt From 4cca67e39f6113afa6fed74567ccf53c709d57a7 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 11 May 2014 14:32:36 -0700 Subject: [PATCH 080/546] Some more fixes; still not done yet. --- src/sage/groups/indexed_free_group.py | 21 +++++++++++++++++ src/sage/monoids/indexed_free_monoid.py | 31 +++++++++++++++++-------- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/sage/groups/indexed_free_group.py b/src/sage/groups/indexed_free_group.py index 9c3d812f9b9..d34c64f1df6 100644 --- a/src/sage/groups/indexed_free_group.py +++ b/src/sage/groups/indexed_free_group.py @@ -380,6 +380,27 @@ def __invert__(self): """ return self.__pow__(-1) + def __floordiv__(self, a): + """ + Return the division of ``self`` by ``a``. + + EXAMPLES:: + + sage: F = FreeGroup(index_set=ZZ, abelian=True) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: elt = a*b*c^3*d^2; elt + F[0]*F[1]*F[2]^3*F[3]^2 + sage: elt // a + F[1]*F[2]^3*F[3]^2 + sage: elt // c + F[0]*F[1]*F[2]^2*F[3]^2 + sage: elt // a*b*d^2 + F[2]^3 + sage: elt // a^4 + F[0]^-3*F[1]*F[2]^3*F[3]^2 + """ + return self * ~a + def __pow__(self, n): """ Raise ``self`` to the power of ``n``. diff --git a/src/sage/monoids/indexed_free_monoid.py b/src/sage/monoids/indexed_free_monoid.py index 3e7d3f441a6..fcd45f41fc9 100644 --- a/src/sage/monoids/indexed_free_monoid.py +++ b/src/sage/monoids/indexed_free_monoid.py @@ -41,8 +41,8 @@ class IndexedMonoidElement(MonoidElement): the free abelian monoid, one could want lex order or have the highest powers first. - Indexed monoid elements are ordered lexicographically w.r.t. the - result of :meth:`_sorted_items` (which for abelian free monoids is + Indexed monoid elements are ordered lexicographically with respect to + the result of :meth:`_sorted_items` (which for abelian free monoids is influenced by the order on the indexing set). """ def __init__(self, F, x): @@ -605,17 +605,21 @@ def cancel(self, elt): Cancel the element ``elt`` out of ``self``. EXAMPLES:: - + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: elt = a*b*c^3*d^2; elt F[0]*F[1]*F[2]^3*F[3]^2 - sage: elt.cancel(a) + sage: elt // a F[1]*F[2]^3*F[3]^2 - sage: elt.cancel(c) + sage: elt // c F[0]*F[1]*F[2]^2*F[3]^2 - sage: elt.cancel(a*b*d^2) + sage: elt // a*b*d^2 F[2]^3 + sage: elt // a^4 + Traceback (most recent call last): + ... + ValueError: invalid cancellation """ d = copy(self._monomial) for k,v in elt._monomial.iteritems(): @@ -743,18 +747,18 @@ def _an_element_(self): pass return x - def gens(self): + def monoid_generators(self): """ - Return the generators of ``self``. + Return the monoid generators of ``self``. EXAMPLES:: sage: F = FreeAbelianMonoid(index_set=ZZ) - sage: F.gens() + sage: F.monoid_generators() Lazy family (Generator map from Integer Ring to Free abelian monoid indexed by Integer Ring(i))_{i in Integer Ring} sage: F = FreeAbelianMonoid(index_set='abcde') - sage: F.gens() + sage: F.monoid_generators() Finite family {'a': F['a'], 'c': F['c'], 'b': F['b'], 'e': F['e'], 'd': F['d']} """ if self._indices.cardinality() == infinity: @@ -762,6 +766,8 @@ def gens(self): return Family(self._indices, gen) return Family(self._indices, self.gen) + gens = monoid_generators + class IndexedFreeMonoid(IndexedMonoid): """ Free monoid with an indexed set of generators. @@ -961,6 +967,11 @@ def cardinality(self): sage: F = FreeAbelianMonoid(index_set=ZZ) sage: F.cardinality() +Infinity + sage: F = FreeAbelianMonoid(index_set=()) + sage: F.cardinality() + 1 """ + if self._indices.cardinality() == 0: + return ZZ.one() return infinity From 0231f73cbf7d50f9985e78a2dc20b80b52ba54a6 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 11 May 2014 15:09:37 -0700 Subject: [PATCH 081/546] Fixed the categories interface. --- src/sage/categories/groups.py | 41 ++++++++++----- src/sage/categories/monoids.py | 25 +++++---- src/sage/groups/indexed_free_group.py | 74 +++++++++++++-------------- 3 files changed, 82 insertions(+), 58 deletions(-) diff --git a/src/sage/categories/groups.py b/src/sage/categories/groups.py index e988a07413b..d51e38b74e4 100644 --- a/src/sage/categories/groups.py +++ b/src/sage/categories/groups.py @@ -55,23 +55,20 @@ def example(self): return GL(4,QQ) @staticmethod - def free(n=None, names='x', index_set=None, abelian=False, **kwds): + def free(index_set=None, names=None, commutative=False, **kwds): r""" - Return the free (abelian) group. + Return the free (commutative) group. INPUT: - - ``n`` -- an integer or ``None`` (default). The number of - generators. If not specified the ``names`` are counted. + - ``index_set`` -- (optional) an index set for the generators; if + an integer, then this represents `\{0, 1, \ldots, n-1\}` - ``names`` -- a string or list/tuple/iterable of strings - (default: ``'x'``). The generator names or name prefix. - - - ``index_set`` -- (optional) an index set for the generators; if - specified then the optional keyword ``abelian`` can be used + (default: ``'x'``); the generator names or name prefix - - ``abelian`` -- (default: ``False``) whether to construct the - free abelian group or the free group + - ``commutative`` -- (default: ``False``) whether to construct the + free commutative (abelian) group or the free group EXAMPLES:: @@ -82,8 +79,28 @@ def free(n=None, names='x', index_set=None, abelian=False, **kwds): sage: F. = Groups().free(); F Free Group on generators {x, y, z} """ - from sage.groups.free_group import FreeGroup - return FreeGroup(n, names, index_set, abelian, **kwds) + from sage.rings.all import ZZ + if index_set in ZZ or (index_set is None and names is not None): + if not commutative: + from sage.groups.free_group import FreeGroup + return FreeGroup(index_set, names, **kwds) + + if names is None: + names = ['F' + repr(i) for i in range(index_set)] + elif isinstance(names, str): + if ',' not in names: + names = [names + repr(i) for i in range(index_set)] + else: + names = names.split(',') + from sage.groups.indexed_group import IndexedFreeAbelianGroup + return IndexedFreeAbelianGroup(names, **kwds) + + if commutative: + from sage.groups.indexed_group import IndexedFreeAbelianGroup + return IndexedFreeAbelianGroup(index_set, **kwds) + + from sage.groups.indexed_group import IndexedFreeGroup + return IndexedFreeGroup(index_set, **kwds) class ParentMethods: diff --git a/src/sage/categories/monoids.py b/src/sage/categories/monoids.py index 553289a23e8..53fb6965ab9 100644 --- a/src/sage/categories/monoids.py +++ b/src/sage/categories/monoids.py @@ -60,7 +60,7 @@ def super_categories(self): return [Semigroups()] @staticmethod - def free(n=None, names=None, index_set=None, abelian=False, **kwds): + def free(n=None, names=None, index_set=None, commutative=False, **kwds): r""" Return a free monoid on `n` generators or with the generators indexed by a set `I`. @@ -72,14 +72,14 @@ def free(n=None, names=None, index_set=None, abelian=False, **kwds): INPUT: - - ``n`` -- an integer + - ``index_set`` -- (optional) an index set for the generators; if + an integer, then this represents `\{0, 1, \ldots, n-1\}` - - ``names`` -- names for the generators + - ``names`` -- a string or list/tuple/iterable of strings + (default: ``'x'``); the generator names or name prefix - - ``index_set`` -- an indexing set for the generators - - - ``abelian`` -- (default: ``False``) whether to construct the - free abelian monoid or the free monoid + - ``commutative`` -- (default: ``False``) whether to construct the + free commutative (abelian) group or the free group EXAMPLES:: @@ -90,8 +90,15 @@ def free(n=None, names=None, index_set=None, abelian=False, **kwds): sage: F. = Monoids().free(); F Free monoid on 3 generators (x, y, z) """ - from sage.monoids.free_monoid import FreeMonoid - return FreeMonoid(n, names, index_set, abelian, **kwds) + from sage.rings.all import ZZ + if index_set in ZZ or (index_set is None and names is not None): + from sage.monoids.free_monoid import FreeMonoid + return FreeMonoid(index_set, names, **kwds) + + if names is not None: + names = normalize_names(n, names) + from sage.monoids.indexed_monoid import IndexedFreeMonoid + return IndexedFreeMonoid(index_set, names=names, **kwds) class ParentMethods: @cached_method diff --git a/src/sage/groups/indexed_free_group.py b/src/sage/groups/indexed_free_group.py index d34c64f1df6..36d13b69344 100644 --- a/src/sage/groups/indexed_free_group.py +++ b/src/sage/groups/indexed_free_group.py @@ -34,10 +34,10 @@ class IndexedFreeGroup(IndexedFreeMonoid, Group): EXAMPLES:: - sage: G = FreeGroup(index_set=ZZ) + sage: G = Groups().free(index_set=ZZ) sage: G Free group indexed by Integer Ring - sage: G = FreeGroup(index_set='abcde') + sage: G = Groups().free(index_set='abcde') sage: G Free group indexed by {'a', 'b', 'c', 'd', 'e'} """ @@ -47,9 +47,9 @@ def __init__(self, indices, prefix, category=None, **kwds): TESTS:: - sage: G = FreeGroup(index_set=ZZ) + sage: G = Groups().free(index_set=ZZ) sage: TestSuite(G).run() - sage: G = FreeGroup(index_set='abc') + sage: G = Groups().free(index_set='abc') sage: TestSuite(G).run() """ Group.__init__(self) @@ -75,13 +75,13 @@ def order(self): EXAMPLES:: - sage: G = FreeGroup(index_set=ZZ) + sage: G = Groups().free(index_set=ZZ) sage: G.order() +Infinity - sage: G = FreeGroup(index_set='abc') + sage: G = Groups().free(index_set='abc') sage: G.order() +Infinity - sage: G = FreeGroup(index_set=[]) + sage: G = Groups().free(index_set=[]) sage: G.order() 1 """ @@ -98,13 +98,13 @@ def is_finite(self): EXAMPLES:: - sage: G = FreeGroup(index_set=ZZ) + sage: G = Groups().free(index_set=ZZ) sage: G.is_finite() False - sage: G = FreeGroup(index_set='abc') + sage: G = Groups().free(index_set='abc') sage: G.is_finite() False - sage: G = FreeGroup(index_set=[]) + sage: G = Groups().free(index_set=[]) sage: G.is_finite() True """ @@ -118,13 +118,13 @@ def rank(self): EXAMPLES:: - sage: G = FreeGroup(index_set=ZZ) + sage: G = Groups().free(index_set=ZZ) sage: G.rank() +Infinity - sage: G = FreeGroup(index_set='abc') + sage: G = Groups().free(index_set='abc') sage: G.rank() 3 - sage: G = FreeGroup(index_set=[]) + sage: G = Groups().free(index_set=[]) sage: G.rank() 0 """ @@ -141,7 +141,7 @@ def __lt__(self, other): EXAMPLES:: - sage: F = FreeGroup(index_set=ZZ) + sage: F = Groups().free(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: a < b True @@ -164,7 +164,7 @@ def __len__(self): EXAMPLES:: - sage: F = FreeGroup(index_set=ZZ) + sage: F = Groups().free(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: elt = a*c^-3*b^-2*a sage: elt.length() @@ -172,7 +172,7 @@ def __len__(self): sage: len(elt) 7 - sage: F = FreeGroup(index_set=ZZ) + sage: F = Groups().free(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: elt = a*c^-3*b^-2*a sage: elt.length() @@ -190,7 +190,7 @@ def _mul_(self, other): EXAMPLES:: - sage: F = FreeGroup(index_set=ZZ) + sage: F = Groups().free(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: a*b^2*e*d F[0]*F[1]^2*F[4]*F[3] @@ -219,7 +219,7 @@ def __invert__(self): EXAMPLES:: - sage: F = FreeGroup(index_set=ZZ) + sage: F = Groups().free(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: x = a*b^2*e^-1*d; ~x F[3]^-1*F[4]*F[1]^-2*F[0]^-1 @@ -237,7 +237,7 @@ def to_word_list(self): EXAMPLES:: - sage: F = FreeGroup(index_set=ZZ) + sage: F = Groups().free(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: x = a*b^2*e*a^-1 sage: x.to_word_list() @@ -253,10 +253,10 @@ class IndexedFreeAbelianGroup(IndexedFreeAbelianMonoid, AbelianGroup): EXAMPLES:: - sage: G = FreeGroup(index_set=ZZ, abelian=True) + sage: G = Groups().free(index_set=ZZ, commutative=True) sage: G Free abelian group indexed by Integer Ring - sage: G = FreeGroup(index_set='abcde', abelian=True) + sage: G = Groups().free(index_set='abcde', commutative=True) sage: G Free abelian group indexed by {'a', 'b', 'c', 'd', 'e'} """ @@ -266,9 +266,9 @@ def __init__(self, indices, prefix, category=None, **kwds): TESTS:: - sage: G = FreeGroup(index_set=ZZ, abelian=True) + sage: G = Groups().free(index_set=ZZ, commutative=True) sage: TestSuite(G).run() - sage: G = FreeGroup(index_set='abc', abelian=True) + sage: G = Groups().free(index_set='abc', commutative=True) sage: TestSuite(G).run() """ AbelianGroup.__init__(self) @@ -279,7 +279,7 @@ def _repr_(self): """ TESTS:: - sage: FreeGroup(index_set=ZZ, abelian=True) + sage: FreeGroup(index_set=ZZ, commutative=True) Free abelian group indexed by Integer Ring """ return 'Free abelian group indexed by {}'.format(self._indices) @@ -291,13 +291,13 @@ def order(self): EXAMPLES:: - sage: G = FreeGroup(index_set=ZZ, abelian=True) + sage: G = Groups().free(index_set=ZZ, commutative=True) sage: G.order() +Infinity - sage: G = FreeGroup(index_set='abc', abelian=True) + sage: G = Groups().free(index_set='abc', commutative=True) sage: G.order() +Infinity - sage: G = FreeGroup(index_set=[], abelian=True) + sage: G = Groups().free(index_set=[], commutative=True) sage: G.order() 1 """ @@ -311,13 +311,13 @@ def is_finite(self): EXAMPLES:: - sage: G = FreeGroup(index_set=ZZ, abelian=True) + sage: G = Groups().free(index_set=ZZ, commutative=True) sage: G.is_finite() False - sage: G = FreeGroup(index_set='abc', abelian=True) + sage: G = Groups().free(index_set='abc', commutative=True) sage: G.is_finite() False - sage: G = FreeGroup(index_set=[], abelian=True) + sage: G = Groups().free(index_set=[], commutative=True) sage: G.is_finite() True """ @@ -330,13 +330,13 @@ def rank(self): EXAMPLES:: - sage: G = FreeGroup(index_set=ZZ, abelian=True) + sage: G = Groups().free(index_set=ZZ, commutative=True) sage: G.rank() +Infinity - sage: G = FreeGroup(index_set='abc', abelian=True) + sage: G = Groups().free(index_set='abc', commutative=True) sage: G.rank() 3 - sage: G = FreeGroup(index_set=[], abelian=True) + sage: G = Groups().free(index_set=[], commutative=True) sage: G.rank() 0 """ @@ -349,7 +349,7 @@ def _mul_(self, other): EXAMPLES:: - sage: F = FreeGroup(index_set=ZZ, abelian=True) + sage: F = Groups().free(index_set=ZZ, commutative=True) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: a*b^2*e^-1*d F[0]*F[1]^2*F[3]*F[4]^-1 @@ -371,7 +371,7 @@ def __invert__(self): EXAMPLES:: - sage: F = FreeGroup(index_set=ZZ, abelian=True) + sage: F = Groups().free(index_set=ZZ, commutative=True) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: x = a*b^2*e^-1*d; ~x F[0]^-1*F[1]^-2*F[3]^-1*F[4] @@ -386,7 +386,7 @@ def __floordiv__(self, a): EXAMPLES:: - sage: F = FreeGroup(index_set=ZZ, abelian=True) + sage: F = Groups().free(index_set=ZZ, commutative=True) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: elt = a*b*c^3*d^2; elt F[0]*F[1]*F[2]^3*F[3]^2 @@ -407,7 +407,7 @@ def __pow__(self, n): EXAMPLES:: - sage: F = FreeGroup(index_set=ZZ, abelian=True) + sage: F = Groups().free(index_set=ZZ, commutative=True) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: x = a*b^2*e^-1*d; x F[0]*F[1]^2*F[3]*F[4]^-1 From 0d8668295f3e49279219a4f45ae49a521680809e Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 11 May 2014 16:29:11 -0700 Subject: [PATCH 082/546] Fixed all doctests except for the pickling. --- src/sage/categories/groups.py | 6 +- src/sage/categories/monoids.py | 2 +- src/sage/groups/free_group.py | 16 +- src/sage/groups/indexed_free_group.py | 306 +++++++++++++++--------- src/sage/monoids/free_abelian_monoid.py | 2 +- src/sage/monoids/free_monoid.py | 11 +- src/sage/monoids/indexed_free_monoid.py | 192 +++++++-------- 7 files changed, 312 insertions(+), 223 deletions(-) diff --git a/src/sage/categories/groups.py b/src/sage/categories/groups.py index d51e38b74e4..13763553a31 100644 --- a/src/sage/categories/groups.py +++ b/src/sage/categories/groups.py @@ -92,14 +92,14 @@ def free(index_set=None, names=None, commutative=False, **kwds): names = [names + repr(i) for i in range(index_set)] else: names = names.split(',') - from sage.groups.indexed_group import IndexedFreeAbelianGroup + from sage.groups.indexed_free_group import IndexedFreeAbelianGroup return IndexedFreeAbelianGroup(names, **kwds) if commutative: - from sage.groups.indexed_group import IndexedFreeAbelianGroup + from sage.groups.indexed_free_group import IndexedFreeAbelianGroup return IndexedFreeAbelianGroup(index_set, **kwds) - from sage.groups.indexed_group import IndexedFreeGroup + from sage.groups.indexed_free_group import IndexedFreeGroup return IndexedFreeGroup(index_set, **kwds) class ParentMethods: diff --git a/src/sage/categories/monoids.py b/src/sage/categories/monoids.py index 53fb6965ab9..c6a524d6f19 100644 --- a/src/sage/categories/monoids.py +++ b/src/sage/categories/monoids.py @@ -97,7 +97,7 @@ def free(n=None, names=None, index_set=None, commutative=False, **kwds): if names is not None: names = normalize_names(n, names) - from sage.monoids.indexed_monoid import IndexedFreeMonoid + from sage.monoids.indexed_free_monoid import IndexedFreeMonoid return IndexedFreeMonoid(index_set, names=names, **kwds) class ParentMethods: diff --git a/src/sage/groups/free_group.py b/src/sage/groups/free_group.py index 5bf5c59f3ad..6d280ea1951 100644 --- a/src/sage/groups/free_group.py +++ b/src/sage/groups/free_group.py @@ -90,8 +90,13 @@ def is_FreeGroup(x): False sage: is_FreeGroup(FreeGroup(0)) True + sage: is_FreeGroup(FreeGroup(index_set=ZZ)) + True """ - return isinstance(x, FreeGroup_class) + if isinstance(x, FreeGroup_class): + return True + from sage.groups.indexed_free_group import IndexedFreeGroup + return isinstance(x, IndexedFreeGroup) def _lexi_gen(zeroes=False): """ @@ -451,6 +456,11 @@ def FreeGroup(n=None, names='x', index_set=None, abelian=False, **kwds): - ``abelian`` -- (default: ``False``) whether to construct a free abelian group or a free group + .. NOTE:: + + If you want to create a free group, it is currently preferential to + use ``Groups().free(...)`` as that does not load GAP. + EXAMPLES:: sage: G. = FreeGroup(); G @@ -505,10 +515,10 @@ def FreeGroup(n=None, names='x', index_set=None, abelian=False, **kwds): names = tuple(normalize_names(n, names)) if index_set is not None or abelian: if abelian: - from sage.groups.indexed_group import IndexedFreeAbelianGroup + from sage.groups.indexed_free_group import IndexedFreeAbelianGroup return IndexedFreeAbelianGroup(index_set, names=names, **kwds) - from sage.groups.indexed_group import IndexedFreeGroup + from sage.groups.indexed_free_group import IndexedFreeGroup return IndexedFreeGroup(index_set, names=names, **kwds) return FreeGroup_class(names) diff --git a/src/sage/groups/indexed_free_group.py b/src/sage/groups/indexed_free_group.py index 36d13b69344..79f0b48c893 100644 --- a/src/sage/groups/indexed_free_group.py +++ b/src/sage/groups/indexed_free_group.py @@ -21,73 +21,39 @@ from copy import copy from sage.categories.groups import Groups +from sage.categories.poor_man_map import PoorManMap from sage.groups.group import Group, AbelianGroup -from sage.monoids.indexed_free_monoid import (IndexedMonoidElement, - IndexedFreeMonoid, IndexedFreeAbelianMonoid) +from sage.monoids.indexed_free_monoid import (IndexedMonoid, + IndexedMonoidElement, IndexedFreeMonoidElement, + IndexedFreeAbelianMonoidElement) from sage.misc.cachefunc import cached_method from sage.rings.integer import Integer from sage.rings.infinity import infinity +from sage.sets.family import Family -class IndexedFreeGroup(IndexedFreeMonoid, Group): +class IndexedGroup(IndexedMonoid): """ - An indexed free group. - - EXAMPLES:: - - sage: G = Groups().free(index_set=ZZ) - sage: G - Free group indexed by Integer Ring - sage: G = Groups().free(index_set='abcde') - sage: G - Free group indexed by {'a', 'b', 'c', 'd', 'e'} + Base class for free (abelian) groups whose generators are indexed + by a set. """ - def __init__(self, indices, prefix, category=None, **kwds): - """ - Initialize ``self``. - - TESTS:: - - sage: G = Groups().free(index_set=ZZ) - sage: TestSuite(G).run() - sage: G = Groups().free(index_set='abc') - sage: TestSuite(G).run() - """ - Group.__init__(self) - category = Groups().or_subcategory(category) - IndexedFreeMonoid.__init__(self, indices, prefix, category, **kwds) - - def _repr_(self): - """ - Return a string representation of ``self`` - - TESTS:: - - sage: FreeGroup(index_set=ZZ) # indirect doctest - Free group indexed by Integer Ring - """ - return 'Free group indexed by {}'.format(self._indices) - def order(self): r""" - Return the number of elements of ``self``. - - This is `\infty` unless this is the trivial group. + Return the number of elements of ``self``, which is `\infty` unless + this is the trivial group. EXAMPLES:: sage: G = Groups().free(index_set=ZZ) sage: G.order() +Infinity - sage: G = Groups().free(index_set='abc') + sage: G = Groups().free(index_set='abc', commutative=True) sage: G.order() +Infinity - sage: G = Groups().free(index_set=[]) + sage: G = Groups().free(index_set=[], commutative=True) sage: G.order() 1 """ - if self.is_finite(): - return Integer(1) - return infinity + return self.cardinality() # TODO: once #10963 is merged, use the categories # Groups().Infinite() / Groups().Finite() and get rid of this @@ -107,6 +73,18 @@ def is_finite(self): sage: G = Groups().free(index_set=[]) sage: G.is_finite() True + + :: + + sage: G = Groups().free(index_set=ZZ, commutative=True) + sage: G.is_finite() + False + sage: G = Groups().free(index_set='abc', commutative=True) + sage: G.is_finite() + False + sage: G = Groups().free(index_set=[], commutative=True) + sage: G.is_finite() + True """ return self.rank() == 0 @@ -127,10 +105,114 @@ def rank(self): sage: G = Groups().free(index_set=[]) sage: G.rank() 0 + + :: + + sage: G = Groups().free(index_set=ZZ, commutative=True) + sage: G.rank() + +Infinity + sage: G = Groups().free(index_set='abc', commutative=True) + sage: G.rank() + 3 + sage: G = Groups().free(index_set=[], commutative=True) + sage: G.rank() + 0 + """ + return self.group_generators().cardinality() + + def group_generators(self): + """ + Return the group generators of ``self``. + + EXAMPLES:: + + sage: G = Groups.free(index_set=ZZ) + sage: G.group_generators() + Lazy family (Generator map from Integer Ring to + Free group indexed by Integer Ring(i))_{i in Integer Ring} + sage: G = Groups().free(index_set='abcde') + sage: sorted(G.group_generators()) + [F['a'], F['b'], F['c'], F['d'], F['e']] """ - return self.gens().cardinality() + if self._indices.cardinality() == infinity: + gen = PoorManMap(self.gen, domain=self._indices, codomain=self, name="Generator map") + return Family(self._indices, gen) + return Family(self._indices, self.gen) - class Element(IndexedFreeMonoid.Element): + gens = group_generators + +class IndexedFreeGroup(IndexedGroup, Group): + """ + An indexed free group. + + EXAMPLES:: + + sage: G = Groups().free(index_set=ZZ) + sage: G + Free group indexed by Integer Ring + sage: G = Groups().free(index_set='abcde') + sage: G + Free group indexed by {'a', 'b', 'c', 'd', 'e'} + """ + def __init__(self, indices, prefix, category=None, **kwds): + """ + Initialize ``self``. + + TESTS:: + + sage: G = Groups().free(index_set=ZZ) + sage: TestSuite(G).run() + sage: G = Groups().free(index_set='abc') + sage: TestSuite(G).run() + """ + Group.__init__(self) + category = Groups().or_subcategory(category) + IndexedGroup.__init__(self, indices, prefix, category, **kwds) + + def _repr_(self): + """ + Return a string representation of ``self`` + + TESTS:: + + sage: Groups().free(index_set=ZZ) # indirect doctest + Free group indexed by Integer Ring + """ + return 'Free group indexed by {}'.format(self._indices) + + @cached_method + def one(self): + """ + Return the identity element of ``self``. + + EXAMPLES:: + + sage: G = Groups().free(ZZ) + sage: G.one() + 1 + """ + return self.element_class(self, ()) + + def gen(self, x): + """ + The generator indexed by ``x`` of ``self``. + + EXAMPLES:: + + sage: G = Groups().free(index_set=ZZ) + sage: G.gen(0) + F[0] + sage: G.gen(2) + F[2] + """ + if x not in self._indices: + raise IndexError("{} is not in the index set".format(x)) + try: + return self.element_class(self, ((self._indices(x),1),)) + except TypeError: # Backup (if it is a string) + return self.element_class(self, ((x,1),)) + + class Element(IndexedFreeMonoidElement): def __lt__(self, other): """ Return whether ``self`` is smaller than ``y``. @@ -141,8 +223,8 @@ def __lt__(self, other): EXAMPLES:: - sage: F = Groups().free(index_set=ZZ) - sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: G = Groups().free(index_set=ZZ) + sage: a,b,c,d,e = [G.gen(i) for i in range(5)] sage: a < b True sage: a^-1*b < b^-1*a @@ -164,16 +246,16 @@ def __len__(self): EXAMPLES:: - sage: F = Groups().free(index_set=ZZ) - sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: G = Groups().free(index_set=ZZ) + sage: a,b,c,d,e = [G.gen(i) for i in range(5)] sage: elt = a*c^-3*b^-2*a sage: elt.length() 7 sage: len(elt) 7 - sage: F = Groups().free(index_set=ZZ) - sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: G = Groups().free(index_set=ZZ) + sage: a,b,c,d,e = [G.gen(i) for i in range(5)] sage: elt = a*c^-3*b^-2*a sage: elt.length() 7 @@ -190,8 +272,8 @@ def _mul_(self, other): EXAMPLES:: - sage: F = Groups().free(index_set=ZZ) - sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: G = Groups().free(index_set=ZZ) + sage: a,b,c,d,e = [G.gen(i) for i in range(5)] sage: a*b^2*e*d F[0]*F[1]^2*F[4]*F[3] sage: (a*b^2*d^2) * (d^-4*b*e) @@ -219,8 +301,8 @@ def __invert__(self): EXAMPLES:: - sage: F = Groups().free(index_set=ZZ) - sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: G = Groups().free(index_set=ZZ) + sage: a,b,c,d,e = [G.gen(i) for i in range(5)] sage: x = a*b^2*e^-1*d; ~x F[3]^-1*F[4]*F[1]^-2*F[0]^-1 sage: x * ~x @@ -237,8 +319,8 @@ def to_word_list(self): EXAMPLES:: - sage: F = Groups().free(index_set=ZZ) - sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: G = Groups().free(index_set=ZZ) + sage: a,b,c,d,e = [G.gen(i) for i in range(5)] sage: x = a*b^2*e*a^-1 sage: x.to_word_list() [(0, 1), (1, 1), (1, 1), (4, 1), (0, -1)] @@ -247,7 +329,7 @@ def to_word_list(self): return [ (k, sign(e)) for k,e in self._sorted_items() for dummy in range(abs(e))] -class IndexedFreeAbelianGroup(IndexedFreeAbelianMonoid, AbelianGroup): +class IndexedFreeAbelianGroup(IndexedGroup, AbelianGroup): """ An indexed free abelian group. @@ -273,84 +355,80 @@ def __init__(self, indices, prefix, category=None, **kwds): """ AbelianGroup.__init__(self) category = Groups().or_subcategory(category) - IndexedFreeAbelianMonoid.__init__(self, indices, prefix, category, **kwds) + IndexedGroup.__init__(self, indices, prefix, category, **kwds) def _repr_(self): """ TESTS:: - sage: FreeGroup(index_set=ZZ, commutative=True) + sage: Groups.free(index_set=ZZ, commutative=True) Free abelian group indexed by Integer Ring """ return 'Free abelian group indexed by {}'.format(self._indices) - def order(self): - r""" - Return the number of elements of ``self``, which is `\infty` unless - this is the trivial group. + def _element_constructor_(self, x=None): + """ + Create an element of ``self`` from ``x``. EXAMPLES:: - sage: G = Groups().free(index_set=ZZ, commutative=True) - sage: G.order() - +Infinity - sage: G = Groups().free(index_set='abc', commutative=True) - sage: G.order() - +Infinity - sage: G = Groups().free(index_set=[], commutative=True) - sage: G.order() - 1 + sage: G = FreeAbelianMonoid(index_set=ZZ) + sage: G(G.gen(2)) + F[2] + sage: G(-5) + F[-5] + sage: G(1) + F[1] + sage: G([[1, 3], [-2, 12]]) + F[-2]^12*F[1]^3 + sage: G({1:3, -2: 12}) + F[-2]^12*F[1]^3 """ - if self.is_finite(): - return Integer(1) - return infinity + if isinstance(x, (list, tuple, dict)): + x = dict(x) + return IndexedGroup._element_constructor_(self, x) - def is_finite(self): + @cached_method + def one(self): """ - Return ``True`` if ``self`` is finite. + Return the identity element of ``self``. EXAMPLES:: sage: G = Groups().free(index_set=ZZ, commutative=True) - sage: G.is_finite() - False - sage: G = Groups().free(index_set='abc', commutative=True) - sage: G.is_finite() - False - sage: G = Groups().free(index_set=[], commutative=True) - sage: G.is_finite() - True + sage: G.one() + 1 """ - return self.rank() == 0 + return self.element_class(self, {}) - def rank(self): + def gen(self, x): """ - Return the rank of ``self``, which is the number of - generators of ``self``. + The generator indexed by ``x`` of ``self``. EXAMPLES:: sage: G = Groups().free(index_set=ZZ, commutative=True) - sage: G.rank() - +Infinity - sage: G = Groups().free(index_set='abc', commutative=True) - sage: G.rank() - 3 - sage: G = Groups().free(index_set=[], commutative=True) - sage: G.rank() - 0 + sage: G.gen(0) + F[0] + sage: G.gen(2) + F[2] """ - return self.gens().cardinality() - - class Element(IndexedFreeAbelianMonoid.Element, IndexedFreeGroup.Element): + if x not in self._indices: + raise IndexError("{} is not in the index set".format(x)) + try: + return self.element_class(self, {self._indices(x):1}) + except TypeError: # Backup (if it is a string) + return self.element_class(self, {x:1}) + + class Element(IndexedFreeAbelianMonoidElement, IndexedFreeGroup.Element): def _mul_(self, other): """ Multiply ``self`` by ``other``. EXAMPLES:: - sage: F = Groups().free(index_set=ZZ, commutative=True) - sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: G = Groups().free(index_set=ZZ, commutative=True) + sage: a,b,c,d,e = [G.gen(i) for i in range(5)] sage: a*b^2*e^-1*d F[0]*F[1]^2*F[3]*F[4]^-1 sage: (a*b^2*d^2) * (d^-4*b^-2*e) @@ -371,8 +449,8 @@ def __invert__(self): EXAMPLES:: - sage: F = Groups().free(index_set=ZZ, commutative=True) - sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: G = Groups().free(index_set=ZZ, commutative=True) + sage: a,b,c,d,e = [G.gen(i) for i in range(5)] sage: x = a*b^2*e^-1*d; ~x F[0]^-1*F[1]^-2*F[3]^-1*F[4] sage: x * ~x @@ -386,15 +464,15 @@ def __floordiv__(self, a): EXAMPLES:: - sage: F = Groups().free(index_set=ZZ, commutative=True) - sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: G = Groups().free(index_set=ZZ, commutative=True) + sage: a,b,c,d,e = [G.gen(i) for i in range(5)] sage: elt = a*b*c^3*d^2; elt F[0]*F[1]*F[2]^3*F[3]^2 sage: elt // a F[1]*F[2]^3*F[3]^2 sage: elt // c F[0]*F[1]*F[2]^2*F[3]^2 - sage: elt // a*b*d^2 + sage: elt // (a*b*d^2) F[2]^3 sage: elt // a^4 F[0]^-3*F[1]*F[2]^3*F[3]^2 @@ -407,8 +485,8 @@ def __pow__(self, n): EXAMPLES:: - sage: F = Groups().free(index_set=ZZ, commutative=True) - sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: G = Groups().free(index_set=ZZ, commutative=True) + sage: a,b,c,d,e = [G.gen(i) for i in range(5)] sage: x = a*b^2*e^-1*d; x F[0]*F[1]^2*F[3]*F[4]^-1 sage: x^3 diff --git a/src/sage/monoids/free_abelian_monoid.py b/src/sage/monoids/free_abelian_monoid.py index cfde2df7cb6..4058d9a574e 100644 --- a/src/sage/monoids/free_abelian_monoid.py +++ b/src/sage/monoids/free_abelian_monoid.py @@ -144,7 +144,7 @@ def FreeAbelianMonoid(n=None, names=None, index_set=None, **kwds): if index_set is not None: if names is not None: names = normalize_names(int(n), names) - from sage.monoids.indexed_monoid import IndexedFreeAbelianMonoid + from sage.monoids.indexed_free_monoid import IndexedFreeAbelianMonoid return IndexedFreeAbelianMonoid(index_set, names=names, **kwds) if names is None: diff --git a/src/sage/monoids/free_monoid.py b/src/sage/monoids/free_monoid.py index 195864f141c..4e21b8de648 100644 --- a/src/sage/monoids/free_monoid.py +++ b/src/sage/monoids/free_monoid.py @@ -126,7 +126,7 @@ def FreeMonoid(n=None, names=None, index_set=None, abelian=False, **kwds): if index_set is not None: if names is not None: names = normalize_names(n, names) - from sage.monoids.indexed_monoid import IndexedFreeMonoid + from sage.monoids.indexed_free_monoid import IndexedFreeMonoid return IndexedFreeMonoid(index_set, names=names, **kwds) if names is None: @@ -148,8 +148,15 @@ def is_FreeMonoid(x): False sage: is_FreeMonoid(FreeAbelianMonoid(0,'')) False + sage: is_FreeMonoid(FreeMonoid(index_set=ZZ)) + True + sage: is_FreeMonoid(FreeAbelianMonoid(index_set=ZZ)) + False """ - return isinstance(x, FreeMonoid_class) + if isinstance(x, FreeMonoid_class): + return True + from sage.monoids.indexed_free_monoid import IndexedFreeMonoid + return isinstance(x, IndexedFreeMonoid) class FreeMonoid_class(Monoid_class): """ diff --git a/src/sage/monoids/indexed_free_monoid.py b/src/sage/monoids/indexed_free_monoid.py index fcd45f41fc9..9c1993d88a5 100644 --- a/src/sage/monoids/indexed_free_monoid.py +++ b/src/sage/monoids/indexed_free_monoid.py @@ -25,6 +25,7 @@ from sage.categories.poor_man_map import PoorManMap from sage.rings.integer import Integer from sage.rings.infinity import infinity +from sage.rings.all import ZZ from sage.sets.finite_enumerated_set import FiniteEnumeratedSet from sage.sets.family import Family @@ -53,7 +54,7 @@ def __init__(self, F, x): sage: F = FreeAbelianMonoid(index_set=ZZ) sage: F(1) - 1 + F[1] sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: x = a^2 * b^3 * a^2 * b^4; x F[0]^4*F[1]^7 @@ -318,32 +319,6 @@ def __ge__(self, y): """ return self.__eq__(y) or self.__gt__(y) - def __len__(self): - """ - Return the length of ``self``. - - EXAMPLES:: - - sage: F = FreeMonoid(index_set=ZZ) - sage: a,b,c,d,e = [F.gen(i) for i in range(5)] - sage: elt = a*c^3*b^2*a - sage: elt.length() - 7 - sage: len(elt) - 7 - - sage: F = FreeAbelianMonoid(index_set=ZZ) - sage: a,b,c,d,e = [F.gen(i) for i in range(5)] - sage: elt = a*c^3*b^2*a - sage: elt.length() - 7 - sage: len(elt) - 7 - """ - return sum(exp for gen,exp in self._monomial) - - length = __len__ - def support(self): """ Return a list of the objects indexing ``self`` with @@ -451,6 +426,7 @@ def __init__(self, F, x): """ IndexedMonoidElement.__init__(self, F, tuple(map(tuple, x))) + def _sorted_items(self): """ Return the sorted items (i.e factors) of ``self``. @@ -498,6 +474,25 @@ def _mul_(self, other): ret += rhs return self.__class__(self.parent(), tuple(ret)) + def __len__(self): + """ + Return the length of ``self``. + + EXAMPLES:: + + sage: F = FreeMonoid(index_set=ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: elt = a*c^3*b^2*a + sage: elt.length() + 7 + sage: len(elt) + 7 + """ + return sum(exp for gen,exp in self._monomial) + + length = __len__ + + class IndexedFreeAbelianMonoidElement(IndexedMonoidElement): """ An element of an indexed free abelian monoid. @@ -587,20 +582,7 @@ def __pow__(self, n): return self.parent().one() return self.__class__(self.parent(), {k:v*n for k,v in self._monomial.iteritems()}) - def dict(self): - """ - Return ``self`` as a dictionary. - - EXAMPLES:: - - sage: F = FreeAbelianMonoid(index_set=ZZ) - sage: a,b,c,d,e = [F.gen(i) for i in range(5)] - sage: (a*c^3).dict() - {0: 1, 2: 3} - """ - return copy(self._monomial) - - def cancel(self, elt): + def __floordiv__(self, elt): """ Cancel the element ``elt`` out of ``self``. @@ -614,7 +596,7 @@ def cancel(self, elt): F[1]*F[2]^3*F[3]^2 sage: elt // c F[0]*F[1]*F[2]^2*F[3]^2 - sage: elt // a*b*d^2 + sage: elt // (a*b*d^2) F[2]^3 sage: elt // a^4 Traceback (most recent call last): @@ -631,6 +613,38 @@ def cancel(self, elt): del d[k] return self.__class__(self.parent(), d) + def __len__(self): + """ + Return the length of ``self``. + + EXAMPLES:: + + sage: F = FreeAbelianMonoid(index_set=ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: elt = a*c^3*b^2*a + sage: elt.length() + 7 + sage: len(elt) + 7 + """ + m = self._monomial + return sum(m[gen] for gen in m) + + length = __len__ + + def dict(self): + """ + Return ``self`` as a dictionary. + + EXAMPLES:: + + sage: F = FreeAbelianMonoid(index_set=ZZ) + sage: a,b,c,d,e = [F.gen(i) for i in range(5)] + sage: (a*c^3).dict() + {0: 1, 2: 3} + """ + return copy(self._monomial) + class IndexedMonoid(Parent, IndexedGenerators, UniqueRepresentation): """ Base class for monoids with an indexed set of generators. @@ -710,11 +724,11 @@ def _element_constructor_(self, x=None): sage: F(-5) F[-5] sage: F(1) - 1 + F[1] sage: F([[1, 3], [-2, 12]]) F[-2]^12*F[1]^3 """ - if x is None or x == 1: + if x is None: return self.one() if x in self._indices: return self.gens()[x] @@ -747,6 +761,31 @@ def _an_element_(self): pass return x + def cardinality(self): + r""" + Return the cardinality of ``self``, which is `\infty` unless this is + the trivial monoid. + + EXAMPLES:: + + sage: F = FreeMonoid(index_set=ZZ) + sage: F.cardinality() + +Infinity + sage: F = FreeMonoid(index_set=()) + sage: F.cardinality() + 1 + + sage: F = FreeAbelianMonoid(index_set=ZZ) + sage: F.cardinality() + +Infinity + sage: F = FreeAbelianMonoid(index_set=()) + sage: F.cardinality() + 1 + """ + if self._indices.cardinality() == 0: + return ZZ.one() + return infinity + def monoid_generators(self): """ Return the monoid generators of ``self``. @@ -758,8 +797,8 @@ def monoid_generators(self): Lazy family (Generator map from Integer Ring to Free abelian monoid indexed by Integer Ring(i))_{i in Integer Ring} sage: F = FreeAbelianMonoid(index_set='abcde') - sage: F.monoid_generators() - Finite family {'a': F['a'], 'c': F['c'], 'b': F['b'], 'e': F['e'], 'd': F['d']} + sage: sorted(F.monoid_generators()) + [F['a'], F['b'], F['c'], F['d'], F['e']] """ if self._indices.cardinality() == infinity: gen = PoorManMap(self.gen, domain=self._indices, codomain=self, name="Generator map") @@ -779,22 +818,13 @@ class IndexedFreeMonoid(IndexedMonoid): For the optional arguments that control the printing, see :class:`~sage.misc.indexed_generators.IndexedGenerators`. - .. NOTE:: - - If there is ambiguity with `1`, we construct the identity in - the monoid instead of the generator:: - - sage: F = FreeMonoid(index_set=ZZ) - sage: F(1) - 1 - sage: F.gen(1) - F[1] - EXAMPLES:: sage: F = FreeMonoid(index_set=ZZ) sage: F.gen(15)^3 * F.gen(2) * F.gen(15) F[15]^3*F[2]*F[15] + sage: F.gen(1) + F[1] Now we examine some of the printing options:: @@ -847,18 +877,6 @@ def gen(self, x): except TypeError: # Backup (if it is a string) return self.element_class(self, ((x,1),)) - def cardinality(self): - r""" - Return the cardinality of ``self``, which is `\infty`. - - EXAMPLES:: - - sage: F = FreeMonoid(index_set=ZZ) - sage: F.cardinality() - +Infinity - """ - return infinity - class IndexedFreeAbelianMonoid(IndexedMonoid): """ Free abelian monoid with an indexed set of generators. @@ -870,22 +888,13 @@ class IndexedFreeAbelianMonoid(IndexedMonoid): For the optional arguments that control the printing, see :class:`~sage.misc.indexed_generators.IndexedGenerators`. - .. NOTE:: - - If there is ambiguity with `1`, we construct the identity in - the monoid instead of the generator:: - - sage: F = FreeAbelianMonoid(index_set=ZZ) - sage: F(1) - 1 - sage: F.gen(1) - F[1] - EXAMPLES:: sage: F = FreeAbelianMonoid(index_set=ZZ) sage: F.gen(15)^3 * F.gen(2) * F.gen(15) F[2]*F[15]^4 + sage: F.gen(1) + F[1] Now we examine some of the printing options:: @@ -906,7 +915,7 @@ def _repr_(self): def _element_constructor_(self, x=None): """ - Create an element of this abelian monoid from ``x``. + Create an element of ``self`` from ``x``. EXAMPLES:: @@ -916,9 +925,11 @@ def _element_constructor_(self, x=None): sage: F(-5) F[-5] sage: F(1) - 1 + F[1] sage: F([[1, 3], [-2, 12]]) F[-2]^12*F[1]^3 + sage: F({1:3, -2: 12}) + F[-2]^12*F[1]^3 """ if isinstance(x, (list, tuple)): x = dict(x) @@ -958,20 +969,3 @@ def gen(self, x): except TypeError: # Backup (if it is a string) return self.element_class(self, {x:1}) - def cardinality(self): - r""" - Return the cardinality of ``self``, which is `\infty`. - - EXAMPLES:: - - sage: F = FreeAbelianMonoid(index_set=ZZ) - sage: F.cardinality() - +Infinity - sage: F = FreeAbelianMonoid(index_set=()) - sage: F.cardinality() - 1 - """ - if self._indices.cardinality() == 0: - return ZZ.one() - return infinity - From db57ef33cae183ca5657fa4a089853e6bdc734e3 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 11 May 2014 16:57:42 -0700 Subject: [PATCH 083/546] More fixes for cardinality methods. --- src/sage/monoids/free_abelian_monoid.py | 3 +++ src/sage/monoids/free_monoid.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/sage/monoids/free_abelian_monoid.py b/src/sage/monoids/free_abelian_monoid.py index 4058d9a574e..f59b6e5080c 100644 --- a/src/sage/monoids/free_abelian_monoid.py +++ b/src/sage/monoids/free_abelian_monoid.py @@ -272,6 +272,9 @@ def cardinality(self): sage: F.cardinality() +Infinity """ + if self.__ngens == 0: + from sage.rings.all import ZZ + return ZZ.one() from sage.rings.infinity import infinity return infinity diff --git a/src/sage/monoids/free_monoid.py b/src/sage/monoids/free_monoid.py index 4e21b8de648..0c956ff45d0 100644 --- a/src/sage/monoids/free_monoid.py +++ b/src/sage/monoids/free_monoid.py @@ -324,6 +324,9 @@ def cardinality(self): sage: F.cardinality() +Infinity """ + if self.__ngens == 0: + from sage.rings.all import ZZ + return ZZ.one() from sage.rings.infinity import infinity return infinity From 1ff1a0483f5e61959a432371b701f593a5dee599 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 11 May 2014 16:58:11 -0700 Subject: [PATCH 084/546] Some whitespace fixes. --- src/sage/monoids/free_abelian_monoid.py | 3 +++ src/sage/monoids/indexed_free_monoid.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/monoids/free_abelian_monoid.py b/src/sage/monoids/free_abelian_monoid.py index f59b6e5080c..2dcb957e4d5 100644 --- a/src/sage/monoids/free_abelian_monoid.py +++ b/src/sage/monoids/free_abelian_monoid.py @@ -103,6 +103,9 @@ def create_object(self, version, key): FreeAbelianMonoid_factory = FreeAbelianMonoidFactory("sage.monoids.free_abelian_monoid.FreeAbelianMonoid_factory") +#from sage.structure.sage_object import register_unpickle_override +#register_unpickle_override('sage.monoids.free_abelian_monoid', 'FreeAbelianMonoid', FreeAbelianMonoid_factory) + def FreeAbelianMonoid(n=None, names=None, index_set=None, **kwds): """ Return a free abelian monoid on `n` generators or with the generators diff --git a/src/sage/monoids/indexed_free_monoid.py b/src/sage/monoids/indexed_free_monoid.py index 9c1993d88a5..9a399ae68cc 100644 --- a/src/sage/monoids/indexed_free_monoid.py +++ b/src/sage/monoids/indexed_free_monoid.py @@ -574,7 +574,7 @@ def __pow__(self, n): """ if not isinstance(n, (int, long, Integer)): raise TypeError("Argument n (= {}) must be an integer".format(n)) - if n < 0: + if n < 0: raise ValueError("Argument n (= {}) must be positive".format(n)) if n == 1: return self @@ -637,7 +637,7 @@ def dict(self): Return ``self`` as a dictionary. EXAMPLES:: - + sage: F = FreeAbelianMonoid(index_set=ZZ) sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: (a*c^3).dict() From caa380bf9a0a039da53ec54bfccc22ca7f3341f1 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Tue, 13 May 2014 02:38:31 +0200 Subject: [PATCH 085/546] Do not unpack hashable entries of tuples when looking for a cache key for unhashable elements in sage.misc.cachefunc --- src/sage/misc/cachefunc.pyx | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index 4a04354ebe6..a5f049989df 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -474,7 +474,7 @@ def _cache_key(o): This function is intended for objects which are not hashable such as `p`-adic numbers. The difference from calling an object's ``_cache_key`` method directly, is that it also works for tuples and unpacks them - recursively. + recursively (if necessary, i.e., if they are not hashable). EXAMPLES:: @@ -492,17 +492,28 @@ def _cache_key(o): sage: _cache_key(o) (1, 2, (3, (((1,),), 0, 20))) + Note that tuples are only partially unpacked if some of its entries are + hashable:: + + sage: o = (matrix([1, 2, 3]), a) + sage: _cache_key(o) + ([1 2 3], (((1,),), 0, 20)) + .. SEEALSO:: :meth:`sage.structure.sage_object.SageObject._cache_key` """ - if isinstance(o, sage.structure.sage_object.SageObject): - o = o._cache_key() - if isinstance(o,tuple): - return tuple(_cache_key(item) for item in o) - else: + try: + hash(o) return o + except TypeError: + if isinstance(o, sage.structure.sage_object.SageObject): + o = o._cache_key() + if isinstance(o,tuple): + return tuple(_cache_key(item) for item in o) + else: + return o cdef class CachedFunction(object): """ From a65ac8524294fde91e021c39b9f227277ec33d23 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Tue, 13 May 2014 02:39:37 +0200 Subject: [PATCH 086/546] improved docstring of SageObject._cache_key --- src/sage/structure/sage_object.pyx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index 070bc71ccb5..5050996f605 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -223,8 +223,11 @@ cdef class SageObject: def _cache_key(self): r""" - Return a hashable key which uniquely identifies this objects for - caching. + Return a key which uniquely identifies this objects for caching. The + output must be hashable itself or a tuple of objects which are hashable + or define a ``_cache_key``. + + This method will only be called if the object itself is not hashable. For most objects this will just be the object itself. However, some immutable objects (such as `p`-adic numbers) can not implement a From f7a77f857a7c8159a89bbfbc10cd70d7e1bf7ddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 13 May 2014 15:48:40 +0200 Subject: [PATCH 087/546] trac #14004 correct 4 failing doctests --- src/sage/categories/finite_posets.py | 23 ++++++++++++----------- src/sage/categories/posets.py | 12 ++++++------ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/sage/categories/finite_posets.py b/src/sage/categories/finite_posets.py index 938b8783c2e..1ee96419e15 100644 --- a/src/sage/categories/finite_posets.py +++ b/src/sage/categories/finite_posets.py @@ -1204,9 +1204,9 @@ def birational_rowmotion(self, labelling): ....: # send order ideal `I` to a `T`-labelling of `P`. ....: dct = {v: TT(v in I) for v in P} ....: return (TT, dct, TT(1), TT(0)) - sage: all( indicator_labelling(P.rowmotion(I)) - ....: == P.birational_rowmotion(indicator_labelling(I)) - ....: for I in P.order_ideals_lattice() ) + sage: all(indicator_labelling(P.rowmotion(I)) + ....: == P.birational_rowmotion(indicator_labelling(I)) + ....: for I in P.order_ideals_lattice(facade=True)) True TESTS: @@ -1374,7 +1374,7 @@ def toggling_orbits(self, vs, element_constructor = set): """ # TODO: implement a generic function taking a set and # bijections on this set, and returning the orbits. - OI = set(self.order_ideals_lattice()) + OI = set(self.order_ideals_lattice(facade=True)) orbits = [] while OI: A = OI.pop() @@ -1738,18 +1738,19 @@ def order_ideals_lattice(self, as_ideals=True, facade=False): if as_ideals: from sage.misc.misc import attrcall from sage.sets.set import Set - ideals = [Set( self.order_ideal(antichain) ) for antichain in self.antichains()] - return LatticePoset((ideals,attrcall("issubset")), + ideals = [Set(self.order_ideal(antichain)) + for antichain in self.antichains()] + return LatticePoset((ideals, attrcall("issubset")), facade=facade) else: from sage.misc.cachefunc import cached_function antichains = [tuple(a) for a in self.antichains()] @cached_function - def is_above(a,xb): - return any(self.is_lequal(xa,xb) for xa in a) - def cmp(a,b): - return all(is_above(a,xb) for xb in b) - return LatticePoset((antichains,cmp), facade=facade) + def is_above(a, xb): + return any(self.is_lequal(xa, xb) for xa in a) + def cmp(a, b): + return all(is_above(a, xb) for xb in b) + return LatticePoset((antichains, cmp), facade=facade) @abstract_method(optional = True) def antichains(self): diff --git a/src/sage/categories/posets.py b/src/sage/categories/posets.py index 2c28ab8d8ee..cc753751895 100644 --- a/src/sage/categories/posets.py +++ b/src/sage/categories/posets.py @@ -402,18 +402,18 @@ def order_ideal_toggle(self, I, v): sage: P.order_ideal_toggle(I, 4) {1, 2, 4} sage: P4 = Posets(4) - sage: all( all( all( P.order_ideal_toggle(P.order_ideal_toggle(I, i), i) == I - ....: for i in range(4) ) - ....: for I in P.order_ideals_lattice() ) - ....: for P in P4 ) + sage: all(all(all(P.order_ideal_toggle(P.order_ideal_toggle(I, i), i) == I + ....: for i in range(4)) + ....: for I in P.order_ideals_lattice(facade=True)) + ....: for P in P4) True """ if not v in I: - if all( u in I for u in self.lower_covers(v) ): + if all(u in I for u in self.lower_covers(v)): from sage.sets.set import Set return I.union(Set({v})) else: - if all( u not in I for u in self.upper_covers(v) ): + if all(u not in I for u in self.upper_covers(v)): from sage.sets.set import Set return I.difference(Set({v})) return I From 05c7c30f33fccdf9fa159ce6fdab0c91f8393225 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Wed, 14 May 2014 21:58:23 +0100 Subject: [PATCH 088/546] Trac 804: fix type error --- src/sage/matrix/matrix0.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index d59ddb9c2b3..3e5a729c297 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -4363,12 +4363,12 @@ cdef class Matrix(sage.structure.element.Matrix): if PY_TYPE_CHECK(self._base_ring, CommutativeRing): return self._lmul_(left) cdef Py_ssize_t r,c - left = self._base_ring(left) + x = self._base_ring(left) cdef Matrix ans ans = self._parent.zero_matrix().__copy__() for r from 0 <= r < self._nrows: for c from 0 <= c < self._ncols: - ans.set_unsafe(r, c, left * self.get_unsafe(r, c)) + ans.set_unsafe(r, c, x * self.get_unsafe(r, c)) return ans cpdef ModuleElement _lmul_(self, RingElement right): @@ -4405,12 +4405,12 @@ cdef class Matrix(sage.structure.element.Matrix): """ # derived classes over a commutative base *just* overload this and not _rmul_ cdef Py_ssize_t r,c - right = self._base_ring(right) + x = self._base_ring(right) cdef Matrix ans ans = self._parent.zero_matrix().__copy__() for r from 0 <= r < self._nrows: for c from 0 <= c < self._ncols: - ans.set_unsafe(r, c, self.get_unsafe(r, c) * right) + ans.set_unsafe(r, c, self.get_unsafe(r, c) * x) return ans cdef sage.structure.element.Matrix _matrix_times_matrix_(self, sage.structure.element.Matrix right): From bf161359d4c73aa81aa60cb77ea1d4a7a2c0473d Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 15 May 2014 15:17:45 -0700 Subject: [PATCH 089/546] Fixed pickling issues and now using index_set as 1st arg to Free*Monoid(). --- src/sage/monoids/free_abelian_monoid.py | 29 ++++++++++++------------- src/sage/monoids/free_monoid.py | 28 +++++++++++++----------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/sage/monoids/free_abelian_monoid.py b/src/sage/monoids/free_abelian_monoid.py index 2dcb957e4d5..2ed347a208e 100644 --- a/src/sage/monoids/free_abelian_monoid.py +++ b/src/sage/monoids/free_abelian_monoid.py @@ -57,8 +57,10 @@ from sage.structure.parent_gens import ParentWithGens, normalize_names from free_abelian_monoid_element import FreeAbelianMonoidElement from sage.rings.integer import Integer +from sage.rings.all import ZZ from sage.structure.factory import UniqueFactory +from sage.misc.decorators import rename_keyword class FreeAbelianMonoidFactory(UniqueFactory): """ @@ -103,10 +105,8 @@ def create_object(self, version, key): FreeAbelianMonoid_factory = FreeAbelianMonoidFactory("sage.monoids.free_abelian_monoid.FreeAbelianMonoid_factory") -#from sage.structure.sage_object import register_unpickle_override -#register_unpickle_override('sage.monoids.free_abelian_monoid', 'FreeAbelianMonoid', FreeAbelianMonoid_factory) - -def FreeAbelianMonoid(n=None, names=None, index_set=None, **kwds): +@rename_keyword(deprecation=15289, n="index_set") +def FreeAbelianMonoid(index_set=None, names=None, **kwds): """ Return a free abelian monoid on `n` generators or with the generators indexed by a set `I`. @@ -118,12 +118,11 @@ def FreeAbelianMonoid(n=None, names=None, index_set=None, **kwds): INPUT: - - ``n`` -- integer + - ``index_set`` -- an indexing set for the generators; if an integer, + then this becomes `\{0, 1, \ldots, n-1\}` - ``names`` -- names of generators - - ``index_set`` -- an indexing set for the generators - OUTPUT: A free abelian monoid. @@ -135,24 +134,24 @@ def FreeAbelianMonoid(n=None, names=None, index_set=None, **kwds): sage: FreeAbelianMonoid(index_set=ZZ) Free abelian monoid indexed by Integer Ring """ - if isinstance(n, str): # Swap args (this works if names is None as well) - names, n = n, names + if isinstance(index_set, str): # Swap args (this works if names is None as well) + names, index_set = index_set, names - if n is None and names is not None: + if index_set is None and names is not None: if isinstance(names, str): - n = names.count(',') + index_set = names.count(',') else: - n = len(names) + index_set = len(names) - if index_set is not None: + if index_set not in ZZ: if names is not None: - names = normalize_names(int(n), names) + names = normalize_names(len(names), names) from sage.monoids.indexed_free_monoid import IndexedFreeAbelianMonoid return IndexedFreeAbelianMonoid(index_set, names=names, **kwds) if names is None: raise ValueError("names must be specified") - return FreeAbelianMonoid_factory(n, names) + return FreeAbelianMonoid_factory(index_set, names) def is_FreeAbelianMonoid(x): """ diff --git a/src/sage/monoids/free_monoid.py b/src/sage/monoids/free_monoid.py index 0c956ff45d0..7581e87ea51 100644 --- a/src/sage/monoids/free_monoid.py +++ b/src/sage/monoids/free_monoid.py @@ -40,6 +40,8 @@ from sage.structure.factory import UniqueFactory from sage.misc.cachefunc import cached_method +from sage.misc.decorators import rename_keyword +from sage.rings.all import ZZ class FreeMonoidFactory(UniqueFactory): """ @@ -73,7 +75,8 @@ def create_object(self, version, key, **kwds): FreeMonoid_factory = FreeMonoidFactory("sage.monoids.free_monoid.FreeMonoid_factory") -def FreeMonoid(n=None, names=None, index_set=None, abelian=False, **kwds): +@rename_keyword(deprecation=15289, n="index_set") +def FreeMonoid(index_set=None, names=None, abelian=False, **kwds): r""" Return a free monoid on `n` generators or with the generators indexed by a set `I`. @@ -85,12 +88,11 @@ def FreeMonoid(n=None, names=None, index_set=None, abelian=False, **kwds): INPUT: - - ``n`` -- integer + - ``index_set`` -- an indexing set for the generators; if an integer, + than this becomes `\{0, 1, \ldots, n-1\}` - ``names`` -- names of generators - - ``index_set`` -- an indexing set for the generators - - ``abelian`` -- (default: ``False``) whether the free monoid is abelian or not @@ -112,26 +114,26 @@ def FreeMonoid(n=None, names=None, index_set=None, abelian=False, **kwds): """ if abelian: from sage.monoids.free_abelian_monoid import FreeAbelianMonoid - return FreeAbelianMonoid(n, names, index_set, **kwds) + return FreeAbelianMonoid(index_set, names, **kwds) - if isinstance(n, str): # Swap args (this works if names is None as well) - names, n = n, names + if isinstance(index_set, str): # Swap args (this works if names is None as well) + names, index_set = index_set, names - if n is None and names is not None: + if index_set is None and names is not None: if isinstance(names, str): - n = names.count(',') + index_set = names.count(',') else: - n = len(names) + index_set = len(names) - if index_set is not None: + if index_set not in ZZ: if names is not None: - names = normalize_names(n, names) + names = normalize_names(len(names), names) from sage.monoids.indexed_free_monoid import IndexedFreeMonoid return IndexedFreeMonoid(index_set, names=names, **kwds) if names is None: raise ValueError("names must be specified") - return FreeMonoid_factory(n, names) + return FreeMonoid_factory(index_set, names) def is_FreeMonoid(x): """ From 164fb4beee6ce00064da5c59591cc5ecc94ef095 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 16 May 2014 12:50:44 -0700 Subject: [PATCH 090/546] Fixed documentation. --- src/doc/en/reference/misc/index.rst | 1 - src/doc/en/reference/structure/index.rst | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/reference/misc/index.rst b/src/doc/en/reference/misc/index.rst index 4a2e6816722..ecbdce90a40 100644 --- a/src/doc/en/reference/misc/index.rst +++ b/src/doc/en/reference/misc/index.rst @@ -49,7 +49,6 @@ Miscellaneous sage/misc/messaging sage/misc/viewer sage/misc/session - sage/misc/indexed_generators Profiling and Performance Testing diff --git a/src/doc/en/reference/structure/index.rst b/src/doc/en/reference/structure/index.rst index bd6d4d32be7..030cbd244e6 100644 --- a/src/doc/en/reference/structure/index.rst +++ b/src/doc/en/reference/structure/index.rst @@ -21,6 +21,7 @@ Basic Structures sage/structure/mutability sage/structure/sequence sage/structure/element_wrapper + sage/structure/indexed_generators sage/structure/global_options sage/sets/cartesian_product From ecdc00fd1e7edf706d8acfadc6325bed93e37843 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 19 May 2014 22:49:14 -0700 Subject: [PATCH 091/546] Fixed doctests because of different processing of input. --- src/sage/monoids/indexed_free_monoid.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/monoids/indexed_free_monoid.py b/src/sage/monoids/indexed_free_monoid.py index 9a399ae68cc..bfafedff4cd 100644 --- a/src/sage/monoids/indexed_free_monoid.py +++ b/src/sage/monoids/indexed_free_monoid.py @@ -60,7 +60,7 @@ def __init__(self, F, x): F[0]^4*F[1]^7 sage: TestSuite(x).run() - sage: F = FreeMonoid(index_set='abcde') + sage: F = FreeMonoid(index_set=tuple('abcde')) sage: a,b,c,d,e = F.gens() sage: a in F True @@ -416,7 +416,7 @@ def __init__(self, F, x): EXAMPLES:: - sage: F = FreeMonoid(index_set='abcde') + sage: F = FreeMonoid(index_set=tuple('abcde')) sage: x = F( [(1, 2), (0, 1), (3, 2), (0, 1)] ) sage: y = F( ((1, 2), (0, 1), [3, 2], [0, 1]) ) sage: z = F( reversed([(0, 1), (3, 2), (0, 1), (1, 2)]) ) @@ -663,7 +663,7 @@ def __classcall__(cls, indices, prefix="F", **kwds): sage: F = FreeAbelianMonoid(index_set=['a','b','c']) sage: G = FreeAbelianMonoid(index_set=('a','b','c')) - sage: H = FreeAbelianMonoid(index_set='abc') + sage: H = FreeAbelianMonoid(index_set=tuple('abc')) sage: F is G and F is H True @@ -696,12 +696,12 @@ def __init__(self, indices, prefix, category=None, names=None, **kwds): sage: F = FreeMonoid(index_set=ZZ) sage: TestSuite(F).run() - sage: F = FreeMonoid(index_set='abcde') + sage: F = FreeMonoid(index_set=tuple('abcde')) sage: TestSuite(F).run() sage: F = FreeAbelianMonoid(index_set=ZZ) sage: TestSuite(F).run() - sage: F = FreeAbelianMonoid(index_set='abcde') + sage: F = FreeAbelianMonoid(index_set=tuple('abcde')) sage: TestSuite(F).run() """ self._indices = indices @@ -743,7 +743,7 @@ def _an_element_(self): sage: G = FreeAbelianMonoid(index_set=ZZ) sage: G.an_element() F[-1]^3*F[0]*F[1]^3 - sage: G = FreeMonoid(index_set='ab') + sage: G = FreeMonoid(index_set=tuple('ab')) sage: G.an_element() F['a']^2*F['b']^2 """ @@ -796,7 +796,7 @@ def monoid_generators(self): sage: F.monoid_generators() Lazy family (Generator map from Integer Ring to Free abelian monoid indexed by Integer Ring(i))_{i in Integer Ring} - sage: F = FreeAbelianMonoid(index_set='abcde') + sage: F = FreeAbelianMonoid(index_set=tuple('abcde')) sage: sorted(F.monoid_generators()) [F['a'], F['b'], F['c'], F['d'], F['e']] """ From 601d39acde0fd78f497e945c3a3e941d39a48150 Mon Sep 17 00:00:00 2001 From: Andrey Novoseltsev Date: Thu, 22 May 2014 16:18:18 -0600 Subject: [PATCH 092/546] Construct vectors in base_ring, not ZZ --- src/sage/numerical/interactive_simplex_method.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/numerical/interactive_simplex_method.py b/src/sage/numerical/interactive_simplex_method.py index cf0307dacf6..7d363b71ded 100644 --- a/src/sage/numerical/interactive_simplex_method.py +++ b/src/sage/numerical/interactive_simplex_method.py @@ -1600,7 +1600,7 @@ def feasible_dictionary(self, auxiliary_dictionary): n = len(c) A = A.matrix_from_columns(range(k) + range(k + 1, n)) b = copy(b) - c = vector(ZZ, n - 1) + c = vector(self.base_ring(), n - 1) for cj, xj in zip(*self.Abcx()[-2:]): if xj in N: c[N.index(xj)] += cj From 77b3c43c2d4049fc74f305a10e36fde35602444f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 25 May 2014 17:55:11 +0200 Subject: [PATCH 093/546] trac #14004 removed strange character --- src/sage/combinat/root_system/root_lattice_realizations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/root_system/root_lattice_realizations.py b/src/sage/combinat/root_system/root_lattice_realizations.py index c9a26c31970..d1f09f48f09 100644 --- a/src/sage/combinat/root_system/root_lattice_realizations.py +++ b/src/sage/combinat/root_system/root_lattice_realizations.py @@ -851,7 +851,7 @@ def nonnesting_partition_lattice(self, facade=False): REFERENCES: .. [Reiner97] Victor Reiner. *Non-crossing partitions for - classical reflection groups*. Discrete Mathematics 177 (1997) + classical reflection groups*. Discrete Mathematics 177 (1997) .. [Arm06] Drew Armstrong. *Generalized Noncrossing Partitions and Combinatorics of Coxeter Groups*. :arxiv:`math/0611106` """ @@ -862,7 +862,7 @@ def generalized_nonnesting_partition_lattice(self, m, facade=False): Return the lattice of `m`-nonnesting partitions This has been defined by Athanasiadis, see chapter 5 of [Arm06]_. - + INPUT: - `m` -- integer From 3598925cfc2c286aa82f0ba2786ef2fea92a90e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 25 May 2014 18:47:33 +0200 Subject: [PATCH 094/546] trac #14004 one more test for B3 --- src/sage/combinat/root_system/root_lattice_realizations.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/combinat/root_system/root_lattice_realizations.py b/src/sage/combinat/root_system/root_lattice_realizations.py index d1f09f48f09..14aefc123e6 100644 --- a/src/sage/combinat/root_system/root_lattice_realizations.py +++ b/src/sage/combinat/root_system/root_lattice_realizations.py @@ -848,6 +848,13 @@ def nonnesting_partition_lattice(self, facade=False): sage: P.coxeter_transformation()**10 == 1 True + sage: R = RootSystem(['B', 3]) + sage: RS = R.root_lattice() + sage: P = RS.nonnesting_partition_lattice(); P + Finite lattice containing 20 elements + sage: P.coxeter_transformation()**7 == 1 + True + REFERENCES: .. [Reiner97] Victor Reiner. *Non-crossing partitions for From 3d74cad55ddc6d1a0c64356a59c88cdac1a6f2d9 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Mon, 26 May 2014 15:46:44 +0200 Subject: [PATCH 095/546] 8734: rename private function, doctest it --- src/sage/interfaces/maxima_lib.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 94441c79e38..74dcca1b614 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -575,7 +575,7 @@ def _create(self, value, name=None): except RuntimeError as error: s = str(error) if "Is" in s: # Maxima asked for a condition - self.missing_assumption(s) + self._missing_assumption(s) else: raise error return name @@ -743,7 +743,7 @@ def sr_integral(self,*args): # if "divergent" in s or 'Principal Value' in s: raise ValueError("Integral is divergent.") elif "Is" in s: # Maxima asked for a condition - self.missing_assumption(s) + self._missing_assumption(s) else: raise @@ -803,7 +803,7 @@ def sr_sum(self,*args): # if "divergent" in s or 'Pole encountered' in s: raise ValueError("Sum is divergent.") elif "Is" in s: # Maxima asked for a condition - self.missing_assumption(s) + self._missing_assumption(s) else: raise @@ -861,7 +861,7 @@ def sr_limit(self,expr,v,a,dir=None): except RuntimeError as error: s = str(error) if "Is" in s: # Maxima asked for a condition - self.missing_assumption(s) + self._missing_assumption(s) else: raise @@ -882,10 +882,19 @@ def sr_tlimit(self,expr,v,a,dir=None): L.append(max_minus) return max_to_sr(maxima_eval(([max_tlimit],L))) - def missing_assumption(self,errstr): + def _missing_assumption(self,errstr): """ Helper function for unified handling of failed computation because an assumption was missing. + + EXAMPLES:: + + sage: from sage.interfaces.maxima_lib import maxima_lib + sage: maxima_lib._missing_assumption('Is xyz a thing?') + Traceback (most recent call last): + ... + ValueError: Computation failed ... + Is xyz a thing? """ j = errstr.find('Is ') errstr = errstr[j:] From d410adc8bf48ee125a1edc1dcb3d7da986a02756 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Tue, 27 May 2014 20:46:54 +0200 Subject: [PATCH 096/546] FiniteStateMachine.process: Refuse Non-Deterministic Finite State Machines Previously, a (more or less) random path was chosen in case of ambiguity in FiniteStateMachine.process. Instead, we now raise a NotImplementedError. --- src/sage/combinat/finite_state_machine.py | 44 ++++++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 30b80ea6661..56d88781d9b 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -4438,10 +4438,6 @@ def process(self, *args, **kwargs): processing (in the case the finite state machine runs as transducer). - Note that in the case the finite state machine is not - deterministic, one possible path is gone. This means, that in - this case the output can be wrong. - EXAMPLES:: sage: from sage.combinat.finite_state_machine import FSMState @@ -4464,6 +4460,25 @@ def process(self, *args, **kwargs): sage: [NAF.process(w)[0] for w in [[0], [0, 1], [1, 1], [0, 1, 0, 1], ....: [0, 1, 1, 1, 0], [1, 0, 0, 1, 1]]] [True, True, False, True, False, False] + + Non-deterministic finite state machines cannot be handeled. + + :: + + sage: T = Transducer([(0, 1, 0, 0), (0, 2, 0, 0)], + ....: initial_states=[0]) + sage: T.process([0]) + Traceback (most recent call last): + ... + NotImplementedError: Non-deterministic path encountered when processing input + sage: T = Transducer([(0, 1, [0, 0], 0), (0, 2, [0, 0, 1], 0)], + ....: initial_states=[0]) + sage: T.process([0]) + (False, None, None) + sage: T.process([0, 0]) + Traceback (most recent call last): + ... + NotImplementedError: Non-deterministic path encountered when processing input """ it = self.iter_process(*args, **kwargs) for _ in it: @@ -7619,7 +7634,10 @@ def determinisation(self): sage: auto.is_deterministic() False sage: auto.process(list('aaab')) - (False, 'A') + Traceback (most recent call last): + ... + NotImplementedError: Non-deterministic path encountered + when processing input sage: auto.states() ['A', 'C', 'B'] sage: Ddet = auto.determinisation() @@ -8751,6 +8769,22 @@ def next(self): found = True except ValueError: pass + + if found: + transitions = ( t for t + in self.current_state.transitions + if t.word_in[:len(next_word)] + == next_word ) + try: + transitions.next() + transitions.next() + raise NotImplementedError("Non-deterministic " + "path encountered " + "when processing " + "input") + except StopIteration: + pass + except StopIteration: # this means input tape is finished if len(next_word) > 0: From 000c74e9f05d89ead828c7220af5a89a75b7bfd7 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Wed, 28 May 2014 07:47:13 +0200 Subject: [PATCH 097/546] FiniteStateMachine.process: raise NotImplementedError when epsilon-transition is reached. Further code cleanup --- src/sage/combinat/finite_state_machine.py | 77 +++++++++++++++-------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 56d88781d9b..efbac0f8579 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -643,6 +643,35 @@ def full_group_by(l, key=lambda x: x): elements[s].append(item) return [(original_keys[s], values ) for (s, values) in elements.items()] + +def startswith(list, prefix): + """ + Determine whether list starts with the given prefix. + + INPUT: + + - ``list`` -- list + - ``prefix`` -- list representing the prefix + + OUTPUT: + + ``True`` or ``False``. + + Similar to :meth:`str.startswith`. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import startswith + sage: startswith([1, 2, 3], [1, 2]) + True + sage: startswith([1], [1, 2]) + False + sage: startswith([1, 3, 2], [1, 2]) + False + """ + + return list[:len(prefix)] == prefix + #***************************************************************************** FSMEmptyWordSymbol = '-' @@ -4471,14 +4500,21 @@ def process(self, *args, **kwargs): Traceback (most recent call last): ... NotImplementedError: Non-deterministic path encountered when processing input - sage: T = Transducer([(0, 1, [0, 0], 0), (0, 2, [0, 0, 1], 0)], - ....: initial_states=[0]) + sage: T = Transducer([(0, 1, [0, 0], 0), (0, 2, [0, 0, 1], 0), + ....: (0, 1, 1, 2), (1, 0, [], 1), (1, 1, 1, 3)], + ....: initial_states=[0], final_states=[0, 1]) sage: T.process([0]) (False, None, None) sage: T.process([0, 0]) Traceback (most recent call last): ... NotImplementedError: Non-deterministic path encountered when processing input + sage: T.process([1]) + (True, 1, [2]) + sage: T.process([1, 1]) + Traceback (most recent call last): + ... + NotImplementedError: process cannot handle epsilon transition leaving state 1. """ it = self.iter_process(*args, **kwargs) for _ in it: @@ -7854,12 +7890,6 @@ def process(self, *args, **kwargs): could not be processed, i.e., when at one point no transition to go could be found.). - Note that in the case the automaton is not - deterministic, one possible path is gone. This means that in - this case the output can be wrong. Use - :meth:`.determinisation` to get a deterministic automaton - machine and try again. - By setting ``FSMOldProcessOutput`` to ``False`` the new desired output is produced. @@ -8456,10 +8486,6 @@ def process(self, *args, **kwargs): - the third gives a list of the output labels used during processing. - Note that in the case the transducer is not - deterministic, one possible path is gone. This means, that in - this case the output can be wrong. - By setting ``FSMOldProcessOutput`` to ``False`` the new desired output is produced. @@ -8763,6 +8789,11 @@ def next(self): try: while not found: next_word.append(self.read_letter()) + if len(next_word) == 1 and any(not t.word_in + for t in self.current_state.transitions): + raise NotImplementedError( + "process cannot handle epsilon transition " + "leaving state %s." % self.current_state.label()) try: transition = self.get_next_transition( next_word) @@ -8770,20 +8801,14 @@ def next(self): except ValueError: pass - if found: - transitions = ( t for t - in self.current_state.transitions - if t.word_in[:len(next_word)] - == next_word ) - try: - transitions.next() - transitions.next() - raise NotImplementedError("Non-deterministic " - "path encountered " - "when processing " - "input") - except StopIteration: - pass + if found and any( + t is not transition and startswith(t.word_in, + next_word) + for t in self.current_state.transitions): + raise NotImplementedError("Non-deterministic " + "path encountered " + "when processing " + "input") except StopIteration: # this means input tape is finished From 35f60813c540f3eaabc45200f3e61f3a227ad1d2 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Sat, 31 May 2014 00:17:59 +0100 Subject: [PATCH 098/546] Trac 12947: add doctest --- src/sage/interfaces/maxima_lib.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 066cf1dff6e..e6d72990dc0 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -724,12 +724,18 @@ def sr_integral(self,*args): 4 This definite integral returned zero (incorrectly) in at least - maxima-5.23. The correct answer is now given (:trac:`11591`):: + Maxima 5.23. The correct answer is now given (:trac:`11591`):: sage: f = (x^2)*exp(x) / (1+exp(x))^2 sage: integrate(f, (x, -infinity, infinity)) 1/3*pi^2 + The following integral was computed incorrectly in versions of + Maxima before 5.27 (see :trac:`12947`):: + + sage: integrate(x*cos(x^3),(x,0,1/2)).n() + 0.124756040961038 + """ try: return max_to_sr(maxima_eval(([max_integrate],[sr_to_max(SR(a)) for a in args]))) From 3d9a07cd45fd002020a1f67cb2394af5a38d76a0 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Sat, 31 May 2014 11:14:48 +0100 Subject: [PATCH 099/546] Trac 12947: add tolerance to doctest --- src/sage/interfaces/maxima_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index e6d72990dc0..7669a41e800 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -733,7 +733,7 @@ def sr_integral(self,*args): The following integral was computed incorrectly in versions of Maxima before 5.27 (see :trac:`12947`):: - sage: integrate(x*cos(x^3),(x,0,1/2)).n() + sage: integrate(x*cos(x^3),(x,0,1/2)).n() # abs tol 1e-16 0.124756040961038 """ From ec5e1a9755740e75f777e7c8184c2797d74f51d0 Mon Sep 17 00:00:00 2001 From: Ivan Andrus Date: Sun, 1 Jun 2014 22:52:15 -0600 Subject: [PATCH 100/546] Ignore files from XCode --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index a9b6be08742..7808c3bbd61 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,9 @@ .idea .iml +# XCode +xcuserdata/ + # Eclipse *.pydevproject .project From dcb66604cff05ea491dd29367d441229277153b1 Mon Sep 17 00:00:00 2001 From: Ivan Andrus Date: Sun, 1 Jun 2014 22:53:26 -0600 Subject: [PATCH 101/546] Added files from new XCode --- .../contents.xcworkspacedata | 7 ++++ .../xcshareddata/Sage.xccheckout | 41 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/mac-app/Sage.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 src/mac-app/Sage.xcodeproj/project.xcworkspace/xcshareddata/Sage.xccheckout diff --git a/src/mac-app/Sage.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/src/mac-app/Sage.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000000..17080955d9c --- /dev/null +++ b/src/mac-app/Sage.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/src/mac-app/Sage.xcodeproj/project.xcworkspace/xcshareddata/Sage.xccheckout b/src/mac-app/Sage.xcodeproj/project.xcworkspace/xcshareddata/Sage.xccheckout new file mode 100644 index 00000000000..caf5cd0ac1e --- /dev/null +++ b/src/mac-app/Sage.xcodeproj/project.xcworkspace/xcshareddata/Sage.xccheckout @@ -0,0 +1,41 @@ + + + + + IDESourceControlProjectFavoriteDictionaryKey + + IDESourceControlProjectIdentifier + E0D9769F-5D54-4AC1-A951-4D318D9DD866 + IDESourceControlProjectName + Sage + IDESourceControlProjectOriginsDictionary + + 14FAB7F0-79B5-47C3-8938-EE51EF938D3B + git://github.com/sagemath/sage.git + + IDESourceControlProjectPath + src/mac-app/Sage.xcodeproj/project.xcworkspace + IDESourceControlProjectRelativeInstallPathDictionary + + 14FAB7F0-79B5-47C3-8938-EE51EF938D3B + ../../../.. + + IDESourceControlProjectURL + git://github.com/sagemath/sage.git + IDESourceControlProjectVersion + 110 + IDESourceControlProjectWCCIdentifier + 14FAB7F0-79B5-47C3-8938-EE51EF938D3B + IDESourceControlProjectWCConfigurations + + + IDESourceControlRepositoryExtensionIdentifierKey + public.vcs.git + IDESourceControlWCCIdentifierKey + 14FAB7F0-79B5-47C3-8938-EE51EF938D3B + IDESourceControlWCCName + sage + + + + From ef460c68b008eb96952c1211f08dd3e4d8d8426f Mon Sep 17 00:00:00 2001 From: Sara Kropf Date: Mon, 2 Jun 2014 13:04:20 +0200 Subject: [PATCH 102/546] Small correction. --- src/sage/combinat/finite_state_machine.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index efbac0f8579..b392a072156 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -4499,7 +4499,7 @@ def process(self, *args, **kwargs): sage: T.process([0]) Traceback (most recent call last): ... - NotImplementedError: Non-deterministic path encountered when processing input + NotImplementedError: Non-deterministic path encountered when processing input. sage: T = Transducer([(0, 1, [0, 0], 0), (0, 2, [0, 0, 1], 0), ....: (0, 1, 1, 2), (1, 0, [], 1), (1, 1, 1, 3)], ....: initial_states=[0], final_states=[0, 1]) @@ -4508,7 +4508,7 @@ def process(self, *args, **kwargs): sage: T.process([0, 0]) Traceback (most recent call last): ... - NotImplementedError: Non-deterministic path encountered when processing input + NotImplementedError: Non-deterministic path encountered when processing input. sage: T.process([1]) (True, 1, [2]) sage: T.process([1, 1]) @@ -7673,7 +7673,7 @@ def determinisation(self): Traceback (most recent call last): ... NotImplementedError: Non-deterministic path encountered - when processing input + when processing input. sage: auto.states() ['A', 'C', 'B'] sage: Ddet = auto.determinisation() @@ -8808,7 +8808,7 @@ def next(self): raise NotImplementedError("Non-deterministic " "path encountered " "when processing " - "input") + "input.") except StopIteration: # this means input tape is finished From 83a6b0efee2ea0ace43a09758a8156115d414635 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Mon, 2 Jun 2014 17:26:52 +0100 Subject: [PATCH 103/546] creates game_thoery folder and computes shapley value --- src/sage/all.py | 2 + src/sage/game_theory/__init__.py | 1 + src/sage/game_theory/all.py | 1 + src/sage/game_theory/cooperative_game.py | 69 ++++++++++++++++++++++++ 4 files changed, 73 insertions(+) create mode 100644 src/sage/game_theory/__init__.py create mode 100644 src/sage/game_theory/all.py create mode 100644 src/sage/game_theory/cooperative_game.py diff --git a/src/sage/all.py b/src/sage/all.py index f4bd4f7f76d..ade355dac4e 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -171,6 +171,8 @@ from sage.matroids.all import * +from sage.game_theory.all import * + # Lazily import notebook functions and interacts (#15335) lazy_import('sagenb.notebook.notebook_object', 'notebook') lazy_import('sagenb.notebook.notebook_object', 'inotebook') diff --git a/src/sage/game_theory/__init__.py b/src/sage/game_theory/__init__.py new file mode 100644 index 00000000000..c9fecacd721 --- /dev/null +++ b/src/sage/game_theory/__init__.py @@ -0,0 +1 @@ +import all diff --git a/src/sage/game_theory/all.py b/src/sage/game_theory/all.py new file mode 100644 index 00000000000..2357d08a931 --- /dev/null +++ b/src/sage/game_theory/all.py @@ -0,0 +1 @@ +from cooperative_game import CooperativeGame diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py new file mode 100644 index 00000000000..185fe7f2c6f --- /dev/null +++ b/src/sage/game_theory/cooperative_game.py @@ -0,0 +1,69 @@ +from itertools import permutations + +class CooperativeGame(): + def __init__(self, characteristic_function, player_list): + self.char_fun = characteristic_function + self.number_players = len(player_list) + self.player_list = player_list + + def shapley_value(self): + r""" + Return the payoff vector for co-operative game. + """ + payoff_vector = [] + for i in self.player_list: + player_contribution = self.marginal_contributions(i) + average = sum(player_contribution) / len(player_contribution) + payoff_vector.append(average) + return payoff_vector + + def is_monotone(): + r""" + Returns True if co-operative game is monotonic. + """ + + def is_superadditive(): + r""" + Returns True if co-operative game is superadditive. + """ + + def marginal_contributions(self, player): + contributions = [] + for i in permutations(self.player_list): + contributions.append(self.marginal_of_pi(player, i)) + return contributions + + def marginal_of_pi(self, player, pi): + player_and_pred = self.get_predecessors_and_player(player, pi) + predecessors = [x for x in player_and_pred if x != player] + if predecessors == None: + predecessors = () + else: + predecessors = tuple(predecessors) + player_and_pred = tuple(player_and_pred) + value = self.char_fun[player_and_pred] - self.char_fun[predecessors] + return value + + def get_predecessors_and_player(self, player, permutation): + predecessors = [] + for k in permutation: + if k == player: + predecessors.append(k) + predecessors.sort() + return predecessors + else: + predecessors.append(k) + +""" +test_function = {(): 0, + ('A',): 6, + ('B',): 12, + ('C',): 42, + ('A', 'B',): 12, + ('A', 'C',): 42, + ('B', 'C',): 42, + ('A', 'B', 'C',): 42} + +test_game = CooperativeGame(test_function, ['A', 'B', 'C']) +print test_game.shapley_value() +""" From 3707dbb4790618c4819f572c1d33d91cbe3efd17 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Mon, 2 Jun 2014 17:40:41 +0100 Subject: [PATCH 104/546] adds starting documentation --- src/sage/game_theory/cooperative_game.py | 47 +++++++++++++++++------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 185fe7f2c6f..7b05bdcda56 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -1,3 +1,36 @@ +r""" +Co-operative games with N players. + + + +AUTHOR:: +- JAMES CAMPBELL 06-2014 + + +EXAMPLES:: + sage: test_function = {(): 0, + ....: ('A',): 6, + ....: ('B',): 12, + ....: ('C',): 42, + ....: ('A', 'B',): 12, + ....: ('A', 'C',): 42, + ....: ('B', 'C',): 42, + ....: ('A', 'B', 'C',): 42} + sage: + sage: test_game = CooperativeGame(test_function, ['A', 'B', 'C']) + sage: print test_game.shapley_value() + [2, 5, 35] +""" + +#***************************************************************************** +# Copyright (C) 2013 YOUR NAME +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** from itertools import permutations class CooperativeGame(): @@ -53,17 +86,3 @@ def get_predecessors_and_player(self, player, permutation): return predecessors else: predecessors.append(k) - -""" -test_function = {(): 0, - ('A',): 6, - ('B',): 12, - ('C',): 42, - ('A', 'B',): 12, - ('A', 'C',): 42, - ('B', 'C',): 42, - ('A', 'B', 'C',): 42} - -test_game = CooperativeGame(test_function, ['A', 'B', 'C']) -print test_game.shapley_value() -""" From 4317f0f342570139c4738a3790d4f8bf23b4e8c3 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Mon, 2 Jun 2014 18:07:26 +0100 Subject: [PATCH 105/546] improve docs --- src/sage/game_theory/cooperative_game.py | 34 +++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 7b05bdcda56..18a1f2c814c 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -4,11 +4,32 @@ AUTHOR:: -- JAMES CAMPBELL 06-2014 +- James Campbell 06-2014 EXAMPLES:: - sage: test_function = {(): 0, +Basic example of how to implement a co-operative game. + +:: + +sage: test_function = {(): 0, + ....: ('1',): 6, + ....: ('2',): 12, + ....: ('3',): 42, + ....: ('1', '2',): 12, + ....: ('1', '3',): 42, + ....: ('2', '3',): 42, + ....: ('1', '2', '3',): 42} + sage: + sage: test_game = CooperativeGame(test_function, ['A', 'B', 'C']) + sage: print test_game.shapley_value() + [2, 5, 35] + +We can also use strings instead of numbers. + +:: + + sage: letter_function = {(): 0, ....: ('A',): 6, ....: ('B',): 12, ....: ('C',): 42, @@ -17,13 +38,13 @@ ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} sage: - sage: test_game = CooperativeGame(test_function, ['A', 'B', 'C']) - sage: print test_game.shapley_value() + sage: letter_game = CooperativeGame(test_function, ['A', 'B', 'C']) + sage: print letter_game.shapley_value() [2, 5, 35] """ #***************************************************************************** -# Copyright (C) 2013 YOUR NAME +# Copyright (C) 2014 James Campbell james.campbell@tanti.org.uk # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -33,6 +54,7 @@ #***************************************************************************** from itertools import permutations + class CooperativeGame(): def __init__(self, characteristic_function, player_list): self.char_fun = characteristic_function @@ -46,7 +68,7 @@ def shapley_value(self): payoff_vector = [] for i in self.player_list: player_contribution = self.marginal_contributions(i) - average = sum(player_contribution) / len(player_contribution) + average = sum(player_contribution) / len(player_contribution) payoff_vector.append(average) return payoff_vector From dc877d9b46e5bb70a0bbcc8f4f6a4b586e10063c Mon Sep 17 00:00:00 2001 From: Sara Kropf Date: Mon, 2 Jun 2014 13:14:30 +0200 Subject: [PATCH 106/546] determine_alphabets considers final output words --- src/sage/combinat/finite_state_machine.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 30b80ea6661..356ece93bdb 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -3794,12 +3794,14 @@ def determine_alphabets(self, reset=True): sage: T = Transducer([(1, 1, 1, 0), (1, 2, 2, 1), ....: (2, 2, 1, 1), (2, 2, 0, 0)], + ....: final_states=[1], ....: determine_alphabets=False) + sage: T.state(1).final_word_out = [1, 4] sage: (T.input_alphabet, T.output_alphabet) (None, None) sage: T.determine_alphabets() sage: (T.input_alphabet, T.output_alphabet) - ([0, 1, 2], [0, 1]) + ([0, 1, 2], [0, 1, 4]) """ if reset: ain = set() @@ -3813,6 +3815,9 @@ def determine_alphabets(self, reset=True): ain.add(letter) for letter in t.word_out: aout.add(letter) + for s in self.iter_final_states(): + for letter in s.final_word_out: + aout.add(letter) self.input_alphabet = list(ain) self.output_alphabet = list(aout) From 91915b13a40b1e55524cd7d631996915be7f4a95 Mon Sep 17 00:00:00 2001 From: Sara Kropf Date: Mon, 2 Jun 2014 13:47:10 +0200 Subject: [PATCH 107/546] transposition not implemented for transducers with non-empty final word out --- src/sage/combinat/finite_state_machine.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 356ece93bdb..f1fbe3fd275 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -5743,6 +5743,23 @@ def transposition(self): ....: initial_states=['1'], final_states=['1', '2']) sage: aut.transposition().initial_states() ['1', '2'] + + + TESTS: + + If a final state of ``self`` has a non-empty final output word, + transposition is not implemented:: + + sage: T = Transducer([('1', '1', 1, 0), ('1', '2', 0, 1), + ....: ('2', '2', 0, 2)], + ....: initial_states=['1'], + ....: final_states=['1', '2']) + sage: T.state('1').final_word_out = [2, 5] + sage: T.transposition() + Traceback (most recent call last): + ... + NotImplementedError: Transposition for transducers with + final output words is not implemented. """ transposition = self.empty_copy() @@ -5762,6 +5779,10 @@ def transposition(self): for final in self.final_states(): state = transposition.state(final.label()) + if final.final_word_out: + raise NotImplementedError("Transposition for transducers " \ + "with final output words is not " \ + "implemented.") if not final.is_initial: state.is_final = False state.is_initial = True From 4eddf379f1a0e300cee9e30ee87f0f6df78b726e Mon Sep 17 00:00:00 2001 From: Sara Kropf Date: Mon, 2 Jun 2014 15:41:09 +0200 Subject: [PATCH 108/546] FiniteStateMachine.equivalence_classes considers final output words --- src/sage/combinat/finite_state_machine.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index f1fbe3fd275..40871ade91f 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -6085,7 +6085,8 @@ def equivalence_classes(self): - `p_a.\mathit{word}_\mathit{in}=p_b.\mathit{word}_\mathit{in}`, - `p_a.\mathit{word}_\mathit{out}=p_b.\mathit{word}_\mathit{out}`, - `a'` and `b'` have the same output label, and - - `a'` and `b'` are both final or both non-final. + - `a'` and `b'` are both final or both non-final and have the + same final output word. The function :meth:`.equivalence_classes` returns a list of the equivalence classes to this equivalence relation. @@ -6104,6 +6105,11 @@ def equivalence_classes(self): ....: ("D", "A", 0, 0), ("D", "A", 1, 1)]) sage: fsm.equivalence_classes() [['A', 'C'], ['B', 'D']] + sage: fsm.state("A").is_final = True + sage: fsm.state("A").final_word_out = 1 + sage: fsm.equivalence_classes() + [['A'], ['B'], ['D'], ['C']] + """ # Two states `a` and `b` are j-equivalent if and only if there @@ -6127,7 +6133,8 @@ def equivalence_classes(self): # initialize with 0-equivalence classes_previous = [] - key_0 = lambda state: (state.is_final, state.color, state.word_out) + key_0 = lambda state: (state.is_final, state.color, state.word_out, + state.final_word_out) states_grouped = full_group_by(self.states(), key=key_0) classes_current = [equivalence_class for (key,equivalence_class) in states_grouped] From 6d1a36b5a01a5fbfa2f342fb7295f9c1db2a9319 Mon Sep 17 00:00:00 2001 From: Sara Kropf Date: Mon, 2 Jun 2014 16:36:23 +0200 Subject: [PATCH 109/546] Transducer.intersection considers final output words --- src/sage/combinat/finite_state_machine.py | 60 ++++++++++++++++++++--- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 40871ade91f..0a4398b8669 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -6190,8 +6190,8 @@ def quotient(self, classes): Non-initial states may be merged with initial states, the resulting state is an initial state. - All states in a class must have the same ``is_final`` and - ``word_out`` values. + All states in a class must have the same ``is_final``, + ``final_word_out`` and ``word_out`` values. EXAMPLES:: @@ -6222,6 +6222,22 @@ def quotient(self, classes): Traceback (most recent call last): ... AssertionError: Transitions of state 'A' and 'B' are incompatible. + + TESTS:: + + sage: fsm = FiniteStateMachine([("A", "B", 0, 1), ("A", "B", 1, 0), + ....: ("B", "C", 0, 0), ("B", "C", 1, 1), + ....: ("C", "D", 0, 1), ("C", "D", 1, 0), + ....: ("D", "A", 0, 0), ("D", "A", 1, 1)], + ....: final_states=["A", "C"]) + sage: fsm.state("A").final_word_out = 1 + sage: fsm.state("C").final_word_out = 2 + sage: fsmq = fsm.quotient([[fsm.state("A"), fsm.state("C")], + ....: [fsm.state("B"), fsm.state("D")]]) + Traceback (most recent call last): + ... + AssertionError: Class ['A', 'C'] mixes + final states with different final output words. """ new = self.empty_copy() state_mapping = {} @@ -6260,6 +6276,9 @@ def quotient(self, classes): [(state_mapping[t.to_state], t.word_in, t.word_out) for t in state.transitions]), \ "Transitions of state %s and %s are incompatible." % (c[0], state) + assert new_state.final_word_out == state.final_word_out, \ + "Class %s mixes final states with different " \ + "final output words." % (c,) return new @@ -8121,6 +8140,25 @@ def intersection(self, other, only_accessible_components=True): ValueError: An epsilon-transition (with empty input or output) was found. + TESTS:: + + sage: transducer1 = Transducer([('1', '2', 1, 0)], + ....: initial_states=['1'], + ....: final_states=['2'], + ....: determine_alphabets=True) + sage: transducer2 = Transducer([('A', 'B', 1, 0)], + ....: initial_states=['A'], + ....: final_states=['B'], + ....: determine_alphabets=True) + sage: res = transducer1.intersection(transducer2) + sage: res.final_states() + [('2', 'B')] + sage: transducer1.state('2').final_word_out = 1 + sage: transducer2.state('B').final_word_out = 2 + sage: res = transducer1.intersection(transducer2) + sage: res.final_states() + [] + REFERENCES: .. [BaWo2012] Javier Baliosian and Dina Wonsever, *Finite State @@ -8142,10 +8180,20 @@ def function(transition1, transition2): else: raise LookupError - return self.product_FiniteStateMachine( - other, - function, - only_accessible_components=only_accessible_components) + new = self.product_FiniteStateMachine( + other, + function, + only_accessible_components=only_accessible_components, + final_function=lambda s1, s2: s1.final_word_out) + + for state in new.iter_final_states(): + state0 = self.state(state.label()[0]) + state1 = other.state(state.label()[1]) + if state0.final_word_out != state1.final_word_out: + state.final_word_out = None + state.is_final = False + + return new def cartesian_product(self, other, only_accessible_components=True): From 13e000cfb0cb0bca6b631215c7195a963d9fb83b Mon Sep 17 00:00:00 2001 From: Sara Kropf Date: Mon, 2 Jun 2014 16:59:32 +0200 Subject: [PATCH 110/546] FSMState.copy also copies final_word_out --- src/sage/combinat/finite_state_machine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 0a4398b8669..66db9504336 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -1161,7 +1161,7 @@ def __copy__(self): 'A' """ new = FSMState(self.label(), self.word_out, - self.is_initial, self.is_final) + self.is_initial, self.is_final, self.final_word_out) if hasattr(self, 'hook'): new.hook = self.hook return new From bcf330800da84450a35e3fd48ed7d59a67f179fb Mon Sep 17 00:00:00 2001 From: Sara Kropf Date: Mon, 2 Jun 2014 17:25:27 +0200 Subject: [PATCH 111/546] Transducer.asymptotic_moments only accepts transducers with all states final and transducers.GrayCode is now such a transducer. --- src/sage/combinat/finite_state_machine.py | 39 +++++++------------ .../finite_state_machine_generators.py | 5 ++- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 66db9504336..ccbfb9299cf 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -374,6 +374,7 @@ sage: product_transducer = shift_right_transducer.cartesian_product(transducers.Identity([0, 1])) sage: sage.combinat.finite_state_machine.FSMOldCodeTransducerCartesianProduct = True sage: Gray_transducer = xor_transducer(product_transducer) + sage: Gray_transducer.construct_final_word_out([0]) sage: Gray_transducer.transitions() [Transition from (('I', 0), 0) to ((0, 0), 0): 0|-, Transition from (('I', 0), 0) to ((1, 0), 0): 1|-, @@ -408,7 +409,7 @@ :: sage: for n in srange(10): - ....: Gray_transducer(n.bits() + [0]) + ....: Gray_transducer(n.bits()) [] [1] [1, 1] @@ -6930,13 +6931,6 @@ def asymptotic_moments(self, variable=SR.symbol('n')): see [HKW2014]_, Theorem 2 for details. If those exponents are integers, we can use a polynomial ring. - .. WARNING:: - - If not all states are final, we only print a warning. This is - for a transitional period to accomodate subsequential - transducers while those are not yet implemented - (cf. :trac:`16191`). - EXAMPLES: #. A trivial example: write the negative of the input:: @@ -6970,7 +6964,8 @@ def asymptotic_moments(self, variable=SR.symbol('n')): ....: (1, 1, 1, 0), ....: (1, '.1', 0, None)], ....: initial_states=[0], - ....: final_states=[0]) + ....: final_states=[0], + ....: with_final_word_out=[0]) As an example, we compute the NAF of `27` by this transducer. Note that we have to add two trailing (at the @@ -7016,8 +7011,6 @@ def asymptotic_moments(self, variable=SR.symbol('n')): Now, we actually compute the asymptotic moments:: sage: moments = NAFweight.asymptotic_moments() - verbose 0 (...) Not all states are final. Proceeding - under the assumption that you know what you are doing. sage: moments['expectation'] 1/3*n + Order(1) sage: moments['variance'] @@ -7025,11 +7018,6 @@ def asymptotic_moments(self, variable=SR.symbol('n')): sage: moments['covariance'] Order(1) - In this case, we can ignore the warning: we could have all - states as final states if we would have a final output - label, i.e., a subsequential transducer. However, this is - not yet implemented in this package, cf. :trac:`16191`. - #. This is Example 3.1 in [HKW2014]_, where a transducer with variable output labels is given. There, the aim was to choose the output labels of this very simple transducer such @@ -7061,8 +7049,6 @@ def asymptotic_moments(self, variable=SR.symbol('n')): `):: sage: moments = transducers.GrayCode().asymptotic_moments() - verbose 0 (...) Not all states are final. Proceeding - under the assumption that you know what you are doing. sage: moments['expectation'] 1/2*n + Order(1) sage: moments['variance'] @@ -7070,11 +7056,6 @@ def asymptotic_moments(self, variable=SR.symbol('n')): sage: moments['covariance'] Order(1) - Also in this case, we can ignore the warning: we could have - all states as final states if we would have a final output - label, i.e., a subsequential transducer. However, this is - not yet implemented in this package, cf. :trac:`16191`. - #. This is the first part of Example 6.3 in [HKW2014]_, counting the number of 10 blocks in the standard binary expansion. The least significant digit is at the left-most @@ -7268,6 +7249,14 @@ def asymptotic_moments(self, variable=SR.symbol('n')): significant performance degradation. 7/6*n + Order(1) + #. All states of ``self`` have to be final:: + + sage: T = Transducer([(0, 1, 1, 4)], initial_states=[0]) + sage: T.asymptotic_moments() + Traceback (most recent call last): + ... + ValueError: Not all states are final. + ALGORITHM: See [HKW2014]_, Theorem 2. @@ -7295,9 +7284,7 @@ def asymptotic_moments(self, variable=SR.symbol('n')): raise ValueError("A unique initial state is required.") if len(self.final_states()) != len(self.states()): - verbose("Not all states are final. Proceeding under the " - "assumption that you know what you are doing.", - level=0) + raise ValueError("Not all states are final.") if not self.is_complete(): raise NotImplementedError("This finite state machine is " diff --git a/src/sage/combinat/finite_state_machine_generators.py b/src/sage/combinat/finite_state_machine_generators.py index b5aa35a63d0..c55e108bd8e 100644 --- a/src/sage/combinat/finite_state_machine_generators.py +++ b/src/sage/combinat/finite_state_machine_generators.py @@ -525,7 +525,7 @@ def GrayCode(self): Transducer with 3 states sage: sage.combinat.finite_state_machine.FSMOldProcessOutput = False sage: for v in srange(0, 10): - ....: print v, G(v.digits(base=2) + [0]) + ....: print v, G(v.digits(base=2)) 0 [] 1 [1] 2 [1, 1] @@ -553,7 +553,8 @@ def GrayCode(self): [2, 1, z, o], [2, 2, o, z]], initial_states=[0], - final_states=[1]) + final_states=[1], + with_final_word_out=[0]) # Easy access to the transducer generators from the command line: From a1ef4f22e9cc51537d4bb531398046388b89b34e Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 2 Jun 2014 21:46:43 +0100 Subject: [PATCH 112/546] Added a short paragraph at the beginning --- src/sage/game_theory/cooperative_game.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 18a1f2c814c..44dfcd01b99 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -1,10 +1,14 @@ r""" Co-operative games with N players. - +This module implements characteristic function cooperative games. +The main contribution is a class for a characteristic function game that takes a characteristic function (as a dictionary) as an input. +Methods to calculate the Shapley value (a fair way of sharing common resources: https://www.youtube.com/watch?v=aThG4YAFErw) as well as test properties of the game (monotonicity, super additivity) are also included. AUTHOR:: -- James Campbell 06-2014 +- James Campbell 06-2014: Original version + +- Vince Knight 06-2014 EXAMPLES:: From a158c7b51ae75764831290b1d016c88ec5c44869 Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 2 Jun 2014 22:16:30 +0100 Subject: [PATCH 113/546] Updated readme to indicate that this is a development repo --- README.txt | 380 ++--------------------------------------------------- 1 file changed, 9 insertions(+), 371 deletions(-) diff --git a/README.txt b/README.txt index 0463ff52866..4b78bc41e50 100644 --- a/README.txt +++ b/README.txt @@ -17,379 +17,17 @@ In many cases, documentation for modules and functions list the authors. -GETTING STARTED ---------------- +**This is the working repository for game theoretical development in Sage. For the main Sage repository see [https://github.com/sagemath/sage](https://github.com/sagemath/sage).** -This README.txt contains build instructions for Sage. If you downloaded -a binary, you do not need to do anything; just execute: +The current open tickets for game theory can be found (which correspond to branches in this repository) below: - ./sage +- [16331: Build capacity to solve matching games in to Sage.](http://trac.sagemath.org/ticket/16331) +- [16332: Build capacity to calculate Shapley value of cooperative games.](http://trac.sagemath.org/ticket/16332) +- [16333: Build class for normal form games as well as ability to obtain Nash equilibria](http://trac.sagemath.org/ticket/16333) -from the command line. If you downloaded the sources, please read below -on how to build Sage and work around common issues. +If you would like to contribute, please see the discussions at the relevant tickets above and/or fork this repository. -If you have questions or encounter problems, please do not hesitate -to email the sage-support mailing list: +Current contributors: - http://groups.google.com/group/sage-support - - -SUPPORTED PLATFORMS -------------------- - -Sage fully supports several Linux distributions, recent versions of -Mac OS X, Windows (using virtualization), as well as a number of -Solaris and OpenSolaris releases. - -There should be no serious bugs in an officially released version of -Sage on any of the fully supported platforms, but any major issues with -a particular release will be documented on an errata page: - - http://wiki.sagemath.org/errata - -Ports are in progress to some other, less common platforms. The list of -supported platforms and their current statuses are given at the -following web page: - - http://wiki.sagemath.org/SupportedPlatforms - -If you are interested in helping port Sage to a new platform, please let -us know at the sage-devel mailing list: - - http://groups.google.com/group/sage-devel - - -QUICK INSTRUCTIONS TO BUILD FROM SOURCE ---------------------------------------- - -The following steps briefly outline the process of building Sage from -source. More detailed instructions, including how to build faster on -multicore machines are contained later in this README and in the -Installation Guide: - - http://www.sagemath.org/doc/installation - -1. Make sure you have the dependencies and 5 GB of free disk space. - - All Linux versions: gcc, make, m4, perl, ranlib, and tar. - Debian or Ubuntu systems: the dpkg-dev package. - Fedora or RedHat systems: the perl-ExtUtils-MakeMaker package. - (install these using your package manager) - - OS X: Xcode. Make sure you have installed the most recent version - of Xcode. With recent versions of OS X (OS X Lion or later), you - can install Xcode for free from the App Store. For pre-Lion - versions of OS X, you can download Xcode from - http://developer.apple.com/downloads/. - - With OS X, you also need to install the "command line tools". When - using OS X Mavericks, after installing Xcode, run this command from - a terminal window: - - xcode-select --install - - Then click "Install" in the pop-up window. - - When using OS X Mountain Lion or earlier, you need to install the - command line tools from Xcode: run Xcode; then from the File - menu, choose "Preferences", then the "Downloads" tab, and then - "Install" the Command Line Tools. - - Other platforms: See detailed instructions below. - -2. Extract the tarball: - - tar xvf sage-*.tar - -3. cd into the Sage directory and type make: - - cd sage-*/ - make - - That's it! Everything is automatic and non-interactive. The build - should work fine on all fully supported platforms. If it does not, we - want to know! - - -ENVIRONMENT VARIABLES ---------------------- - -There are a lot of environment variables which control the install -process of Sage, see: - - http://sagemath.org/doc/installation/source.html#environment-variables - - -IMPLEMENTATION --------------- - -Sage has significant components written in the following languages: -C/C++, Python, Cython, Lisp, and Fortran. Lisp (ECL), Python, and Cython -are built as part of Sage and a GNU Fortran (gfortran) binary is -included (OS X only), so you do not need them in order to build Sage. - - -MORE DETAILED INSTRUCTIONS TO BUILD FROM SOURCE ------------------------------------------------ - -1. Make sure you have about 5 GB of free disk space. - -2. Install build dependencies. - - Linux: See quick instructions above. - - OS X: Make sure you have XCode version >= 2.4, i.e. "gcc -v" should - output build >= 5363. If you don't, go to: - - http://developer.apple.com/ - - sign up, and download the free XCode package. Only OS X >= 10.4 is - supported. - - Solaris and OpenSolaris: Building Sage on these platforms is more - tricky than on Linux or OS X. For details on how to build Sage on - these platforms, see: - - http://wiki.sagemath.org/solaris - - Windows: Download and install VirtualBox, and then download the - Sage virtual appliance. For details, see: - - http://wiki.sagemath.org/SageAppliance - - NOTE: On some operating systems, it might be necessary to install - gas/as, gld/ld, gnm/nm. On most platforms, these are automatically - installed when you install the programs listed above. - -3. Extract the Sage source tarball and cd into a directory with no - spaces in it. If you have a machine with 4 processors, say, type - the following to configure the build script to perform a parallel - compilation of Sage using 4 jobs: - - export MAKE="make -j4" - - (With 4 processors, you might also consider "-j5" or "-j6" -- - building with more jobs than CPU cores can speed things up.) - You might in addition pass a "-l" flag to "make": this - sets a load limit, so for example if you execute - - export MAKE="make -j4 -l5.5" - - then "make" won't start more than one job at a time if the system - load average is above 5.5. See - http://www.gnu.org/software/make/manual/make.html#Options-Summary - and http://www.gnu.org/software/make/manual/make.html#Parallel. - - If you want to run the test suite for each individual spkg as it is - installed, type: - - export SAGE_CHECK="yes" - - before starting the Sage build. This will run each test suite and - will raise an error if any failures occur. Python's test suite has - been disabled by default, because it causes failures on most - systems. To renable the Python testsuite, set the environment - variable SAGE_CHECK_PACKAGES to "python". - - To start the build, type: - - make - -4. Wait about 20 minutes to 14 days, depending on your computer (it took - about 2 weeks to build Sage on the T-Mobile G1 Android cell phone). - -5. Type "./sage" to try it out. - -6. OPTIONAL: Type "make ptest" to test all examples in the documentation - (over 200,000 lines of input!) -- this takes from 10 minutes to - several hours. Don't get too disturbed if there are 2 to 3 failures, - but always feel free to email the section of logs/ptest.log that - contains errors to the sage-support mailing list. If there are - numerous failures, there was a serious problem with your build. - -7. The HTML version of the documentation is built - during the compilation process of Sage and resides in the directory: - - $SAGE_ROOT/src/doc/output/html/ - - OPTIONAL: If you want to build the PDF version (requires LaTeX) of - the documentation, run: - - make doc-pdf - -8. OPTIONAL: It is highly recommended that you install the optional GAP - database by typing: - - ./sage --optional - - then installing (with "./sage -i") the package whose name begins with - database_gap. This will download the package from - sage.math.washington.edu and install it. While you're at it, you - might install other databases of interest to you. - -9. OPTIONAL: It is recommended that you have both LaTeX and the - ImageMagick tools (e.g. the "convert" command) installed since some - plotting functionality benefits from it. - -10. OPTIONAL: Read this if you are intending to run a Sage notebook - server for multiple users. For security (i.e., to run - "notebook(secure=True)") you want to access the server using the - HTTPS protocol. First, install OpenSSL and the OpenSSL development - headers on your system if they are not already installed. Then - install pyOpenSSL by building Sage and then typing - - ./sage -i pyopenssl - - Note that this command requires internet access. Alternatively, - "make ssl" builds Sage and installs pyOpenSSL. - - -PROBLEMS --------- - -If you have problems building Sage, check the Sage Installation Guide, -and also note the following. Each separate component of Sage is -contained in an spkg; these are stored in spkg/standard/. As each one -is built, a build log is stored in logs/pkgs/, so you can browse these -to find error messages. If an spkg fails to build, the whole build -process will stop soon after, so check the most recent log files -first, or run - - grep -li "^Error" logs/pkgs/* - -from the top-level Sage directory to find log files with error -messages in them. Send (a small part of) the relevant log file to the -sage-devel mailing list, making sure to include at least some of the -error messages; probably someone there will have some helpful -suggestions. - - -SUPPORTED COMPILERS -------------------- - -Sage includes a GCC (GNU Compiler Collection) package. In order to -build Sage, you need a C compiler which can build GCC and its -prerequisites. gcc version 4.0.1 or later should probably work. On -Solaris or OpenSolaris, building with the Sun compiler should also work. - -The GCC package in Sage is not always installed. It is determined -automatically whether it needs to be installed. You can override this -by setting the environment variable SAGE_INSTALL_GCC=yes (to force -installation of GCC) or SAGE_INSTALL_GCC=no (to disable installation of -GCC). If you don't want to install GCC, you need to have recent -versions of gcc, g++ and gfortran; moreover, the versions must be equal. - -There are some known problems with old assemblers, in particular when -building the ECM package. You should ensure that your assembler -understands all instructions for your processor. On Linux, this means -you need a recent version of binutils; on OS X you need a recent version -of XCode. - - -DIRECTORY LAYOUT ----------------- - -Simplified directory layout (only essential files/directories): - -SAGE_ROOT Root directory (sage-x.y.z in the Sage tarball) -+-- build -| +-- deps Dependency information of packages -| +-- pkgs Every package is a subdirectory here -| +-- atlas -| ... -| +-- zn_poly -+-- COPYING.txt Copyright information -+-- local Compiled packages are installed here -| +-- bin Executables -| +-- include C/C++ headers -| +-- lib Shared libraries -| +-- share Databases, architecture-independent data -| +-- var -| +-- sage List of installed packages -| +-- tmp Temporary files when building Sage -+-- logs -| +-- dochtml.log Log of the documentation build -| +-- install.log Full install log -| +-- pkgs Build logs of individual packages -| +--- atlas-3.10.1.p7.log -| ... -| +--- zn_poly-0.9.p11.log -+-- Makefile Running "make" uses this file -+-- README.txt This file -+-- sage Script to start Sage -+-- src All of the Sage source (not third-party packages) -| +-- bin Scripts that Sage uses internally -| +-- doc Sage documentation -| +-- sage The Sage library source code -+-- upstream Source tarballs of packages -| +-- atlas-3.10.1.tar.bz2 -| ... -| +-- zn_poly-0.9.tar.bz2 -+-- VERSION.txt - -For more details, see: - - http://sagemath.org/doc/developer/coding_basics.html#files-and-directory-structure - - -RELOCATION ----------- - -You *should* be able to move the sage-x.y.z/ directory anywhere you -want. If you copy the sage script or make a symbolic link to it, you -should modify the script to reflect this (as instructed at the top of -the script). It is best if the path to Sage does not have any spaces in -it. - -For a system-wide installation, as root you can move the sage-x.y.z/ -directory to a system-wide directory. Afterwards, you need to start up -Sage as root at least once prior to using the system-wide Sage as a -normal user. See the Installation Guide for further information on -performing a system-wide installation: - - http://www.sagemath.org/doc/installation/source.html#installation-in-a-multiuser-environment - -If you find anything that doesn't work correctly after you moved the -directory, please email the sage-support mailing list. - - -REDISTRIBUTION --------------- - -Your local Sage install is almost exactly the same as any "developer" -install. You can make changes to documentation, source, etc., and very -easily package the complete results up for redistribution just like we -do. - -1. To make your own source tarball of Sage, type: - - sage --sdist - -2. To make a binary distribution with your currently installed packages, - type: - - sage --bdist - -3. To make a binary that will run on the widest range of target - machines, set the SAGE_FAT_BINARY environment variable to "yes" - before building Sage: - - export SAGE_FAT_BINARY="yes" - make distclean && make - ./sage --bdist - -In all cases, the result is placed in the directory "$SAGE_ROOT/dist/". - - -CHANGES TO INCLUDED SOFTWARE ----------------------------- - -All software included with Sage is copyrighted by the respective authors -and released under an open source license that is "GPL version 3 or -later" compatible. See the file COPYING.txt for more details. - -Sources are in unmodified (as far as possible) tarballs in the -"$SAGE_ROOT/upstream" directory. The remaining description, version -information, patches, and build scripts are in the accompanying -"$SAGE_ROOT/build/pkgs/" directory. This directory is -part of the Sage git repository. +- James Campbell (Cardiff) +- Vincent Knight (Cardiff) From cdf19a22d9b9fa18f00736a1b6e56a91755a480f Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 2 Jun 2014 22:19:43 +0100 Subject: [PATCH 114/546] Fixed readme --- README.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.txt b/README.txt index 4b78bc41e50..247230672df 100644 --- a/README.txt +++ b/README.txt @@ -17,13 +17,13 @@ In many cases, documentation for modules and functions list the authors. -**This is the working repository for game theoretical development in Sage. For the main Sage repository see [https://github.com/sagemath/sage](https://github.com/sagemath/sage).** +**This is the working repository for game theoretical development in Sage. For the main Sage repository see https://github.com/sagemath/sage. ** The current open tickets for game theory can be found (which correspond to branches in this repository) below: -- [16331: Build capacity to solve matching games in to Sage.](http://trac.sagemath.org/ticket/16331) -- [16332: Build capacity to calculate Shapley value of cooperative games.](http://trac.sagemath.org/ticket/16332) -- [16333: Build class for normal form games as well as ability to obtain Nash equilibria](http://trac.sagemath.org/ticket/16333) +- 16331: Build capacity to solve matching games in to Sage: http://trac.sagemath.org/ticket/16331 +- 16332: Build capacity to calculate Shapley value of cooperative games: http://trac.sagemath.org/ticket/16332 +- 16333: Build class for normal form games as well as ability to obtain Nash equilibria: http://trac.sagemath.org/ticket/16333 If you would like to contribute, please see the discussions at the relevant tickets above and/or fork this repository. From 6eebbcd29a9d936a0c4ee8aac8ce1795be2a0b07 Mon Sep 17 00:00:00 2001 From: Ivan Andrus Date: Mon, 2 Jun 2014 09:04:14 -0600 Subject: [PATCH 115/546] Fixed a warning --- src/mac-app/AppDelegate.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mac-app/AppDelegate.m b/src/mac-app/AppDelegate.m index e0e7c7e9958..ee3d295641e 100644 --- a/src/mac-app/AppDelegate.m +++ b/src/mac-app/AppDelegate.m @@ -35,7 +35,7 @@ - (void)applicationWillFinishLaunching:(NSNotification *)aNotification{ if( returnCode != 0 ) { // According to http://www.cocoadev.com/index.pl?TransformProcessType // TransformProcessType is available since 10.3, but doen't work for our case until 10.5 - NSLog(@"Could not show Sage.app in the dock. Error %ld", returnCode); + NSLog(@"Could not show Sage.app in the dock. Error %d", (int)returnCode); // It's forbidden to showInDock since it doesn't work [defaults setBool:NO forKey:@"myShowInDock"]; [defaults synchronize]; From eb134581fc1b354948e99e150e6c5fdd63cd9120 Mon Sep 17 00:00:00 2001 From: Ivan Andrus Date: Mon, 2 Jun 2014 00:28:10 -0600 Subject: [PATCH 116/546] Ensure that Sage is read/write --- src/mac-app/AppController.h | 1 + src/mac-app/AppController.m | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/mac-app/AppController.h b/src/mac-app/AppController.h index 1b3b6f47579..6445a4cec05 100644 --- a/src/mac-app/AppController.h +++ b/src/mac-app/AppController.h @@ -60,6 +60,7 @@ -(IBAction)showPreferences:(id)sender; +-(void)ensureReadWrite; -(void)setupPaths; // Quit diff --git a/src/mac-app/AppController.m b/src/mac-app/AppController.m index 5705bbdad16..a607e0644be 100644 --- a/src/mac-app/AppController.m +++ b/src/mac-app/AppController.m @@ -33,6 +33,7 @@ - (void) awakeFromNib{ // Find sageBinary etc. [self setupPaths]; + [self ensureReadWrite]; // Initialize the StatusItem if desired. // If we are on Tiger, then showing in the dock doesn't work @@ -306,6 +307,33 @@ -(void)setupPaths{ } } +-(void)ensureReadWrite { + NSFileManager *filemgr = [NSFileManager defaultManager]; + NSLog(@"Checking if sageBinary (%@) is writeable.",sageBinary); + if ( ! [filemgr isWritableFileAtPath:sageBinary] ) { + NSAlert *alert = [NSAlert alertWithMessageText:@"Read-only Sage" + defaultButton:@"Quit" + alternateButton:@"Preferences" + otherButton:@"Continue" + informativeTextWithFormat:@"You are attempting to run Sage.app with a read-only copy of Sage " + "(most likely due to running it from the disk image). " + "Unfortunately, this is not supported for technical reasons. \n" + "Please drag Sage.app to your hard-drive and run it from there, " + "or choose a different executable in Preferences."]; + [alert setAlertStyle:NSWarningAlertStyle]; + NSModalResponse resp = [alert runModal]; + if (resp == NSModalResponseOK) {// Quit + NSLog(@"Quitting after a read-only Sage warning."); + [NSApp terminate:self]; + } else if ( resp == NSModalResponseCancel ) { // Continue + NSLog(@"Preferences after a read-only Sage warning."); + [self showPreferences:self]; + } else { + NSLog(@"Continuing from read-only Sage warning."); + } + } +} + -(IBAction)revealInFinder:(id)sender{ if ( [[sender title] isEqualToString:@"Reveal in Shell"] ) { [self terminalRun:[NSString stringWithFormat:@"cd '%@' && $SHELL", From 1f606404fbf86b314c260127e8588973476bc059 Mon Sep 17 00:00:00 2001 From: drvinceknight Date: Tue, 3 Jun 2014 08:16:23 +0100 Subject: [PATCH 117/546] Added payoff vector as attribute --- src/sage/game_theory/cooperative_game.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 44dfcd01b99..c0683d738a6 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -64,6 +64,7 @@ def __init__(self, characteristic_function, player_list): self.char_fun = characteristic_function self.number_players = len(player_list) self.player_list = player_list + self.payoff_vector = False def shapley_value(self): r""" @@ -74,6 +75,7 @@ def shapley_value(self): player_contribution = self.marginal_contributions(i) average = sum(player_contribution) / len(player_contribution) payoff_vector.append(average) + self.payoff_vector = payoff_vector return payoff_vector def is_monotone(): From 1fcec384efa5e360501172f386f91c6bcb0a2a20 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 09:20:45 +0100 Subject: [PATCH 118/546] fixes #5 by moving some documentation --- src/sage/game_theory/cooperative_game.py | 73 ++++++++++++------------ 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index c0683d738a6..45c85d0ea01 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -9,42 +9,6 @@ - James Campbell 06-2014: Original version - Vince Knight 06-2014 - - -EXAMPLES:: -Basic example of how to implement a co-operative game. - -:: - -sage: test_function = {(): 0, - ....: ('1',): 6, - ....: ('2',): 12, - ....: ('3',): 42, - ....: ('1', '2',): 12, - ....: ('1', '3',): 42, - ....: ('2', '3',): 42, - ....: ('1', '2', '3',): 42} - sage: - sage: test_game = CooperativeGame(test_function, ['A', 'B', 'C']) - sage: print test_game.shapley_value() - [2, 5, 35] - -We can also use strings instead of numbers. - -:: - - sage: letter_function = {(): 0, - ....: ('A',): 6, - ....: ('B',): 12, - ....: ('C',): 42, - ....: ('A', 'B',): 12, - ....: ('A', 'C',): 42, - ....: ('B', 'C',): 42, - ....: ('A', 'B', 'C',): 42} - sage: - sage: letter_game = CooperativeGame(test_function, ['A', 'B', 'C']) - sage: print letter_game.shapley_value() - [2, 5, 35] """ #***************************************************************************** @@ -61,6 +25,43 @@ class CooperativeGame(): def __init__(self, characteristic_function, player_list): + r""" + An object representing a co-operative game. Primarily used to solve + problems, but can also provide other information. + + EXAMPLES:: + Basic example of how to implement a co-operative game. + + :: + + sage: test_function = {(): 0, + ....: ('1',): 6, + ....: ('2',): 12, + ....: ('3',): 42, + ....: ('1', '2',): 12, + ....: ('1', '3',): 42, + ....: ('2', '3',): 42, + ....: ('1', '2', '3',): 42} + sage: test_game = CooperativeGame(test_function, ['1', '2', '3']) + sage: print test_game.shapley_value() + [2, 5, 35] + + We can also use strings instead of numbers. + + :: + + sage: letter_function = {(): 0, + ....: ('A',): 6, + ....: ('B',): 12, + ....: ('C',): 42, + ....: ('A', 'B',): 12, + ....: ('A', 'C',): 42, + ....: ('B', 'C',): 42, + ....: ('A', 'B', 'C',): 42} + sage: letter_game = CooperativeGame(test_function, ['A', 'B', 'C']) + sage: print letter_game.shapley_value() + [2, 5, 35] + """ self.char_fun = characteristic_function self.number_players = len(player_list) self.player_list = player_list From 829dedc2102cd5c732aa1ce92a5f35e2710e03c4 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 09:31:03 +0100 Subject: [PATCH 119/546] fixes #6 and all tests pass, only 6 so far... --- src/sage/game_theory/cooperative_game.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 45c85d0ea01..281993a7efd 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -58,7 +58,7 @@ def __init__(self, characteristic_function, player_list): ....: ('A', 'C',): 42, ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} - sage: letter_game = CooperativeGame(test_function, ['A', 'B', 'C']) + sage: letter_game = CooperativeGame(letter_function, ['A', 'B', 'C']) sage: print letter_game.shapley_value() [2, 5, 35] """ @@ -115,3 +115,4 @@ def get_predecessors_and_player(self, player, permutation): return predecessors else: predecessors.append(k) + From ed94f20640104c42b2b0ed7522ea466db42f58d6 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 09:44:07 +0100 Subject: [PATCH 120/546] improves get_predecessors function --- src/sage/game_theory/cooperative_game.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 281993a7efd..76703a41679 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -21,6 +21,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from itertools import permutations +import copy class CooperativeGame(): @@ -96,8 +97,7 @@ def marginal_contributions(self, player): return contributions def marginal_of_pi(self, player, pi): - player_and_pred = self.get_predecessors_and_player(player, pi) - predecessors = [x for x in player_and_pred if x != player] + predecessors, player_and_pred = self.get_predecessors(player, pi) if predecessors == None: predecessors = () else: @@ -106,13 +106,16 @@ def marginal_of_pi(self, player, pi): value = self.char_fun[player_and_pred] - self.char_fun[predecessors] return value - def get_predecessors_and_player(self, player, permutation): - predecessors = [] + def get_predecessors(self, player, permutation): + pred = [] + play_and_pred = [] for k in permutation: if k == player: - predecessors.append(k) - predecessors.sort() - return predecessors + play_and_pred.append(k) + pred.sort() + play_and_pred.sort() + return pred, play_and_pred else: - predecessors.append(k) + pred.append(k) + play_and_pred.append(k) From 8623f345853fa942ffcf59a99ead9c1dd28c0288 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 09:55:46 +0100 Subject: [PATCH 121/546] improves documentation --- src/sage/game_theory/cooperative_game.py | 38 ++++++++++++++---------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 76703a41679..557363d9226 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -2,8 +2,11 @@ Co-operative games with N players. This module implements characteristic function cooperative games. -The main contribution is a class for a characteristic function game that takes a characteristic function (as a dictionary) as an input. -Methods to calculate the Shapley value (a fair way of sharing common resources: https://www.youtube.com/watch?v=aThG4YAFErw) as well as test properties of the game (monotonicity, super additivity) are also included. +The main contribution is a class for a characteristic function game +that takes a characteristic function (as a dictionary) as an input. +Methods to calculate the Shapley value (a fair way of sharing common +resources: https://www.youtube.com/watch?v=aThG4YAFErw) as well as +test properties of the game (monotonicity, super additivity) are also included. AUTHOR:: - James Campbell 06-2014: Original version @@ -21,7 +24,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from itertools import permutations -import copy class CooperativeGame(): @@ -31,9 +33,7 @@ def __init__(self, characteristic_function, player_list): problems, but can also provide other information. EXAMPLES:: - Basic example of how to implement a co-operative game. - - :: + Basic example of how to implement a co-operative game. :: sage: test_function = {(): 0, ....: ('1',): 6, @@ -44,12 +44,8 @@ def __init__(self, characteristic_function, player_list): ....: ('2', '3',): 42, ....: ('1', '2', '3',): 42} sage: test_game = CooperativeGame(test_function, ['1', '2', '3']) - sage: print test_game.shapley_value() - [2, 5, 35] - We can also use strings instead of numbers. - - :: + We can also use strings instead of numbers. :: sage: letter_function = {(): 0, ....: ('A',): 6, @@ -60,8 +56,6 @@ def __init__(self, characteristic_function, player_list): ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} sage: letter_game = CooperativeGame(letter_function, ['A', 'B', 'C']) - sage: print letter_game.shapley_value() - [2, 5, 35] """ self.char_fun = characteristic_function self.number_players = len(player_list) @@ -71,6 +65,21 @@ def __init__(self, characteristic_function, player_list): def shapley_value(self): r""" Return the payoff vector for co-operative game. + + EXAMPLES:: + A typical example of the use of shapley_value. :: + + sage: test_function = {(): 0, + ....: ('1',): 6, + ....: ('2',): 12, + ....: ('3',): 42, + ....: ('1', '2',): 12, + ....: ('1', '3',): 42, + ....: ('2', '3',): 42, + ....: ('1', '2', '3',): 42} + sage: test_game = CooperativeGame(test_function, ['1', '2', '3']) + sage: print test_game.shapley_value() + [2, 5, 35] """ payoff_vector = [] for i in self.player_list: @@ -98,7 +107,7 @@ def marginal_contributions(self, player): def marginal_of_pi(self, player, pi): predecessors, player_and_pred = self.get_predecessors(player, pi) - if predecessors == None: + if predecessors is None: predecessors = () else: predecessors = tuple(predecessors) @@ -118,4 +127,3 @@ def get_predecessors(self, player, permutation): else: pred.append(k) play_and_pred.append(k) - From c241ee99f0d5936fc9d4f676a5cbeff058c74630 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 10:17:42 +0100 Subject: [PATCH 122/546] moves documentation and changes tests slightly --- src/sage/game_theory/cooperative_game.py | 85 +++++++++++++----------- 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 557363d9226..f3a0d05810a 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -24,39 +24,50 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from itertools import permutations +from sage.structure.sage_object import SageObject -class CooperativeGame(): - def __init__(self, characteristic_function, player_list): - r""" - An object representing a co-operative game. Primarily used to solve - problems, but can also provide other information. +class CooperativeGame(SageObject): + r""" + An object representing a co-operative game. Primarily used to compute the + Shapley Value, but can also provide other information. - EXAMPLES:: - Basic example of how to implement a co-operative game. :: + INPUT: - sage: test_function = {(): 0, - ....: ('1',): 6, - ....: ('2',): 12, - ....: ('3',): 42, - ....: ('1', '2',): 12, - ....: ('1', '3',): 42, - ....: ('2', '3',): 42, - ....: ('1', '2', '3',): 42} - sage: test_game = CooperativeGame(test_function, ['1', '2', '3']) - - We can also use strings instead of numbers. :: - - sage: letter_function = {(): 0, - ....: ('A',): 6, - ....: ('B',): 12, - ....: ('C',): 42, - ....: ('A', 'B',): 12, - ....: ('A', 'C',): 42, - ....: ('B', 'C',): 42, - ....: ('A', 'B', 'C',): 42} - sage: letter_game = CooperativeGame(letter_function, ['A', 'B', 'C']) - """ + - characteristic_function - a dictionary containing all possible sets of players. + * Key - Each set must be entered as a tuple, not a string. Apart from + the empty set, they must all end with a comma. + * Value - A real number representing each set of players contribution. + + - player_list - a list of all the players. + + EXAMPLES:: + Basic example of how to implement a co-operative game. :: + + sage: integer_function = {(): 0, + ....: (1,): 6, + ....: (2,): 12, + ....: (3,): 42, + ....: (1, 2,): 12, + ....: (1, 3,): 42, + ....: (2, 3,): 42, + ....: (1, 2, 3,): 42} + sage: integer_game = CooperativeGame(integer_function, [1, 2, 3]) + + We can also use strings instead of numbers. :: + + sage: letter_function = {(): 0, + ....: ('A',): 6, + ....: ('B',): 12, + ....: ('C',): 42, + ....: ('A', 'B',): 12, + ....: ('A', 'C',): 42, + ....: ('B', 'C',): 42, + ....: ('A', 'B', 'C',): 42} + sage: letter_game = CooperativeGame(letter_function, ['A', 'B', 'C']) + """ + + def __init__(self, characteristic_function, player_list): self.char_fun = characteristic_function self.number_players = len(player_list) self.player_list = player_list @@ -70,14 +81,14 @@ def shapley_value(self): A typical example of the use of shapley_value. :: sage: test_function = {(): 0, - ....: ('1',): 6, - ....: ('2',): 12, - ....: ('3',): 42, - ....: ('1', '2',): 12, - ....: ('1', '3',): 42, - ....: ('2', '3',): 42, - ....: ('1', '2', '3',): 42} - sage: test_game = CooperativeGame(test_function, ['1', '2', '3']) + ....: (1,): 6, + ....: (2,): 12, + ....: (3,): 42, + ....: (1, 2,): 12, + ....: (1, 3,): 42, + ....: (2, 3,): 42, + ....: (1, 2, 3,): 42} + sage: test_game = CooperativeGame(test_function, [1, 2, 3]) sage: print test_game.shapley_value() [2, 5, 35] """ From 0257aa9d4617090b2ef194aa9a74f56c9a4a1d0c Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 10:38:55 +0100 Subject: [PATCH 123/546] adds payoff_vector to test --- src/sage/game_theory/cooperative_game.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index f3a0d05810a..5e9c5867e6c 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -41,6 +41,9 @@ class CooperativeGame(SageObject): - player_list - a list of all the players. + - payoff_vector - default = ``False``, a list can be passed instead but + this will be overwritten if shapley_value is called. + EXAMPLES:: Basic example of how to implement a co-operative game. :: @@ -67,11 +70,11 @@ class CooperativeGame(SageObject): sage: letter_game = CooperativeGame(letter_function, ['A', 'B', 'C']) """ - def __init__(self, characteristic_function, player_list): + def __init__(self, characteristic_function, player_list, payoff_vector=False): self.char_fun = characteristic_function self.number_players = len(player_list) self.player_list = player_list - self.payoff_vector = False + self.payoff_vector = payoff_vector def shapley_value(self): r""" @@ -89,8 +92,12 @@ def shapley_value(self): ....: (2, 3,): 42, ....: (1, 2, 3,): 42} sage: test_game = CooperativeGame(test_function, [1, 2, 3]) + sage: test_game.payoff_vector + False sage: print test_game.shapley_value() [2, 5, 35] + sage: test_game.payoff_vector + [2, 5, 35] """ payoff_vector = [] for i in self.player_list: From 03c1f4515b266f11e905136d6bda9a9de126b322 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 3 Jun 2014 11:39:54 +0200 Subject: [PATCH 124/546] trac #16430: Small speedup for OA(None,p^c) --- src/sage/combinat/designs/latin_squares.py | 6 ++-- .../combinat/designs/orthogonal_arrays.py | 31 ++++++++++++------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 55a81c96129..0d46459957f 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -265,7 +265,7 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi sage: designs.mutually_orthogonal_latin_squares(1, None) Traceback (most recent call last): ... - ValueError: there are no bound on k when n=1. + ValueError: there are no bound on k when 0<=n<=1 sage: designs.mutually_orthogonal_latin_squares(10,2,existence=True) True sage: designs.mutually_orthogonal_latin_squares(10,2) @@ -289,11 +289,11 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi # Is k is None we find the largest available if k is None: - if n == 1: + if n == 0 or n == 1: if existence: from sage.rings.infinity import Infinity return Infinity - raise ValueError("there are no bound on k when n=1.") + raise ValueError("there are no bound on k when 0<=n<=1") k = orthogonal_array(None,n,existence=True) - 2 if existence: diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 8af57c319a2..0897f08f23d 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -259,15 +259,15 @@ def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): sage: designs.transversal_design(None, 1) Traceback (most recent call last): ... - ValueError: there are no bound on k when n=1. + ValueError: there are no bound on k when 0<=n<=1 """ # Is k is None we find the largest available if k is None: - if n == 1: + if n == 0 or n == 1: if existence: from sage.rings.infinity import Infinity return Infinity - raise ValueError("there are no bound on k when n=1.") + raise ValueError("there are no bound on k when 0<=n<=1") k = orthogonal_array(None,n,existence=True) if existence: @@ -713,7 +713,7 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): sage: designs.orthogonal_array(4,2) Traceback (most recent call last): ... - EmptySetError: No Orthogonal Array exists when k>=n+t except when n=1 + EmptySetError: No Orthogonal Array exists when k>=n+t except when n<=1 sage: designs.orthogonal_array(12,20) Traceback (most recent call last): ... @@ -738,11 +738,16 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): sage: designs.orthogonal_array(None,1) Traceback (most recent call last): ... - ValueError: there are no bound on k when n=1. + ValueError: there are no bound on k when 0<=n<=1 sage: designs.orthogonal_array(None,14,existence=True) 6 sage: designs.orthogonal_array(16,1) [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] + + when `t>2` and `k=None`:: + + sage: designs.orthogonal_array(None,5,t=3,existence=True) + 1 """ from sage.rings.finite_rings.constructor import FiniteField @@ -752,15 +757,17 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): # If k is set to None we find the largest value available if k is None: - if n == 1: + if n == 0 or n == 1: if existence: from sage.rings.infinity import Infinity return Infinity - raise ValueError("there are no bound on k when n=1.") - - for k in range(1,n+2): - if not orthogonal_array(k+1,n,existence=True): - break + raise ValueError("there are no bound on k when 0<=n<=1") + elif t == 2 and projective_plane(n,existence=True): + k = n+1 + else: + for k in range(1,n+2): + if not orthogonal_array(k+1,n,t=t,existence=True): + break if existence: return k @@ -777,7 +784,7 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): # i.e. k=n+t except when n=1") + raise EmptySetError("No Orthogonal Array exists when k>=n+t except when n<=1") elif k == t: if existence: From f7f86b81576f70729fd3aac983f6d159e15a1261 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 10:47:06 +0100 Subject: [PATCH 125/546] creates show function --- src/sage/game_theory/cooperative_game.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 5e9c5867e6c..3dcef1bbec0 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -145,3 +145,14 @@ def get_predecessors(self, player, permutation): else: pred.append(k) play_and_pred.append(k) + + def show(self): + np = self.number_players + cf = self.char_fun + pv = self.payoff_vector + print "A Co-operative Game with %s players" % np + print "It's Characteristic Function is %s" % cf + if pv is False: + print "And it has no Payoff Vector" + else: + print "And its Payoff Vector is %s" % pv From ef178ceeb21fe41446e044e15f472f954c07df3c Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 10:55:51 +0100 Subject: [PATCH 126/546] adds test to show function --- src/sage/game_theory/cooperative_game.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 3dcef1bbec0..9a15077924a 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -147,6 +147,30 @@ def get_predecessors(self, player, permutation): play_and_pred.append(k) def show(self): + r""" + EXAMPLES:: + Typical use of the show function.:: + + sage: letter_function = {(): 0, + ....: ('A',): 6, + ....: ('B',): 12, + ....: ('C',): 42, + ....: ('A', 'B',): 12, + ....: ('A', 'C',): 42, + ....: ('B', 'C',): 42, + ....: ('A', 'B', 'C',): 42} + sage: letter_game = CooperativeGame(letter_function, ['A', 'B', 'C'], [14, 14, 14]) + sage: letter_game.show() + A Co-operative Game with 3 players + It's Characteristic Function is {('A',): 6, ('B', 'C'): 42, (): 0, ('C',): 42, ('A', 'B'): 12, ('B',): 12, ('A', 'C'): 42, ('A', 'B', 'C'): 42} + And its Payoff Vector is [14, 14, 14] + sage: letter_game.shapley_value() + [2, 5, 35] + sage: letter_game.show() + A Co-operative Game with 3 players + It's Characteristic Function is {('A',): 6, ('B', 'C'): 42, (): 0, ('C',): 42, ('A', 'B'): 12, ('B',): 12, ('A', 'C'): 42, ('A', 'B', 'C'): 42} + And its Payoff Vector is [2, 5, 35] + """ np = self.number_players cf = self.char_fun pv = self.payoff_vector From 2905ba92669042f8467227043f61cb9452e30219 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 12:01:16 +0100 Subject: [PATCH 127/546] creates is_monotone function --- src/sage/game_theory/cooperative_game.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 9a15077924a..63e5e4c999c 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -107,12 +107,19 @@ def shapley_value(self): self.payoff_vector = payoff_vector return payoff_vector - def is_monotone(): + def is_monotone(self): r""" Returns True if co-operative game is monotonic. """ + sets = list(self.char_fun.keys()) + for i, k in permutations(sets, 2): + if set(i) <= set(k) and self.char_fun[i] > self.char_fun[k]: + return False + else: + pass + return True - def is_superadditive(): + def is_superadditive(self): r""" Returns True if co-operative game is superadditive. """ From f29ed8c7c2b63c36707eceed9a54e8e2ff7464ae Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 12:21:08 +0100 Subject: [PATCH 128/546] creates is_superadditive function --- src/sage/game_theory/cooperative_game.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 63e5e4c999c..a534de5786a 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -123,6 +123,14 @@ def is_superadditive(self): r""" Returns True if co-operative game is superadditive. """ + sets = list(self.char_fun.keys()) + for i, k in permutations(sets, 2): + j = tuple(set(i) | set(k)) + if self.char_fun[j] < self.char_fun[i] + self.char_fun[k]: + return False + else: + pass + return True def marginal_contributions(self, player): contributions = [] From 71b240ade8e471fd4f56712ad401a32879b4dedb Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 13:04:13 +0100 Subject: [PATCH 129/546] creates tests for is_superadditive and passes them --- src/sage/game_theory/cooperative_game.py | 82 ++++++++++++++++-------- 1 file changed, 57 insertions(+), 25 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index a534de5786a..cedcd67334d 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -48,25 +48,25 @@ class CooperativeGame(SageObject): Basic example of how to implement a co-operative game. :: sage: integer_function = {(): 0, - ....: (1,): 6, - ....: (2,): 12, - ....: (3,): 42, - ....: (1, 2,): 12, - ....: (1, 3,): 42, - ....: (2, 3,): 42, - ....: (1, 2, 3,): 42} + ....: (1,): 6, + ....: (2,): 12, + ....: (3,): 42, + ....: (1, 2,): 12, + ....: (1, 3,): 42, + ....: (2, 3,): 42, + ....: (1, 2, 3,): 42} sage: integer_game = CooperativeGame(integer_function, [1, 2, 3]) We can also use strings instead of numbers. :: sage: letter_function = {(): 0, - ....: ('A',): 6, - ....: ('B',): 12, - ....: ('C',): 42, - ....: ('A', 'B',): 12, - ....: ('A', 'C',): 42, - ....: ('B', 'C',): 42, - ....: ('A', 'B', 'C',): 42} + ....: ('A',): 6, + ....: ('B',): 12, + ....: ('C',): 42, + ....: ('A', 'B',): 12, + ....: ('A', 'C',): 42, + ....: ('B', 'C',): 42, + ....: ('A', 'B', 'C',): 42} sage: letter_game = CooperativeGame(letter_function, ['A', 'B', 'C']) """ @@ -122,14 +122,46 @@ def is_monotone(self): def is_superadditive(self): r""" Returns True if co-operative game is superadditive. + + EXAMPLES:: + An exmple that returns False. :: + + sage: test_function = {(): 0, + ....: (1,): 6, + ....: (2,): 12, + ....: (3,): 42, + ....: (1, 2,): 12, + ....: (1, 3,): 42, + ....: (2, 3,): 42, + ....: (1, 2, 3,): 42} + sage: test_game = CooperativeGame(test_function, [1, 2, 3]) + sage: test_game.is_superadditive() + False + + An exmple that returns True. :: + + sage: A_function = {(): 0, + ....: (1,): 6, + ....: (2,): 12, + ....: (3,): 42, + ....: (1, 2,): 18, + ....: (1, 3,): 48, + ....: (2, 3,): 55, + ....: (1, 2, 3,): 80} + sage: A_game = CooperativeGame(A_function, [1, 2, 3]) + sage: A_game.is_superadditive() + True """ sets = list(self.char_fun.keys()) for i, k in permutations(sets, 2): - j = tuple(set(i) | set(k)) - if self.char_fun[j] < self.char_fun[i] + self.char_fun[k]: - return False - else: + if set(i) & set(k) != set(): pass + else: + j = tuple(set(i) | set(k)) + if self.char_fun[j] < self.char_fun[i] + self.char_fun[k]: + return False + else: + pass return True def marginal_contributions(self, player): @@ -167,13 +199,13 @@ def show(self): Typical use of the show function.:: sage: letter_function = {(): 0, - ....: ('A',): 6, - ....: ('B',): 12, - ....: ('C',): 42, - ....: ('A', 'B',): 12, - ....: ('A', 'C',): 42, - ....: ('B', 'C',): 42, - ....: ('A', 'B', 'C',): 42} + ....: ('A',): 6, + ....: ('B',): 12, + ....: ('C',): 42, + ....: ('A', 'B',): 12, + ....: ('A', 'C',): 42, + ....: ('B', 'C',): 42, + ....: ('A', 'B', 'C',): 42} sage: letter_game = CooperativeGame(letter_function, ['A', 'B', 'C'], [14, 14, 14]) sage: letter_game.show() A Co-operative Game with 3 players From b4438eefe20921c3be5d774995f323691db0c395 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 13:28:48 +0100 Subject: [PATCH 130/546] removes player_list from required attributes --- src/sage/game_theory/cooperative_game.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index cedcd67334d..f4b0f14bef2 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -39,8 +39,6 @@ class CooperativeGame(SageObject): the empty set, they must all end with a comma. * Value - A real number representing each set of players contribution. - - player_list - a list of all the players. - - payoff_vector - default = ``False``, a list can be passed instead but this will be overwritten if shapley_value is called. @@ -55,7 +53,7 @@ class CooperativeGame(SageObject): ....: (1, 3,): 42, ....: (2, 3,): 42, ....: (1, 2, 3,): 42} - sage: integer_game = CooperativeGame(integer_function, [1, 2, 3]) + sage: integer_game = CooperativeGame(integer_function) We can also use strings instead of numbers. :: @@ -67,13 +65,13 @@ class CooperativeGame(SageObject): ....: ('A', 'C',): 42, ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} - sage: letter_game = CooperativeGame(letter_function, ['A', 'B', 'C']) + sage: letter_game = CooperativeGame(letter_function) """ - def __init__(self, characteristic_function, player_list, payoff_vector=False): + def __init__(self, characteristic_function, payoff_vector=False): self.char_fun = characteristic_function - self.number_players = len(player_list) - self.player_list = player_list + self.player_list = list(characteristic_function.keys()[-1]) + self.number_players = len(self.player_list) self.payoff_vector = payoff_vector def shapley_value(self): @@ -91,7 +89,9 @@ def shapley_value(self): ....: (1, 3,): 42, ....: (2, 3,): 42, ....: (1, 2, 3,): 42} - sage: test_game = CooperativeGame(test_function, [1, 2, 3]) + sage: test_game = CooperativeGame(test_function) + sage: test_game.player_list + [1, 2, 3] sage: test_game.payoff_vector False sage: print test_game.shapley_value() @@ -134,7 +134,7 @@ def is_superadditive(self): ....: (1, 3,): 42, ....: (2, 3,): 42, ....: (1, 2, 3,): 42} - sage: test_game = CooperativeGame(test_function, [1, 2, 3]) + sage: test_game = CooperativeGame(test_function) sage: test_game.is_superadditive() False @@ -148,7 +148,7 @@ def is_superadditive(self): ....: (1, 3,): 48, ....: (2, 3,): 55, ....: (1, 2, 3,): 80} - sage: A_game = CooperativeGame(A_function, [1, 2, 3]) + sage: A_game = CooperativeGame(A_function) sage: A_game.is_superadditive() True """ @@ -206,7 +206,7 @@ def show(self): ....: ('A', 'C',): 42, ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} - sage: letter_game = CooperativeGame(letter_function, ['A', 'B', 'C'], [14, 14, 14]) + sage: letter_game = CooperativeGame(letter_function, [14, 14, 14]) sage: letter_game.show() A Co-operative Game with 3 players It's Characteristic Function is {('A',): 6, ('B', 'C'): 42, (): 0, ('C',): 42, ('A', 'B'): 12, ('B',): 12, ('A', 'C'): 42, ('A', 'B', 'C'): 42} From 9ec2701863349e7a1cd9b998911eb9f781416a0c Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 14:18:36 +0100 Subject: [PATCH 131/546] sets up functions for properties of payoff_vector --- src/sage/game_theory/cooperative_game.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index f4b0f14bef2..bbff0e74caa 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -227,3 +227,27 @@ def show(self): print "And it has no Payoff Vector" else: print "And its Payoff Vector is %s" % pv + + def is_efficient(self): + r""" + Returns True if the current payoff_vector is efficient. + """ + if sum(self.payoff_vector) == self.char_fun[tuple(self.player_list)]: + return True + else: + return False + + def is_nullplayer(self): + r""" + Returns True if the current payoff_vector possesses the null player property. + """ + + def is_symmetry(self): + r""" + Returns True if the current payoff_vector ipossesses the symmetry property. + """ + + def is_additivity(self): + r""" + Returns True if the current payoff_vector possesses the additivity property. + """ From c0490eedbf9133305bd1361c61e1d6404b45f4ea Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 14:19:32 +0100 Subject: [PATCH 132/546] change starting paragraph --- src/sage/game_theory/cooperative_game.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index bbff0e74caa..fcba8a3b291 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -2,8 +2,7 @@ Co-operative games with N players. This module implements characteristic function cooperative games. -The main contribution is a class for a characteristic function game -that takes a characteristic function (as a dictionary) as an input. +The main contribution is a class for a characteristic function game. Methods to calculate the Shapley value (a fair way of sharing common resources: https://www.youtube.com/watch?v=aThG4YAFErw) as well as test properties of the game (monotonicity, super additivity) are also included. From 0a56cc93379ab8c4456ffbe98d8ad32739971468 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 15:14:01 +0100 Subject: [PATCH 133/546] creates is_nullplayer function --- src/sage/game_theory/cooperative_game.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index fcba8a3b291..f1751895636 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -93,7 +93,7 @@ def shapley_value(self): [1, 2, 3] sage: test_game.payoff_vector False - sage: print test_game.shapley_value() + sage: test_game.shapley_value() [2, 5, 35] sage: test_game.payoff_vector [2, 5, 35] @@ -240,6 +240,26 @@ def is_nullplayer(self): r""" Returns True if the current payoff_vector possesses the null player property. """ + sets = list(self.char_fun.keys()) + element = [i for i in sets if len(i) == 1] + other = [i for i in sets if len(i) > 1] + status = True + for j in element: + for k in other: + union = tuple(set(j) | set(k)) + if self.char_fun[union] == self.char_fun[j]: + pass + else: + status = False + break + + if status is False: + pass + elif status is True and self.char_fun[j] == 0: + pass + else: + return False + return True def is_symmetry(self): r""" From 014b103165f417ec7e82b275a44430343fd79a12 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 15:31:40 +0100 Subject: [PATCH 134/546] creates is_symmetry function --- src/sage/game_theory/cooperative_game.py | 27 +++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index f1751895636..beee612e8ee 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -22,7 +22,7 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from itertools import permutations +from itertools import permutations, combinations from sage.structure.sage_object import SageObject @@ -242,7 +242,7 @@ def is_nullplayer(self): """ sets = list(self.char_fun.keys()) element = [i for i in sets if len(i) == 1] - other = [i for i in sets if len(i) > 1] + other = [i for i in sets if len(i) > 0] status = True for j in element: for k in other: @@ -255,7 +255,7 @@ def is_nullplayer(self): if status is False: pass - elif status is True and self.char_fun[j] == 0: + elif self.char_fun[j] == 0: pass else: return False @@ -265,6 +265,27 @@ def is_symmetry(self): r""" Returns True if the current payoff_vector ipossesses the symmetry property. """ + sets = list(self.char_fun.keys()) + element = [i for i in sets if len(i) == 1] + other = [i for i in sets if len(i) > 0] + status = True + for j, k in combinations(element, 2): + for m in other: + junion = tuple(set(j) | set(m)) + kunion = tuple(set(k) | set(m)) + if self.char_fun[junion] == self.char_fun[kunion]: + pass + else: + status = False + break + + if status is False: + pass + elif self.char_fun[j] == self.char_fun[k]: + pass + else: + return False + return True def is_additivity(self): r""" From 60325bb1ea9a7bc1b942768b18eb247eafdcc7d3 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 15:42:21 +0100 Subject: [PATCH 135/546] fixes error in is_nullplayer when using strings --- src/sage/game_theory/cooperative_game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index beee612e8ee..8ae73bb1199 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -246,7 +246,7 @@ def is_nullplayer(self): status = True for j in element: for k in other: - union = tuple(set(j) | set(k)) + union = tuple(sorted(set(j) | set(k))) if self.char_fun[union] == self.char_fun[j]: pass else: From 5b4e01a554e03eaed3f00147903a38042e0a3a05 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 15:57:27 +0100 Subject: [PATCH 136/546] adds lots more examples --- src/sage/game_theory/cooperative_game.py | 81 ++++++++++++++++++++---- 1 file changed, 70 insertions(+), 11 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 8ae73bb1199..4fe5fdc8a1b 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -42,7 +42,8 @@ class CooperativeGame(SageObject): this will be overwritten if shapley_value is called. EXAMPLES:: - Basic example of how to implement a co-operative game. :: + Basic example of how to implement a co-operative game. These functions will + be used repeatedly in other examples. :: sage: integer_function = {(): 0, ....: (1,): 6, @@ -80,7 +81,7 @@ def shapley_value(self): EXAMPLES:: A typical example of the use of shapley_value. :: - sage: test_function = {(): 0, + sage: integer_function = {(): 0, ....: (1,): 6, ....: (2,): 12, ....: (3,): 42, @@ -88,14 +89,14 @@ def shapley_value(self): ....: (1, 3,): 42, ....: (2, 3,): 42, ....: (1, 2, 3,): 42} - sage: test_game = CooperativeGame(test_function) - sage: test_game.player_list + sage: integer_game = CooperativeGame(integer_function) + sage: integer_game.player_list [1, 2, 3] - sage: test_game.payoff_vector + sage: integer_game.payoff_vector False - sage: test_game.shapley_value() + sage: integer_game.shapley_value() [2, 5, 35] - sage: test_game.payoff_vector + sage: integer_game.payoff_vector [2, 5, 35] """ payoff_vector = [] @@ -109,6 +110,21 @@ def shapley_value(self): def is_monotone(self): r""" Returns True if co-operative game is monotonic. + + EXAMPLES:: + Shows the use of is_monotone on a simple game. :: + + sage: integer_function = {(): 0, + ....: (1,): 6, + ....: (2,): 12, + ....: (3,): 42, + ....: (1, 2,): 12, + ....: (1, 3,): 42, + ....: (2, 3,): 42, + ....: (1, 2, 3,): 42} + sage: integer_game = CooperativeGame(integer_function) + sage: integer_game.is_monotone() + True """ sets = list(self.char_fun.keys()) for i, k in permutations(sets, 2): @@ -125,7 +141,7 @@ def is_superadditive(self): EXAMPLES:: An exmple that returns False. :: - sage: test_function = {(): 0, + sage: integer_function = {(): 0, ....: (1,): 6, ....: (2,): 12, ....: (3,): 42, @@ -133,8 +149,8 @@ def is_superadditive(self): ....: (1, 3,): 42, ....: (2, 3,): 42, ....: (1, 2, 3,): 42} - sage: test_game = CooperativeGame(test_function) - sage: test_game.is_superadditive() + sage: integer_game = CooperativeGame(integer_function) + sage: integer_game.is_superadditive() False An exmple that returns True. :: @@ -230,6 +246,21 @@ def show(self): def is_efficient(self): r""" Returns True if the current payoff_vector is efficient. + + EXAMPLES:: + An efficient payoff_vector.:: + + sage: letter_function = {(): 0, + ....: ('A',): 6, + ....: ('B',): 12, + ....: ('C',): 42, + ....: ('A', 'B',): 12, + ....: ('A', 'C',): 42, + ....: ('B', 'C',): 42, + ....: ('A', 'B', 'C',): 42} + sage: letter_game = CooperativeGame(letter_function, [14, 14, 14]) + sage: letter_game.is_efficient() + True """ if sum(self.payoff_vector) == self.char_fun[tuple(self.player_list)]: return True @@ -239,6 +270,20 @@ def is_efficient(self): def is_nullplayer(self): r""" Returns True if the current payoff_vector possesses the null player property. + + EXAMPLES:: + + sage: integer_function = {(): 0, + ....: (1,): 6, + ....: (2,): 12, + ....: (3,): 42, + ....: (1, 2,): 12, + ....: (1, 3,): 42, + ....: (2, 3,): 42, + ....: (1, 2, 3,): 42} + sage: integer_game = CooperativeGame(integer_function, [2, 5, 35]) + sage: integer_game.is_symmetry() + True """ sets = list(self.char_fun.keys()) element = [i for i in sets if len(i) == 1] @@ -263,7 +308,21 @@ def is_nullplayer(self): def is_symmetry(self): r""" - Returns True if the current payoff_vector ipossesses the symmetry property. + Returns True if the current payoff_vector possesses the symmetry property. + + EXAMPLES:: + + sage: integer_function = {(): 0, + ....: (1,): 6, + ....: (2,): 12, + ....: (3,): 42, + ....: (1, 2,): 12, + ....: (1, 3,): 42, + ....: (2, 3,): 42, + ....: (1, 2, 3,): 42} + sage: integer_game = CooperativeGame(integer_function, [2, 5, 35]) + sage: integer_game.is_symmetry() + True """ sets = list(self.char_fun.keys()) element = [i for i in sets if len(i) == 1] From b2e35c0303ed2267b1086607d4163aec9a89cfdc Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 16:32:31 +0100 Subject: [PATCH 137/546] is_nullplayer passes all tests --- src/sage/game_theory/cooperative_game.py | 41 ++++++++++++++++-------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 4fe5fdc8a1b..ab4fd3dd2dd 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -272,27 +272,42 @@ def is_nullplayer(self): Returns True if the current payoff_vector possesses the null player property. EXAMPLES:: + A payoff_vector that returns True. :: - sage: integer_function = {(): 0, - ....: (1,): 6, - ....: (2,): 12, - ....: (3,): 42, - ....: (1, 2,): 12, - ....: (1, 3,): 42, - ....: (2, 3,): 42, - ....: (1, 2, 3,): 42} - sage: integer_game = CooperativeGame(integer_function, [2, 5, 35]) - sage: integer_game.is_symmetry() + sage: letter_function = {(): 0, + ....: ('A',): 6, + ....: ('B',): 12, + ....: ('C',): 42, + ....: ('A', 'B',): 12, + ....: ('A', 'C',): 42, + ....: ('B', 'C',): 42, + ....: ('A', 'B', 'C',): 42} + sage: letter_game = CooperativeGame(letter_function, [14, 14, 14]) + sage: letter_game.is_nullplayer() True + + A payoff_vector that returns False. :: + + sage: A_function = {(): 0, + ....: (1,): 0, + ....: (2,): 12, + ....: (3,): 42, + ....: (1, 2,): 12, + ....: (1, 3,): 42, + ....: (2, 3,): 55, + ....: (1, 2, 3,): 55} + sage: A_game = CooperativeGame(A_function, [10, 10, 25]) + sage: A_game.is_nullplayer() + False """ sets = list(self.char_fun.keys()) element = [i for i in sets if len(i) == 1] - other = [i for i in sets if len(i) > 0] + other = [i for i in sets] status = True for j in element: for k in other: union = tuple(sorted(set(j) | set(k))) - if self.char_fun[union] == self.char_fun[j]: + if self.char_fun[union] == self.char_fun[k]: pass else: status = False @@ -300,7 +315,7 @@ def is_nullplayer(self): if status is False: pass - elif self.char_fun[j] == 0: + elif self.payoff_vector[element.index(j)] == 0: pass else: return False From 2d733d78b9736eea1f1a9e577df936c7d8801e56 Mon Sep 17 00:00:00 2001 From: drvinceknight Date: Tue, 3 Jun 2014 16:40:19 +0100 Subject: [PATCH 138/546] Changed show method to be slightly nicer on the eye. Needs to be changed in docs --- src/sage/game_theory/cooperative_game.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 4fe5fdc8a1b..807b4621fd5 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -237,11 +237,13 @@ def show(self): cf = self.char_fun pv = self.payoff_vector print "A Co-operative Game with %s players" % np - print "It's Characteristic Function is %s" % cf + print "Characteristic Function is" + for key in cf: + print "\t %s : %s" %(key, cf[key]) if pv is False: - print "And it has no Payoff Vector" + print "And it has no Shapley Value" else: - print "And its Payoff Vector is %s" % pv + print "Shapley Value is %s" % pv def is_efficient(self): r""" From 848324742966e682be8d28e570fe343360006ebc Mon Sep 17 00:00:00 2001 From: drvinceknight Date: Tue, 3 Jun 2014 17:05:45 +0100 Subject: [PATCH 139/546] Changed show output to read payoff vector --- src/sage/game_theory/cooperative_game.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 9a88f17d376..70b2c8b319b 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -241,9 +241,9 @@ def show(self): for key in cf: print "\t %s : %s" %(key, cf[key]) if pv is False: - print "And it has no Shapley Value" + print "And it has no payoff vector" else: - print "Shapley Value is %s" % pv + print "Payoff vector is %s" % pv def is_efficient(self): r""" From ffbb1c880156a7145239505fda0b5ec263c71708 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 19:01:55 +0100 Subject: [PATCH 140/546] rewrites nullplayer --- src/sage/game_theory/cooperative_game.py | 43 ++++++++++-------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index ab4fd3dd2dd..58fb3d01b74 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -267,7 +267,7 @@ def is_efficient(self): else: return False - def is_nullplayer(self): + def nullplayer(self): r""" Returns True if the current payoff_vector possesses the null player property. @@ -275,7 +275,7 @@ def is_nullplayer(self): A payoff_vector that returns True. :: sage: letter_function = {(): 0, - ....: ('A',): 6, + ....: ('A',): 0, ....: ('B',): 12, ....: ('C',): 42, ....: ('A', 'B',): 12, @@ -283,13 +283,13 @@ def is_nullplayer(self): ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} sage: letter_game = CooperativeGame(letter_function, [14, 14, 14]) - sage: letter_game.is_nullplayer() - True + sage: letter_game.nullplayer() + [('A',)] A payoff_vector that returns False. :: sage: A_function = {(): 0, - ....: (1,): 0, + ....: (1,): 6, ....: (2,): 12, ....: (3,): 42, ....: (1, 2,): 12, @@ -297,29 +297,22 @@ def is_nullplayer(self): ....: (2, 3,): 55, ....: (1, 2, 3,): 55} sage: A_game = CooperativeGame(A_function, [10, 10, 25]) - sage: A_game.is_nullplayer() - False + sage: A_game.nullplayer() + [] """ sets = list(self.char_fun.keys()) - element = [i for i in sets if len(i) == 1] - other = [i for i in sets] - status = True - for j in element: - for k in other: - union = tuple(sorted(set(j) | set(k))) - if self.char_fun[union] == self.char_fun[k]: - pass - else: - status = False - break - - if status is False: - pass - elif self.payoff_vector[element.index(j)] == 0: - pass + player = [(i,) for i in self.player_list if self.char_fun[(i,)] == 0] + nulls = [] + for k in player: + test = [set(m) for m in sets if k in m] + results = [] + for j in test: + results.append(self.char_fun[tuple(j.remove(k))] == self.char_fun[tuple(j)]) + if all(results): + nulls.append(k) else: - return False - return True + pass + return nulls def is_symmetry(self): r""" From dd3ef27e27de0a7f28ae5c5186a04315111e4088 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 21:08:09 +0100 Subject: [PATCH 141/546] fixes show tests --- src/sage/game_theory/cooperative_game.py | 32 ++++++++++++++++++------ 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 8c6d260d319..783d6b3237c 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -224,14 +224,30 @@ def show(self): sage: letter_game = CooperativeGame(letter_function, [14, 14, 14]) sage: letter_game.show() A Co-operative Game with 3 players - It's Characteristic Function is {('A',): 6, ('B', 'C'): 42, (): 0, ('C',): 42, ('A', 'B'): 12, ('B',): 12, ('A', 'C'): 42, ('A', 'B', 'C'): 42} - And its Payoff Vector is [14, 14, 14] + Characteristic Function is + ('A',) : 6 + ('B', 'C') : 42 + () : 0 + ('C',) : 42 + ('A', 'B') : 12 + ('B',) : 12 + ('A', 'C') : 42 + ('A', 'B', 'C') : 42 + Payoff vector is [14, 14, 14] sage: letter_game.shapley_value() [2, 5, 35] sage: letter_game.show() A Co-operative Game with 3 players - It's Characteristic Function is {('A',): 6, ('B', 'C'): 42, (): 0, ('C',): 42, ('A', 'B'): 12, ('B',): 12, ('A', 'C'): 42, ('A', 'B', 'C'): 42} - And its Payoff Vector is [2, 5, 35] + Characteristic Function is + ('A',) : 6 + ('B', 'C') : 42 + () : 0 + ('C',) : 42 + ('A', 'B') : 12 + ('B',) : 12 + ('A', 'C') : 42 + ('A', 'B', 'C') : 42 + Payoff vector is [2, 5, 35] """ np = self.number_players cf = self.char_fun @@ -323,8 +339,8 @@ def is_symmetry(self): EXAMPLES:: sage: integer_function = {(): 0, - ....: (1,): 6, - ....: (2,): 12, + ....: (1,): 5, + ....: (2,): 5, ....: (3,): 42, ....: (1, 2,): 12, ....: (1, 3,): 42, @@ -332,7 +348,7 @@ def is_symmetry(self): ....: (1, 2, 3,): 42} sage: integer_game = CooperativeGame(integer_function, [2, 5, 35]) sage: integer_game.is_symmetry() - True + False """ sets = list(self.char_fun.keys()) element = [i for i in sets if len(i) == 1] @@ -350,7 +366,7 @@ def is_symmetry(self): if status is False: pass - elif self.char_fun[j] == self.char_fun[k]: + elif self.payoff_vector[self.player_list.index(j)] == self.payoff_vector[self.player_list.index(k)]: pass else: return False From bbfcb0bbd67292d2b1f0026adf79b59dedd853c6 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 21:23:30 +0100 Subject: [PATCH 142/546] Fixes #10 including all associated documentation --- src/sage/game_theory/cooperative_game.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 783d6b3237c..0e7e6adc364 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -38,7 +38,7 @@ class CooperativeGame(SageObject): the empty set, they must all end with a comma. * Value - A real number representing each set of players contribution. - - payoff_vector - default = ``False``, a list can be passed instead but + - payoff_vector - default = ``False``, a dictionary can be passed instead but this will be overwritten if shapley_value is called. EXAMPLES:: @@ -95,15 +95,15 @@ def shapley_value(self): sage: integer_game.payoff_vector False sage: integer_game.shapley_value() - [2, 5, 35] + {1: 2, 2: 5, 3: 35} sage: integer_game.payoff_vector - [2, 5, 35] + {1: 2, 2: 5, 3: 35} """ - payoff_vector = [] + payoff_vector = {} for i in self.player_list: player_contribution = self.marginal_contributions(i) average = sum(player_contribution) / len(player_contribution) - payoff_vector.append(average) + payoff_vector[i] = average self.payoff_vector = payoff_vector return payoff_vector @@ -235,7 +235,7 @@ def show(self): ('A', 'B', 'C') : 42 Payoff vector is [14, 14, 14] sage: letter_game.shapley_value() - [2, 5, 35] + {'A': 2, 'C': 35, 'B': 5} sage: letter_game.show() A Co-operative Game with 3 players Characteristic Function is @@ -247,7 +247,7 @@ def show(self): ('B',) : 12 ('A', 'C') : 42 ('A', 'B', 'C') : 42 - Payoff vector is [2, 5, 35] + Payoff vector is {'A': 2, 'C': 35, 'B': 5} """ np = self.number_players cf = self.char_fun From 65d06b81e5c0ead52129399b826353f7c0cd65a7 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 3 Jun 2014 22:02:03 +0100 Subject: [PATCH 143/546] fixes is_symmetry and updates tests for payoff_vector dictionary --- src/sage/game_theory/cooperative_game.py | 33 ++++++++++-------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 0e7e6adc364..087152fb934 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -221,7 +221,7 @@ def show(self): ....: ('A', 'C',): 42, ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} - sage: letter_game = CooperativeGame(letter_function, [14, 14, 14]) + sage: letter_game = CooperativeGame(letter_function, {'A': 14, 'B': 14, 'C': 14}) sage: letter_game.show() A Co-operative Game with 3 players Characteristic Function is @@ -233,7 +233,7 @@ def show(self): ('B',) : 12 ('A', 'C') : 42 ('A', 'B', 'C') : 42 - Payoff vector is [14, 14, 14] + Payoff vector is {'A': 14, 'C': 14, 'B': 14} sage: letter_game.shapley_value() {'A': 2, 'C': 35, 'B': 5} sage: letter_game.show() @@ -276,11 +276,11 @@ def is_efficient(self): ....: ('A', 'C',): 42, ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} - sage: letter_game = CooperativeGame(letter_function, [14, 14, 14]) + sage: letter_game = CooperativeGame(letter_function, {'A': 14, 'B': 14, 'C': 14}) sage: letter_game.is_efficient() True """ - if sum(self.payoff_vector) == self.char_fun[tuple(self.player_list)]: + if sum(self.payoff_vector.values()) == self.char_fun[tuple(self.player_list)]: return True else: return False @@ -300,7 +300,7 @@ def nullplayer(self): ....: ('A', 'C',): 42, ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} - sage: letter_game = CooperativeGame(letter_function, [14, 14, 14]) + sage: letter_game = CooperativeGame(letter_function, {'A': 14, 'B': 14, 'C': 14}) sage: letter_game.nullplayer() [('A',)] @@ -314,7 +314,7 @@ def nullplayer(self): ....: (1, 3,): 42, ....: (2, 3,): 55, ....: (1, 2, 3,): 55} - sage: A_game = CooperativeGame(A_function, [10, 10, 25]) + sage: A_game = CooperativeGame(A_function, {1: 10, 2: 10, 3: 25}) sage: A_game.nullplayer() [] """ @@ -346,30 +346,25 @@ def is_symmetry(self): ....: (1, 3,): 42, ....: (2, 3,): 42, ....: (1, 2, 3,): 42} - sage: integer_game = CooperativeGame(integer_function, [2, 5, 35]) + sage: integer_game = CooperativeGame(integer_function, {1: 2, 2: 5, 3: 35}) sage: integer_game.is_symmetry() False """ sets = list(self.char_fun.keys()) element = [i for i in sets if len(i) == 1] - other = [i for i in sets if len(i) > 0] - status = True + other = [i for i in sets] for j, k in combinations(element, 2): for m in other: junion = tuple(set(j) | set(m)) kunion = tuple(set(k) | set(m)) - if self.char_fun[junion] == self.char_fun[kunion]: + results = [] + results.append(self.char_fun[junion] == self.char_fun[kunion]) + if all(results) and self.payoff_vector[j[0]] == self.payoff_vector[k[0]]: pass + elif all(results): + return False else: - status = False - break - - if status is False: - pass - elif self.payoff_vector[self.player_list.index(j)] == self.payoff_vector[self.player_list.index(k)]: - pass - else: - return False + pass return True def is_additivity(self): From 0d16c0bb839c66d251309a9f460ac0130debab8c Mon Sep 17 00:00:00 2001 From: vince Date: Wed, 4 Jun 2014 08:20:51 +0100 Subject: [PATCH 144/546] Changed iteration from i to player for more readable code --- src/sage/game_theory/cooperative_game.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 087152fb934..76ead610584 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -100,10 +100,10 @@ def shapley_value(self): {1: 2, 2: 5, 3: 35} """ payoff_vector = {} - for i in self.player_list: - player_contribution = self.marginal_contributions(i) + for player in self.player_list: + player_contribution = self.marginal_contributions(player) average = sum(player_contribution) / len(player_contribution) - payoff_vector[i] = average + payoff_vector[player] = average self.payoff_vector = payoff_vector return payoff_vector From adcf57bbf4dc037a98afa927d0d1a2637e3209ea Mon Sep 17 00:00:00 2001 From: vince Date: Wed, 4 Jun 2014 08:25:24 +0100 Subject: [PATCH 145/546] Spelling of word example --- src/sage/game_theory/cooperative_game.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 76ead610584..69955a2418a 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -139,7 +139,7 @@ def is_superadditive(self): Returns True if co-operative game is superadditive. EXAMPLES:: - An exmple that returns False. :: + An example that returns False. :: sage: integer_function = {(): 0, ....: (1,): 6, @@ -153,7 +153,7 @@ def is_superadditive(self): sage: integer_game.is_superadditive() False - An exmple that returns True. :: + An example that returns True. :: sage: A_function = {(): 0, ....: (1,): 6, From a3eb4f638ed4738fca1d1a040c511a97c6529bd1 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 08:32:46 +0100 Subject: [PATCH 146/546] changes output for nullplayer --- src/sage/game_theory/cooperative_game.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 087152fb934..19f5a8948d4 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -300,9 +300,9 @@ def nullplayer(self): ....: ('A', 'C',): 42, ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} - sage: letter_game = CooperativeGame(letter_function, {'A': 14, 'B': 14, 'C': 14}) + sage: letter_game = CooperativeGame(letter_function, {'A': 0, 'B': 14, 'C': 14}) sage: letter_game.nullplayer() - [('A',)] + True A payoff_vector that returns False. :: @@ -316,7 +316,7 @@ def nullplayer(self): ....: (1, 2, 3,): 55} sage: A_game = CooperativeGame(A_function, {1: 10, 2: 10, 3: 25}) sage: A_game.nullplayer() - [] + True """ sets = list(self.char_fun.keys()) player = [(i,) for i in self.player_list if self.char_fun[(i,)] == 0] @@ -330,7 +330,12 @@ def nullplayer(self): nulls.append(k) else: pass - return nulls + for i in nulls: + if self.payoff_vector[i[0]] != 0: + return False + else: + pass + return True def is_symmetry(self): r""" From 17472bf997d2d76ab417c9c883e468f23c0eb1a9 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 08:33:32 +0100 Subject: [PATCH 147/546] changes name of is_symmetry to symmetry --- src/sage/game_theory/cooperative_game.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 19f5a8948d4..2f6766e514c 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -337,7 +337,7 @@ def nullplayer(self): pass return True - def is_symmetry(self): + def symmetry(self): r""" Returns True if the current payoff_vector possesses the symmetry property. @@ -352,7 +352,7 @@ def is_symmetry(self): ....: (2, 3,): 42, ....: (1, 2, 3,): 42} sage: integer_game = CooperativeGame(integer_function, {1: 2, 2: 5, 3: 35}) - sage: integer_game.is_symmetry() + sage: integer_game.symmetry() False """ sets = list(self.char_fun.keys()) From 886995cb4a10a4e2ce64fbd8afe9a4680d78765f Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 08:58:08 +0100 Subject: [PATCH 148/546] adds lots of examples to documentation --- src/sage/game_theory/cooperative_game.py | 39 ++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index f9db35d49ed..698c2f7df41 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -66,6 +66,45 @@ class CooperativeGame(SageObject): ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} sage: letter_game = CooperativeGame(letter_function) + + From this we can now compute the Shapley Value. :: + + sage: letter_game.shapley_value() + {'A': 2, 'B': 5, 'C': 35} + + We can test if it is Monotonic or Superadditive. :: + + sage: letter_game.is_monotone() + True + sage: letter_game.is_superadditive() + True + + The show function will give us basic information about the game. :: + + sage: letter_game.show() + A Co-operative Game with 3 players + Characteristic Function is + ('A',) : 6 + ('B', 'C') : 42 + () : 0 + ('C',) : 42 + ('A', 'B') : 12 + ('B',) : 12 + ('A', 'C') : 42 + ('A', 'B', 'C') : 42 + Payoff vector is {'A': 2, 'C': 35, 'B': 5} + + Finally, we can test 3 basic properties of the Payoff Vector. They are, + * Is it is efficient? + * Does it possess the nullplayer property? + * Does it possesss the symmetry property? :: + + sage: letter_game.is_efficient() + True + sage: letter_game.nullplayer() + True + sage: letter_game.symmetry() + True """ def __init__(self, characteristic_function, payoff_vector=False): From cbbd4fbc5f7c1b52c3194c98201c54287aafb2a3 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 09:44:49 +0100 Subject: [PATCH 149/546] symmetry works with strings --- src/sage/game_theory/cooperative_game.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 698c2f7df41..092cd654352 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -383,8 +383,8 @@ def symmetry(self): EXAMPLES:: sage: integer_function = {(): 0, - ....: (1,): 5, - ....: (2,): 5, + ....: (1,): 12, + ....: (2,): 12, ....: (3,): 42, ....: (1, 2,): 12, ....: (1, 3,): 42, @@ -398,17 +398,17 @@ def symmetry(self): element = [i for i in sets if len(i) == 1] other = [i for i in sets] for j, k in combinations(element, 2): + results = [] for m in other: - junion = tuple(set(j) | set(m)) - kunion = tuple(set(k) | set(m)) - results = [] + junion = tuple(sorted(list(set(j) | set(m)))) + kunion = tuple(sorted(list(set(k) | set(m)))) results.append(self.char_fun[junion] == self.char_fun[kunion]) - if all(results) and self.payoff_vector[j[0]] == self.payoff_vector[k[0]]: - pass - elif all(results): - return False - else: - pass + if all(results) and self.payoff_vector[j[0]] == self.payoff_vector[k[0]]: + pass + elif all(results): + return False + else: + pass return True def is_additivity(self): From 4193ca40b72511064c5f71e451308cac4de2fd1d Mon Sep 17 00:00:00 2001 From: Punarbasu Purkayastha Date: Wed, 4 Jun 2014 16:47:26 +0800 Subject: [PATCH 150/546] This patch fixes two things, both related to exclusion of invalid plot points. 1. automatically exclude invalid points from a plot 2. fix excluded regions in case more than one exclude point was in an invalid region Thanks to kcrisman and ddrake for the idea how to do this auto exclusion --- src/sage/plot/plot.py | 62 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index 041d15fe635..f6cee439b01 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -752,13 +752,22 @@ def plot(funcs, *args, **kwds): - ``fillalpha`` - (default: 0.5) How transparent the fill is. A number between 0 and 1. - Note that this function does NOT simply sample equally spaced - points between ``xmin`` and ``xmax``. Instead it computes equally spaced - points and add small perturbations to them. This reduces the - possibility of, e.g., sampling sin only at multiples of - `2\pi`, which would yield a very misleading graph. + .. note:: + + - this function does NOT simply sample equally spaced points + between xmin and xmax. Instead it computes equally spaced points + and adds small perturbations to them. This reduces the possibility + of, e.g., sampling sin only at multiples of `2\pi`, which would + yield a very misleading graph. + + - if there is a range of consecutive points where the function has + no value, then those points will be excluded from the plot. See + the example below on automatic exclusion of points. - EXAMPLES: We plot the sin function:: + + EXAMPLES: + + We plot the sin function:: sage: P = plot(sin, (0,10)); print P Graphics object consisting of 1 graphics primitive @@ -1043,6 +1052,17 @@ def plot(funcs, *args, **kwds): sage: f(x) = (floor(x)+0.5) / (1-(x-0.5)^2) sage: plot(f, (x, -3.5, 3.5), detect_poles = 'show', exclude = [-3..3], ymin = -5, ymax = 5) + Regions in which the plot has no values are automatically excluded. The + regions thus excluded are in addition to the exclusion points present + in the ``exclude`` keyword argument.:: + + sage: set_verbose(-1) + sage: plot(arcsec, (x, -2, 2)) # [-1, 1] is excluded automatically + + sage: plot(arcsec, (x, -2, 2), exclude=[1.5]) # x=1.5 is also excluded + + sage: set_verbose(0) + TESTS: We do not randomize the endpoints:: @@ -1316,6 +1336,24 @@ def _plot(funcs, xrange, parametric=False, else: data = generate_plot_points(f, xrange, plot_points, adaptive_tolerance, adaptive_recursion, randomize) + # Need exclude to be a list to be able to do automatic exclusion of + # plot regions. + if exclude is None: + exclude = [] + + for i in range(len(data)-1): + # If the difference between consecutive x-values is more than + # 2 times the difference between two consecutive plot points, then + # add an exclusion point. + if abs(data[i+1][0] - data[i][0]) > 2*abs(xmax - xmin)/plot_points: + exclude.append((data[i][0] + data[i+1][0])/2) + + # We set exclude back to None if there are no points to be excluded + if exclude == []: + exclude = None + else: + exclude = sorted(exclude) + if parametric: # We need the original x-values to be able to exclude points in parametric plots exclude_data = data @@ -1425,11 +1463,13 @@ def _plot(funcs, xrange, parametric=False, if exclude is not None and (x0 <= exclusion_point <= x1): G += line(data[start_index:i], **options) start_index = i + 2 - try: - exclusion_point = exclude.pop() - except IndexError: - # all excluded points were considered - exclude = None + while exclusion_point <= x1: + try: + exclusion_point = exclude.pop() + except IndexError: + # all excluded points were considered + exclude = None + break G += line(data[start_index:], legend_label=legend_label, **options) else: From c31f63ed3860dbf2368ed17b3f54d8dd0ebf0331 Mon Sep 17 00:00:00 2001 From: Punarbasu Purkayastha Date: Wed, 4 Jun 2014 16:50:50 +0800 Subject: [PATCH 151/546] fix parametric plots that fail due to invalid plot points --- src/sage/plot/plot.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index f6cee439b01..198f18db30e 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -1252,6 +1252,11 @@ def _plot(funcs, xrange, parametric=False, 1 sage: p1.show(ymin=-10,ymax=10) # should be one legend + Parametric plots that get evaluated at invalid points should still + plot properly (:trac:`13246`):: + + sage: parametric_plot((x, arcsec(x)), (x, -2, 2)) + """ from sage.plot.misc import setup_for_eval_on_grid @@ -1348,16 +1353,23 @@ def _plot(funcs, xrange, parametric=False, if abs(data[i+1][0] - data[i][0]) > 2*abs(xmax - xmin)/plot_points: exclude.append((data[i][0] + data[i+1][0])/2) - # We set exclude back to None if there are no points to be excluded - if exclude == []: - exclude = None - else: - exclude = sorted(exclude) - if parametric: # We need the original x-values to be able to exclude points in parametric plots exclude_data = data - data = [(fdata, g(x)) for x, fdata in data] + newdata = [] + for x,fdata in data: + try: + newdata.append((fdata, g(x))) + except ValueError: + newdata.append((fdata, 0)) # append a dummy value 0 + exclude.append(x) + data = newdata + + # We set exclude back to None if there are no points to be excluded + if not exclude: + exclude = None + else: + exclude.sort() G = Graphics() From 2766d45d19aaa10ee29e782baf3d683b05d036b5 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 09:54:13 +0100 Subject: [PATCH 152/546] superadditive works with strings --- src/sage/game_theory/cooperative_game.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 092cd654352..4c747f4ad13 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -70,14 +70,14 @@ class CooperativeGame(SageObject): From this we can now compute the Shapley Value. :: sage: letter_game.shapley_value() - {'A': 2, 'B': 5, 'C': 35} + {'A': 2, 'C': 35, 'B': 5} We can test if it is Monotonic or Superadditive. :: sage: letter_game.is_monotone() True sage: letter_game.is_superadditive() - True + False The show function will give us basic information about the game. :: @@ -211,7 +211,7 @@ def is_superadditive(self): if set(i) & set(k) != set(): pass else: - j = tuple(set(i) | set(k)) + j = tuple(sorted(list(set(i) | set(k)))) if self.char_fun[j] < self.char_fun[i] + self.char_fun[k]: return False else: From 000932d4e317e99a7cd4bc6cddef3f07eff5aa02 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 10:09:58 +0100 Subject: [PATCH 153/546] makes class documentation look good --- src/sage/game_theory/cooperative_game.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 4c747f4ad13..2935cd6d297 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -94,10 +94,11 @@ class CooperativeGame(SageObject): ('A', 'B', 'C') : 42 Payoff vector is {'A': 2, 'C': 35, 'B': 5} - Finally, we can test 3 basic properties of the Payoff Vector. They are, + Finally, we can test 3 basic properties of the Payoff Vector. They are * Is it is efficient? * Does it possess the nullplayer property? - * Does it possesss the symmetry property? :: + * Does it possesss the symmetry property? + :: sage: letter_game.is_efficient() True From 778b4b81f1b218b8fee4d85b917055549a8a2917 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 10:28:52 +0100 Subject: [PATCH 154/546] improves names of some variables --- src/sage/game_theory/cooperative_game.py | 45 ++++++++++++------------ 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 2935cd6d297..871ab7235a3 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -109,7 +109,7 @@ class CooperativeGame(SageObject): """ def __init__(self, characteristic_function, payoff_vector=False): - self.char_fun = characteristic_function + self.ch_f = characteristic_function self.player_list = list(characteristic_function.keys()[-1]) self.number_players = len(self.player_list) self.payoff_vector = payoff_vector @@ -166,9 +166,9 @@ def is_monotone(self): sage: integer_game.is_monotone() True """ - sets = list(self.char_fun.keys()) - for i, k in permutations(sets, 2): - if set(i) <= set(k) and self.char_fun[i] > self.char_fun[k]: + sets = list(self.ch_f.keys()) + for p1, p2 in permutations(sets, 2): + if set(p1) <= set(p2) and self.ch_f[p1] > self.ch_f[p2]: return False else: pass @@ -207,13 +207,13 @@ def is_superadditive(self): sage: A_game.is_superadditive() True """ - sets = list(self.char_fun.keys()) - for i, k in permutations(sets, 2): - if set(i) & set(k) != set(): + sets = list(self.ch_f.keys()) + for p1, p2 in permutations(sets, 2): + if set(p1) & set(p2) != set(): pass else: - j = tuple(sorted(list(set(i) | set(k)))) - if self.char_fun[j] < self.char_fun[i] + self.char_fun[k]: + j = tuple(sorted(list(set(p1) | set(p2)))) + if self.ch_f[j] < self.ch_f[p1] + self.ch_f[p2]: return False else: pass @@ -232,7 +232,7 @@ def marginal_of_pi(self, player, pi): else: predecessors = tuple(predecessors) player_and_pred = tuple(player_and_pred) - value = self.char_fun[player_and_pred] - self.char_fun[predecessors] + value = self.ch_f[player_and_pred] - self.ch_f[predecessors] return value def get_predecessors(self, player, permutation): @@ -290,7 +290,7 @@ def show(self): Payoff vector is {'A': 2, 'C': 35, 'B': 5} """ np = self.number_players - cf = self.char_fun + cf = self.ch_f pv = self.payoff_vector print "A Co-operative Game with %s players" % np print "Characteristic Function is" @@ -320,7 +320,7 @@ def is_efficient(self): sage: letter_game.is_efficient() True """ - if sum(self.payoff_vector.values()) == self.char_fun[tuple(self.player_list)]: + if sum(self.payoff_vector.values()) == self.ch_f[tuple(self.player_list)]: return True else: return False @@ -358,14 +358,14 @@ def nullplayer(self): sage: A_game.nullplayer() True """ - sets = list(self.char_fun.keys()) - player = [(i,) for i in self.player_list if self.char_fun[(i,)] == 0] + sets = list(self.ch_f.keys()) + player = [(i,) for i in self.player_list if self.ch_f[(i,)] == 0] nulls = [] for k in player: test = [set(m) for m in sets if k in m] results = [] for j in test: - results.append(self.char_fun[tuple(j.remove(k))] == self.char_fun[tuple(j)]) + results.append(self.ch_f[tuple(j.remove(k))] == self.ch_f[tuple(j)]) if all(results): nulls.append(k) else: @@ -395,16 +395,15 @@ def symmetry(self): sage: integer_game.symmetry() False """ - sets = list(self.char_fun.keys()) + sets = list(self.ch_f.keys()) element = [i for i in sets if len(i) == 1] - other = [i for i in sets] - for j, k in combinations(element, 2): + for c1, c2 in combinations(element, 2): results = [] - for m in other: - junion = tuple(sorted(list(set(j) | set(m)))) - kunion = tuple(sorted(list(set(k) | set(m)))) - results.append(self.char_fun[junion] == self.char_fun[kunion]) - if all(results) and self.payoff_vector[j[0]] == self.payoff_vector[k[0]]: + for m in sets: + junion = tuple(sorted(list(set(c1) | set(m)))) + kunion = tuple(sorted(list(set(c2) | set(m)))) + results.append(self.ch_f[junion] == self.ch_f[kunion]) + if all(results) and self.payoff_vector[c1[0]] == self.payoff_vector[c2[0]]: pass elif all(results): return False From 56eac8dc58592d1a0fe94546875ace41bd4142c5 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 10:29:15 +0100 Subject: [PATCH 155/546] removes useless is_additive function --- src/sage/game_theory/cooperative_game.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 871ab7235a3..5dcf1dbd458 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -410,8 +410,3 @@ def symmetry(self): else: pass return True - - def is_additivity(self): - r""" - Returns True if the current payoff_vector possesses the additivity property. - """ From 0d24290a8cc627d5323c0f05057db0a806599494 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 10:54:24 +0100 Subject: [PATCH 156/546] raises error if key is not a tuple --- src/sage/game_theory/cooperative_game.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 5dcf1dbd458..e351f4ed5ca 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -114,6 +114,12 @@ def __init__(self, characteristic_function, payoff_vector=False): self.number_players = len(self.player_list) self.payoff_vector = payoff_vector + for key in list(self.ch_f.keys()): + if type(key) is not tuple: + raise TypeError("Sey must be a tuple") + + + def shapley_value(self): r""" Return the payoff vector for co-operative game. From 4611440152fff8fe69a6d04f80650cb5d6b9c874 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 11:15:48 +0100 Subject: [PATCH 157/546] creates a check for characteristic function being a dictionary --- src/sage/game_theory/cooperative_game.py | 41 ++++++++++++++++++++---- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index e351f4ed5ca..d8dc680861d 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -109,17 +109,46 @@ class CooperativeGame(SageObject): """ def __init__(self, characteristic_function, payoff_vector=False): + r""" + Initializes a co-operative game and checks the inputs. + + TESTS: + + An attempt to construct a game from an integer. :: + + sage: int_game = CooperativeGame(4) + Traceback (most recent call last): + ... + TypeError: Characteristic function must be a dictionary + + This test checks that an error is raised when a key in the + Characteristic Function is not a tuple. :: + + sage: tuple_function = {(): 0, + ....: (1): 6, + ....: (2,): 12, + ....: (3,): 42, + ....: (1, 2,): 12, + ....: (1, 3,): 42, + ....: (2, 3,): 42, + ....: (1, 2, 3,): 42} + sage: tuple_game = CooperativeGame(tuple_function) + Traceback (most recent call last): + ... + TypeError: Key must be a tuple + """ + if type(characteristic_function) is not dict: + raise TypeError("Characteristic function must be a dictionary") + + for key in list(characteristic_function.keys()): + if type(key) is not tuple: + raise TypeError("Key must be a tuple") + self.ch_f = characteristic_function self.player_list = list(characteristic_function.keys()[-1]) self.number_players = len(self.player_list) self.payoff_vector = payoff_vector - for key in list(self.ch_f.keys()): - if type(key) is not tuple: - raise TypeError("Sey must be a tuple") - - - def shapley_value(self): r""" Return the payoff vector for co-operative game. From ffa33e8366bfdc142f053674815c278edd3eacda Mon Sep 17 00:00:00 2001 From: drvinceknight Date: Wed, 4 Jun 2014 11:48:19 +0100 Subject: [PATCH 158/546] Tweaks to docs and made monotone test a one liner --- src/sage/game_theory/cooperative_game.py | 26 ++++++++++++++++-------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index d8dc680861d..98723d8030c 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -79,7 +79,7 @@ class CooperativeGame(SageObject): sage: letter_game.is_superadditive() False - The show function will give us basic information about the game. :: + The show function will display basic information about the game. :: sage: letter_game.show() A Co-operative Game with 3 players @@ -94,10 +94,10 @@ class CooperativeGame(SageObject): ('A', 'B', 'C') : 42 Payoff vector is {'A': 2, 'C': 35, 'B': 5} - Finally, we can test 3 basic properties of the Payoff Vector. They are + We can test 3 basic properties of the Payoff Vector. They are * Is it is efficient? * Does it possess the nullplayer property? - * Does it possesss the symmetry property? + * Does it possess the symmetry property? :: sage: letter_game.is_efficient() @@ -106,6 +106,16 @@ class CooperativeGame(SageObject): True sage: letter_game.symmetry() True + + Any Payoff Vector can be passed to the game and these properties can once again be tested: + + sage: letter_game.payoff_vector = {'A': 0, 'C': 35, 'B': 3} + sage: letter_game.is_efficient() + False + sage: letter_game.nullplayer() + True + sage: letter_game.symmetry() + True """ def __init__(self, characteristic_function, payoff_vector=False): @@ -136,6 +146,8 @@ def __init__(self, characteristic_function, payoff_vector=False): Traceback (most recent call last): ... TypeError: Key must be a tuple + + The above test failed as `(1)` is not read as a tuple. """ if type(characteristic_function) is not dict: raise TypeError("Characteristic function must be a dictionary") @@ -202,12 +214,8 @@ def is_monotone(self): True """ sets = list(self.ch_f.keys()) - for p1, p2 in permutations(sets, 2): - if set(p1) <= set(p2) and self.ch_f[p1] > self.ch_f[p2]: - return False - else: - pass - return True + return not any([set(p1) <= set(p2) and self.ch_f[p1] > self.ch_f[p2] + for p1, p2 in permutations(sets, 2)]) def is_superadditive(self): r""" From e4951f8856f8669f188a0afb69426ce8a927527f Mon Sep 17 00:00:00 2001 From: drvinceknight Date: Wed, 4 Jun 2014 12:04:32 +0100 Subject: [PATCH 159/546] Changed how player list is obtained --- src/sage/game_theory/cooperative_game.py | 56 +++++++++++++++++------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 98723d8030c..1ca1c8c081f 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -157,7 +157,8 @@ def __init__(self, characteristic_function, payoff_vector=False): raise TypeError("Key must be a tuple") self.ch_f = characteristic_function - self.player_list = list(characteristic_function.keys()[-1]) + #self.player_list = list(characteristic_function.keys()[-1]) + self.player_list = list(max(characteristic_function.keys(), key = lambda key : len(key))) self.number_players = len(self.player_list) self.payoff_vector = payoff_vector @@ -199,7 +200,7 @@ def is_monotone(self): Returns True if co-operative game is monotonic. EXAMPLES:: - Shows the use of is_monotone on a simple game. :: + Shows the use of is_monotone on a simple game that is monotone. :: sage: integer_function = {(): 0, ....: (1,): 6, @@ -212,6 +213,20 @@ def is_monotone(self): sage: integer_game = CooperativeGame(integer_function) sage: integer_game.is_monotone() True + + Shows the use of is_monotone for a game that is not monotone. :: + + sage: integer_function = {(): 0, + ....: (1,): 6, + ....: (2,): 12, + ....: (3,): 42, + ....: (1, 2,): 10, + ....: (1, 3,): 42, + ....: (2, 3,): 42, + ....: (1, 2, 3,): 42} + sage: integer_game = CooperativeGame(integer_function) + sage: integer_game.is_monotone() + False """ sets = list(self.ch_f.keys()) return not any([set(p1) <= set(p2) and self.ch_f[p1] > self.ch_f[p2] @@ -252,20 +267,16 @@ def is_superadditive(self): """ sets = list(self.ch_f.keys()) for p1, p2 in permutations(sets, 2): - if set(p1) & set(p2) != set(): - pass - else: - j = tuple(sorted(list(set(p1) | set(p2)))) - if self.ch_f[j] < self.ch_f[p1] + self.ch_f[p2]: + if set(p1) & set(p2) == set(): + union = tuple(sorted(list(set(p1) | set(p2)))) + if self.ch_f[union] < self.ch_f[p1] + self.ch_f[p2]: return False - else: - pass return True def marginal_contributions(self, player): contributions = [] - for i in permutations(self.player_list): - contributions.append(self.marginal_of_pi(player, i)) + for pi in permutations(self.player_list): + contributions.append(self.marginal_of_pi(player, pi)) return contributions def marginal_of_pi(self, player, pi): @@ -294,7 +305,7 @@ def get_predecessors(self, player, permutation): def show(self): r""" EXAMPLES:: - Typical use of the show function.:: + Typical use of the show function with a given Payoff Vector.:: sage: letter_function = {(): 0, ....: ('A',): 6, @@ -317,6 +328,9 @@ def show(self): ('A', 'C') : 42 ('A', 'B', 'C') : 42 Payoff vector is {'A': 14, 'C': 14, 'B': 14} + + Typical use of the show function with after calculating the Shapley value.:: + sage: letter_game.shapley_value() {'A': 2, 'C': 35, 'B': 5} sage: letter_game.show() @@ -362,11 +376,21 @@ def is_efficient(self): sage: letter_game = CooperativeGame(letter_function, {'A': 14, 'B': 14, 'C': 14}) sage: letter_game.is_efficient() True + + sage: letter_function = {(): 0, + ....: ('A',): 6, + ....: ('B',): 12, + ....: ('C',): 42, + ....: ('A', 'B',): 12, + ....: ('A', 'C',): 42, + ....: ('B', 'C',): 42, + ....: ('A', 'B', 'C',): 42} + sage: letter_game = CooperativeGame(letter_function) + sage: letter_game.payoff_vector = {'A': 10, 'B': 14, 'C': 14} + sage: letter_game.is_efficient() + False """ - if sum(self.payoff_vector.values()) == self.ch_f[tuple(self.player_list)]: - return True - else: - return False + return sum(self.payoff_vector.values()) == self.ch_f[tuple(self.player_list)] def nullplayer(self): r""" From a5700b4d571f02297d05525725a0315686f50ac4 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 12:23:51 +0100 Subject: [PATCH 160/546] player_list is now a tuple --- src/sage/game_theory/cooperative_game.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 1ca1c8c081f..0cce9292fe2 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -158,7 +158,7 @@ def __init__(self, characteristic_function, payoff_vector=False): self.ch_f = characteristic_function #self.player_list = list(characteristic_function.keys()[-1]) - self.player_list = list(max(characteristic_function.keys(), key = lambda key : len(key))) + self.player_list = max(characteristic_function.keys(), key = lambda key : len(key)) self.number_players = len(self.player_list) self.payoff_vector = payoff_vector @@ -179,7 +179,7 @@ def shapley_value(self): ....: (1, 2, 3,): 42} sage: integer_game = CooperativeGame(integer_function) sage: integer_game.player_list - [1, 2, 3] + (1, 2, 3) sage: integer_game.payoff_vector False sage: integer_game.shapley_value() @@ -390,7 +390,7 @@ def is_efficient(self): sage: letter_game.is_efficient() False """ - return sum(self.payoff_vector.values()) == self.ch_f[tuple(self.player_list)] + return sum(self.payoff_vector.values()) == self.ch_f[self.player_list] def nullplayer(self): r""" From a9b58710b0d9d117cd9168d798e34c1fd81262db Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 12:56:33 +0100 Subject: [PATCH 161/546] improves nullplayer --- src/sage/game_theory/cooperative_game.py | 27 +++++++++++------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 0cce9292fe2..56598c8e994 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -414,7 +414,7 @@ def nullplayer(self): A payoff_vector that returns False. :: sage: A_function = {(): 0, - ....: (1,): 6, + ....: (1,): 0, ....: (2,): 12, ....: (3,): 42, ....: (1, 2,): 12, @@ -423,25 +423,22 @@ def nullplayer(self): ....: (1, 2, 3,): 55} sage: A_game = CooperativeGame(A_function, {1: 10, 2: 10, 3: 25}) sage: A_game.nullplayer() - True + False """ - sets = list(self.ch_f.keys()) - player = [(i,) for i in self.player_list if self.ch_f[(i,)] == 0] + allsets = list(self.ch_f.keys()) + players = [(i,) for i in self.player_list if self.ch_f[(i,)] == 0] nulls = [] - for k in player: - test = [set(m) for m in sets if k in m] + for player in players: + test = [set(m) for m in allsets if player in m] results = [] - for j in test: - results.append(self.ch_f[tuple(j.remove(k))] == self.ch_f[tuple(j)]) + for sets in test: + results.append(self.ch_f[tuple(sets.remove(player))] == self.ch_f[tuple(sets)]) if all(results): - nulls.append(k) - else: - pass - for i in nulls: - if self.payoff_vector[i[0]] != 0: + nulls.append(player) + + for player in nulls: + if self.payoff_vector[player[0]] != 0: return False - else: - pass return True def symmetry(self): From 54b5ce6e73635a7b2cfd5f7a5ef265031ac6c51f Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 13:16:07 +0100 Subject: [PATCH 162/546] creates test for is_efficient --- src/sage/game_theory/cooperative_game.py | 36 ++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 56598c8e994..bb4afed26e7 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -389,7 +389,28 @@ def is_efficient(self): sage: letter_game.payoff_vector = {'A': 10, 'B': 14, 'C': 14} sage: letter_game.is_efficient() False + + TESTS:: + Checks that a game has a payoff_vector. :: + + sage: A_function = {(): 0, + ....: (1,): 0, + ....: (2,): 12, + ....: (3,): 42, + ....: (1, 2,): 12, + ....: (1, 3,): 42, + ....: (2, 3,): 55, + ....: (1, 2, 3,): 55} + sage: A_game = CooperativeGame(A_function) + sage: A_game.is_efficient() + Traceback (most recent call last): + ... + ValueError: Game must have a payoff_vector + """ + if not self.payoff_vector: + raise ValueError("Game must have a payoff_vector") + return sum(self.payoff_vector.values()) == self.ch_f[self.player_list] def nullplayer(self): @@ -446,6 +467,21 @@ def symmetry(self): Returns True if the current payoff_vector possesses the symmetry property. EXAMPLES:: + A Payoff Vector that returns True. :: + + sage: letter_function = {(): 0, + ....: ('A',): 6, + ....: ('B',): 12, + ....: ('C',): 42, + ....: ('A', 'B',): 12, + ....: ('A', 'C',): 42, + ....: ('B', 'C',): 42, + ....: ('A', 'B', 'C',): 42} + sage: letter_game = CooperativeGame(letter_function, {'A': 5, 'B': 14, 'C': 20}) + sage: letter_game.symmetry() + True + + A Payoff Vector that returns False. :: sage: integer_function = {(): 0, ....: (1,): 12, From f2026a79dfa45c8fe5ffb5711ce11ac9d1be3af8 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 13:19:23 +0100 Subject: [PATCH 163/546] adds tests to symmetry and null player to check for payoff_vector --- src/sage/game_theory/cooperative_game.py | 41 +++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index bb4afed26e7..cfadc38741c 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -406,7 +406,6 @@ def is_efficient(self): Traceback (most recent call last): ... ValueError: Game must have a payoff_vector - """ if not self.payoff_vector: raise ValueError("Game must have a payoff_vector") @@ -445,7 +444,27 @@ def nullplayer(self): sage: A_game = CooperativeGame(A_function, {1: 10, 2: 10, 3: 25}) sage: A_game.nullplayer() False + + TESTS:: + Checks that a game has a payoff_vector. :: + + sage: A_function = {(): 0, + ....: (1,): 0, + ....: (2,): 12, + ....: (3,): 42, + ....: (1, 2,): 12, + ....: (1, 3,): 42, + ....: (2, 3,): 55, + ....: (1, 2, 3,): 55} + sage: A_game = CooperativeGame(A_function) + sage: A_game.nullplayer() + Traceback (most recent call last): + ... + ValueError: Game must have a payoff_vector """ + if not self.payoff_vector: + raise ValueError("Game must have a payoff_vector") + allsets = list(self.ch_f.keys()) players = [(i,) for i in self.player_list if self.ch_f[(i,)] == 0] nulls = [] @@ -494,7 +513,27 @@ def symmetry(self): sage: integer_game = CooperativeGame(integer_function, {1: 2, 2: 5, 3: 35}) sage: integer_game.symmetry() False + + TESTS:: + Checks that a game has a payoff_vector. :: + + sage: A_function = {(): 0, + ....: (1,): 0, + ....: (2,): 12, + ....: (3,): 42, + ....: (1, 2,): 12, + ....: (1, 3,): 42, + ....: (2, 3,): 55, + ....: (1, 2, 3,): 55} + sage: A_game = CooperativeGame(A_function) + sage: A_game.is_efficient() + Traceback (most recent call last): + ... + ValueError: Game must have a payoff_vector """ + if not self.payoff_vector: + raise ValueError("Game must have a payoff_vector") + sets = list(self.ch_f.keys()) element = [i for i in sets if len(i) == 1] for c1, c2 in combinations(element, 2): From b36e38c5605ab31e1940da0df5ceec642b9235ad Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 13:35:28 +0100 Subject: [PATCH 164/546] Revert "Fixed readme" This reverts commit cdf19a22d9b9fa18f00736a1b6e56a91755a480f. --- README.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.txt b/README.txt index 247230672df..4b78bc41e50 100644 --- a/README.txt +++ b/README.txt @@ -17,13 +17,13 @@ In many cases, documentation for modules and functions list the authors. -**This is the working repository for game theoretical development in Sage. For the main Sage repository see https://github.com/sagemath/sage. ** +**This is the working repository for game theoretical development in Sage. For the main Sage repository see [https://github.com/sagemath/sage](https://github.com/sagemath/sage).** The current open tickets for game theory can be found (which correspond to branches in this repository) below: -- 16331: Build capacity to solve matching games in to Sage: http://trac.sagemath.org/ticket/16331 -- 16332: Build capacity to calculate Shapley value of cooperative games: http://trac.sagemath.org/ticket/16332 -- 16333: Build class for normal form games as well as ability to obtain Nash equilibria: http://trac.sagemath.org/ticket/16333 +- [16331: Build capacity to solve matching games in to Sage.](http://trac.sagemath.org/ticket/16331) +- [16332: Build capacity to calculate Shapley value of cooperative games.](http://trac.sagemath.org/ticket/16332) +- [16333: Build class for normal form games as well as ability to obtain Nash equilibria](http://trac.sagemath.org/ticket/16333) If you would like to contribute, please see the discussions at the relevant tickets above and/or fork this repository. From 9b9ebd49ca8d7e0a835ac08fbe20213a78842c77 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 13:35:55 +0100 Subject: [PATCH 165/546] Revert "Updated readme to indicate that this is a development repo" This reverts commit a158c7b51ae75764831290b1d016c88ec5c44869. --- README.txt | 380 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 371 insertions(+), 9 deletions(-) diff --git a/README.txt b/README.txt index 4b78bc41e50..0463ff52866 100644 --- a/README.txt +++ b/README.txt @@ -17,17 +17,379 @@ In many cases, documentation for modules and functions list the authors. -**This is the working repository for game theoretical development in Sage. For the main Sage repository see [https://github.com/sagemath/sage](https://github.com/sagemath/sage).** +GETTING STARTED +--------------- -The current open tickets for game theory can be found (which correspond to branches in this repository) below: +This README.txt contains build instructions for Sage. If you downloaded +a binary, you do not need to do anything; just execute: -- [16331: Build capacity to solve matching games in to Sage.](http://trac.sagemath.org/ticket/16331) -- [16332: Build capacity to calculate Shapley value of cooperative games.](http://trac.sagemath.org/ticket/16332) -- [16333: Build class for normal form games as well as ability to obtain Nash equilibria](http://trac.sagemath.org/ticket/16333) + ./sage -If you would like to contribute, please see the discussions at the relevant tickets above and/or fork this repository. +from the command line. If you downloaded the sources, please read below +on how to build Sage and work around common issues. -Current contributors: +If you have questions or encounter problems, please do not hesitate +to email the sage-support mailing list: -- James Campbell (Cardiff) -- Vincent Knight (Cardiff) + http://groups.google.com/group/sage-support + + +SUPPORTED PLATFORMS +------------------- + +Sage fully supports several Linux distributions, recent versions of +Mac OS X, Windows (using virtualization), as well as a number of +Solaris and OpenSolaris releases. + +There should be no serious bugs in an officially released version of +Sage on any of the fully supported platforms, but any major issues with +a particular release will be documented on an errata page: + + http://wiki.sagemath.org/errata + +Ports are in progress to some other, less common platforms. The list of +supported platforms and their current statuses are given at the +following web page: + + http://wiki.sagemath.org/SupportedPlatforms + +If you are interested in helping port Sage to a new platform, please let +us know at the sage-devel mailing list: + + http://groups.google.com/group/sage-devel + + +QUICK INSTRUCTIONS TO BUILD FROM SOURCE +--------------------------------------- + +The following steps briefly outline the process of building Sage from +source. More detailed instructions, including how to build faster on +multicore machines are contained later in this README and in the +Installation Guide: + + http://www.sagemath.org/doc/installation + +1. Make sure you have the dependencies and 5 GB of free disk space. + + All Linux versions: gcc, make, m4, perl, ranlib, and tar. + Debian or Ubuntu systems: the dpkg-dev package. + Fedora or RedHat systems: the perl-ExtUtils-MakeMaker package. + (install these using your package manager) + + OS X: Xcode. Make sure you have installed the most recent version + of Xcode. With recent versions of OS X (OS X Lion or later), you + can install Xcode for free from the App Store. For pre-Lion + versions of OS X, you can download Xcode from + http://developer.apple.com/downloads/. + + With OS X, you also need to install the "command line tools". When + using OS X Mavericks, after installing Xcode, run this command from + a terminal window: + + xcode-select --install + + Then click "Install" in the pop-up window. + + When using OS X Mountain Lion or earlier, you need to install the + command line tools from Xcode: run Xcode; then from the File + menu, choose "Preferences", then the "Downloads" tab, and then + "Install" the Command Line Tools. + + Other platforms: See detailed instructions below. + +2. Extract the tarball: + + tar xvf sage-*.tar + +3. cd into the Sage directory and type make: + + cd sage-*/ + make + + That's it! Everything is automatic and non-interactive. The build + should work fine on all fully supported platforms. If it does not, we + want to know! + + +ENVIRONMENT VARIABLES +--------------------- + +There are a lot of environment variables which control the install +process of Sage, see: + + http://sagemath.org/doc/installation/source.html#environment-variables + + +IMPLEMENTATION +-------------- + +Sage has significant components written in the following languages: +C/C++, Python, Cython, Lisp, and Fortran. Lisp (ECL), Python, and Cython +are built as part of Sage and a GNU Fortran (gfortran) binary is +included (OS X only), so you do not need them in order to build Sage. + + +MORE DETAILED INSTRUCTIONS TO BUILD FROM SOURCE +----------------------------------------------- + +1. Make sure you have about 5 GB of free disk space. + +2. Install build dependencies. + + Linux: See quick instructions above. + + OS X: Make sure you have XCode version >= 2.4, i.e. "gcc -v" should + output build >= 5363. If you don't, go to: + + http://developer.apple.com/ + + sign up, and download the free XCode package. Only OS X >= 10.4 is + supported. + + Solaris and OpenSolaris: Building Sage on these platforms is more + tricky than on Linux or OS X. For details on how to build Sage on + these platforms, see: + + http://wiki.sagemath.org/solaris + + Windows: Download and install VirtualBox, and then download the + Sage virtual appliance. For details, see: + + http://wiki.sagemath.org/SageAppliance + + NOTE: On some operating systems, it might be necessary to install + gas/as, gld/ld, gnm/nm. On most platforms, these are automatically + installed when you install the programs listed above. + +3. Extract the Sage source tarball and cd into a directory with no + spaces in it. If you have a machine with 4 processors, say, type + the following to configure the build script to perform a parallel + compilation of Sage using 4 jobs: + + export MAKE="make -j4" + + (With 4 processors, you might also consider "-j5" or "-j6" -- + building with more jobs than CPU cores can speed things up.) + You might in addition pass a "-l" flag to "make": this + sets a load limit, so for example if you execute + + export MAKE="make -j4 -l5.5" + + then "make" won't start more than one job at a time if the system + load average is above 5.5. See + http://www.gnu.org/software/make/manual/make.html#Options-Summary + and http://www.gnu.org/software/make/manual/make.html#Parallel. + + If you want to run the test suite for each individual spkg as it is + installed, type: + + export SAGE_CHECK="yes" + + before starting the Sage build. This will run each test suite and + will raise an error if any failures occur. Python's test suite has + been disabled by default, because it causes failures on most + systems. To renable the Python testsuite, set the environment + variable SAGE_CHECK_PACKAGES to "python". + + To start the build, type: + + make + +4. Wait about 20 minutes to 14 days, depending on your computer (it took + about 2 weeks to build Sage on the T-Mobile G1 Android cell phone). + +5. Type "./sage" to try it out. + +6. OPTIONAL: Type "make ptest" to test all examples in the documentation + (over 200,000 lines of input!) -- this takes from 10 minutes to + several hours. Don't get too disturbed if there are 2 to 3 failures, + but always feel free to email the section of logs/ptest.log that + contains errors to the sage-support mailing list. If there are + numerous failures, there was a serious problem with your build. + +7. The HTML version of the documentation is built + during the compilation process of Sage and resides in the directory: + + $SAGE_ROOT/src/doc/output/html/ + + OPTIONAL: If you want to build the PDF version (requires LaTeX) of + the documentation, run: + + make doc-pdf + +8. OPTIONAL: It is highly recommended that you install the optional GAP + database by typing: + + ./sage --optional + + then installing (with "./sage -i") the package whose name begins with + database_gap. This will download the package from + sage.math.washington.edu and install it. While you're at it, you + might install other databases of interest to you. + +9. OPTIONAL: It is recommended that you have both LaTeX and the + ImageMagick tools (e.g. the "convert" command) installed since some + plotting functionality benefits from it. + +10. OPTIONAL: Read this if you are intending to run a Sage notebook + server for multiple users. For security (i.e., to run + "notebook(secure=True)") you want to access the server using the + HTTPS protocol. First, install OpenSSL and the OpenSSL development + headers on your system if they are not already installed. Then + install pyOpenSSL by building Sage and then typing + + ./sage -i pyopenssl + + Note that this command requires internet access. Alternatively, + "make ssl" builds Sage and installs pyOpenSSL. + + +PROBLEMS +-------- + +If you have problems building Sage, check the Sage Installation Guide, +and also note the following. Each separate component of Sage is +contained in an spkg; these are stored in spkg/standard/. As each one +is built, a build log is stored in logs/pkgs/, so you can browse these +to find error messages. If an spkg fails to build, the whole build +process will stop soon after, so check the most recent log files +first, or run + + grep -li "^Error" logs/pkgs/* + +from the top-level Sage directory to find log files with error +messages in them. Send (a small part of) the relevant log file to the +sage-devel mailing list, making sure to include at least some of the +error messages; probably someone there will have some helpful +suggestions. + + +SUPPORTED COMPILERS +------------------- + +Sage includes a GCC (GNU Compiler Collection) package. In order to +build Sage, you need a C compiler which can build GCC and its +prerequisites. gcc version 4.0.1 or later should probably work. On +Solaris or OpenSolaris, building with the Sun compiler should also work. + +The GCC package in Sage is not always installed. It is determined +automatically whether it needs to be installed. You can override this +by setting the environment variable SAGE_INSTALL_GCC=yes (to force +installation of GCC) or SAGE_INSTALL_GCC=no (to disable installation of +GCC). If you don't want to install GCC, you need to have recent +versions of gcc, g++ and gfortran; moreover, the versions must be equal. + +There are some known problems with old assemblers, in particular when +building the ECM package. You should ensure that your assembler +understands all instructions for your processor. On Linux, this means +you need a recent version of binutils; on OS X you need a recent version +of XCode. + + +DIRECTORY LAYOUT +---------------- + +Simplified directory layout (only essential files/directories): + +SAGE_ROOT Root directory (sage-x.y.z in the Sage tarball) ++-- build +| +-- deps Dependency information of packages +| +-- pkgs Every package is a subdirectory here +| +-- atlas +| ... +| +-- zn_poly ++-- COPYING.txt Copyright information ++-- local Compiled packages are installed here +| +-- bin Executables +| +-- include C/C++ headers +| +-- lib Shared libraries +| +-- share Databases, architecture-independent data +| +-- var +| +-- sage List of installed packages +| +-- tmp Temporary files when building Sage ++-- logs +| +-- dochtml.log Log of the documentation build +| +-- install.log Full install log +| +-- pkgs Build logs of individual packages +| +--- atlas-3.10.1.p7.log +| ... +| +--- zn_poly-0.9.p11.log ++-- Makefile Running "make" uses this file ++-- README.txt This file ++-- sage Script to start Sage ++-- src All of the Sage source (not third-party packages) +| +-- bin Scripts that Sage uses internally +| +-- doc Sage documentation +| +-- sage The Sage library source code ++-- upstream Source tarballs of packages +| +-- atlas-3.10.1.tar.bz2 +| ... +| +-- zn_poly-0.9.tar.bz2 ++-- VERSION.txt + +For more details, see: + + http://sagemath.org/doc/developer/coding_basics.html#files-and-directory-structure + + +RELOCATION +---------- + +You *should* be able to move the sage-x.y.z/ directory anywhere you +want. If you copy the sage script or make a symbolic link to it, you +should modify the script to reflect this (as instructed at the top of +the script). It is best if the path to Sage does not have any spaces in +it. + +For a system-wide installation, as root you can move the sage-x.y.z/ +directory to a system-wide directory. Afterwards, you need to start up +Sage as root at least once prior to using the system-wide Sage as a +normal user. See the Installation Guide for further information on +performing a system-wide installation: + + http://www.sagemath.org/doc/installation/source.html#installation-in-a-multiuser-environment + +If you find anything that doesn't work correctly after you moved the +directory, please email the sage-support mailing list. + + +REDISTRIBUTION +-------------- + +Your local Sage install is almost exactly the same as any "developer" +install. You can make changes to documentation, source, etc., and very +easily package the complete results up for redistribution just like we +do. + +1. To make your own source tarball of Sage, type: + + sage --sdist + +2. To make a binary distribution with your currently installed packages, + type: + + sage --bdist + +3. To make a binary that will run on the widest range of target + machines, set the SAGE_FAT_BINARY environment variable to "yes" + before building Sage: + + export SAGE_FAT_BINARY="yes" + make distclean && make + ./sage --bdist + +In all cases, the result is placed in the directory "$SAGE_ROOT/dist/". + + +CHANGES TO INCLUDED SOFTWARE +---------------------------- + +All software included with Sage is copyrighted by the respective authors +and released under an open source license that is "GPL version 3 or +later" compatible. See the file COPYING.txt for more details. + +Sources are in unmodified (as far as possible) tarballs in the +"$SAGE_ROOT/upstream" directory. The remaining description, version +information, patches, and build scripts are in the accompanying +"$SAGE_ROOT/build/pkgs/" directory. This directory is +part of the Sage git repository. From e696ecf08f00b291a5229b3bf2885fdbd9543ea8 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 13:36:53 +0100 Subject: [PATCH 166/546] deletes commented out line --- src/sage/game_theory/cooperative_game.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index cfadc38741c..8b6e2edd479 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -157,7 +157,6 @@ def __init__(self, characteristic_function, payoff_vector=False): raise TypeError("Key must be a tuple") self.ch_f = characteristic_function - #self.player_list = list(characteristic_function.keys()[-1]) self.player_list = max(characteristic_function.keys(), key = lambda key : len(key)) self.number_players = len(self.player_list) self.payoff_vector = payoff_vector From db58a84f81f1af3382c6baacbce6614583c4d468 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 15:05:30 +0100 Subject: [PATCH 167/546] creates function to test for power set --- src/sage/game_theory/cooperative_game.py | 78 +++++++++++++++++++++++- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 8b6e2edd479..d89f48914b5 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -22,7 +22,7 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from itertools import permutations, combinations +from itertools import chain, permutations, combinations from sage.structure.sage_object import SageObject @@ -156,8 +156,15 @@ def __init__(self, characteristic_function, payoff_vector=False): if type(key) is not tuple: raise TypeError("Key must be a tuple") + players = set(max(characteristic_function.keys(), key=lambda key: len(key))) + for com in chain.from_iterable(combinations(players, r) for r in range(len(players)+1)): + if z in characteristic_function.keys(): + pass + else: + raise ValueError("Characteristic Function must be the power set") + self.ch_f = characteristic_function - self.player_list = max(characteristic_function.keys(), key = lambda key : len(key)) + self.player_list = max(characteristic_function.keys(), key=lambda key: len(key)) self.number_players = len(self.player_list) self.payoff_vector = payoff_vector @@ -273,12 +280,56 @@ def is_superadditive(self): return True def marginal_contributions(self, player): + r""" + Returns a list of contributions specific to one player. + + INPUT: + + -player - A real number or string. + + EXAMPLES:: + + sage: integer_function = {(): 0, + ....: (1,): 6, + ....: (2,): 12, + ....: (3,): 42, + ....: (1, 2,): 12, + ....: (1, 3,): 42, + ....: (2, 3,): 42, + ....: (1, 2, 3,): 42} + sage: integer_game = CooperativeGame(integer_function) + sage: integer_game.marginal_contributions(1) + + """ contributions = [] for pi in permutations(self.player_list): contributions.append(self.marginal_of_pi(player, pi)) return contributions def marginal_of_pi(self, player, pi): + r""" + Returns a value for the players contribution in one permutation. + + INPUT: + + -player - A real number or string. + + -pi - A tuple which is the permutation that should be used. + + EXAMPLES:: + + sage: integer_function = {(): 0, + ....: (1,): 6, + ....: (2,): 12, + ....: (3,): 42, + ....: (1, 2,): 12, + ....: (1, 3,): 42, + ....: (2, 3,): 42, + ....: (1, 2, 3,): 42} + sage: integer_game = CooperativeGame(integer_function) + sage: integer_game.marginal_of_pi(2, (2, 3, 1)) + + """ predecessors, player_and_pred = self.get_predecessors(player, pi) if predecessors is None: predecessors = () @@ -289,6 +340,29 @@ def marginal_of_pi(self, player, pi): return value def get_predecessors(self, player, permutation): + r""" + Returns a list of all the predecessors of a player in a certain permutation. + + INPUT: + + -player - A real number or string. + + -permutation - A tuple which is the permutation that should be used. + + EXAMPLES:: + + sage: integer_function = {(): 0, + ....: (1,): 6, + ....: (2,): 12, + ....: (3,): 42, + ....: (1, 2,): 12, + ....: (1, 3,): 42, + ....: (2, 3,): 42, + ....: (1, 2, 3,): 42} + sage: integer_game = CooperativeGame(integer_function) + sage: integer_game.get_predecessors(1, (2, 3, 1)) + [2, 3] + """ pred = [] play_and_pred = [] for k in permutation: From 2b204ca4d9664ca42f0001cbf6d3fc08dd47aa6f Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 15:06:03 +0100 Subject: [PATCH 168/546] adds a longer function and game --- src/sage/game_theory/cooperative_game.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index d89f48914b5..521abb19ba3 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -622,3 +622,25 @@ def symmetry(self): else: pass return True + +r""" +sage: long_function = {(): 0, +....: (1,): 0, +....: (2,): 0, +....: (3,): 0, +....: (4,): 0, +....: (1, 2): 0, +....: (1, 3): 0, +....: (1, 4): 0, +....: (2, 3): 0, +....: (2, 4): 0, +....: (3, 4): 0, +....: (1, 2, 3): 0, +....: (1, 2, 4): 45, +....: (1, 3, 4): 40, +....: (2, 3, 4): 0, +....: (1, 2, 3, 4): 65} +sage: long_game = CooperativeGame(long_function) +sage: long_game.shapley_value() +{1: 20, 2: 20, 3: 5, 4: 20} +""" From b9328a904b8a30f65c4c3408fa484f6b75437c69 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 15:34:09 +0100 Subject: [PATCH 169/546] starts adding in longer examples --- src/sage/game_theory/cooperative_game.py | 92 +++++++++++++++++------- 1 file changed, 68 insertions(+), 24 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 521abb19ba3..0ed56021e89 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -34,8 +34,8 @@ class CooperativeGame(SageObject): INPUT: - characteristic_function - a dictionary containing all possible sets of players. - * Key - Each set must be entered as a tuple, not a string. Apart from - the empty set, they must all end with a comma. + * Key - Each set must be entered as a tuple, not a string. A single element + tuple must end with a comma. * Value - A real number representing each set of players contribution. - payoff_vector - default = ``False``, a dictionary can be passed instead but @@ -192,6 +192,28 @@ def shapley_value(self): {1: 2, 2: 5, 3: 35} sage: integer_game.payoff_vector {1: 2, 2: 5, 3: 35} + + A longer example of the shapley_value. :: + + sage: long_function = {(): 0, + ....: (1,): 0, + ....: (2,): 0, + ....: (3,): 0, + ....: (4,): 0, + ....: (1, 2): 0, + ....: (1, 3): 0, + ....: (1, 4): 0, + ....: (2, 3): 0, + ....: (2, 4): 0, + ....: (3, 4): 0, + ....: (1, 2, 3): 0, + ....: (1, 2, 4): 45, + ....: (1, 3, 4): 40, + ....: (2, 3, 4): 0, + ....: (1, 2, 3, 4): 65} + sage: long_game = CooperativeGame(long_function) + sage: long_game.shapley_value() + {1: 20, 2: 20, 3: 5, 4: 20} """ payoff_vector = {} for player in self.player_list: @@ -233,6 +255,28 @@ def is_monotone(self): sage: integer_game = CooperativeGame(integer_function) sage: integer_game.is_monotone() False + + Shows the use of is_monotone for a longer game. :: + + sage: long_function = {(): 0, + ....: (1,): 0, + ....: (2,): 0, + ....: (3,): 0, + ....: (4,): 0, + ....: (1, 2): 0, + ....: (1, 3): 0, + ....: (1, 4): 0, + ....: (2, 3): 0, + ....: (2, 4): 0, + ....: (3, 4): 0, + ....: (1, 2, 3): 0, + ....: (1, 2, 4): 45, + ....: (1, 3, 4): 40, + ....: (2, 3, 4): 0, + ....: (1, 2, 3, 4): 65} + sage: long_game = CooperativeGame(long_function) + sage: long_game.is_monotone() + True """ sets = list(self.ch_f.keys()) return not any([set(p1) <= set(p2) and self.ch_f[p1] > self.ch_f[p2] @@ -270,6 +314,28 @@ def is_superadditive(self): sage: A_game = CooperativeGame(A_function) sage: A_game.is_superadditive() True + + An example for is_superadditive with a longer game. :: + + sage: long_function = {(): 0, + ....: (1,): 0, + ....: (2,): 0, + ....: (3,): 0, + ....: (4,): 0, + ....: (1, 2): 0, + ....: (1, 3): 0, + ....: (1, 4): 0, + ....: (2, 3): 0, + ....: (2, 4): 0, + ....: (3, 4): 0, + ....: (1, 2, 3): 0, + ....: (1, 2, 4): 45, + ....: (1, 3, 4): 40, + ....: (2, 3, 4): 0, + ....: (1, 2, 3, 4): 65} + sage: long_game = CooperativeGame(long_function) + sage: long_game.is_superadditive() + False """ sets = list(self.ch_f.keys()) for p1, p2 in permutations(sets, 2): @@ -622,25 +688,3 @@ def symmetry(self): else: pass return True - -r""" -sage: long_function = {(): 0, -....: (1,): 0, -....: (2,): 0, -....: (3,): 0, -....: (4,): 0, -....: (1, 2): 0, -....: (1, 3): 0, -....: (1, 4): 0, -....: (2, 3): 0, -....: (2, 4): 0, -....: (3, 4): 0, -....: (1, 2, 3): 0, -....: (1, 2, 4): 45, -....: (1, 3, 4): 40, -....: (2, 3, 4): 0, -....: (1, 2, 3, 4): 65} -sage: long_game = CooperativeGame(long_function) -sage: long_game.shapley_value() -{1: 20, 2: 20, 3: 5, 4: 20} -""" From f1f3e4fa89c25d12420b52f64442c50f2f610d00 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 15:57:48 +0100 Subject: [PATCH 170/546] adds more long examples --- src/sage/game_theory/cooperative_game.py | 70 +++++++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 0ed56021e89..26ff2c1fd8a 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -444,7 +444,7 @@ def get_predecessors(self, player, permutation): def show(self): r""" EXAMPLES:: - Typical use of the show function with a given Payoff Vector.:: + Typical use of the show function with a given Payoff Vector. :: sage: letter_function = {(): 0, ....: ('A',): 6, @@ -468,7 +468,7 @@ def show(self): ('A', 'B', 'C') : 42 Payoff vector is {'A': 14, 'C': 14, 'B': 14} - Typical use of the show function with after calculating the Shapley value.:: + Typical use of the show function after calculating the Shapley value. :: sage: letter_game.shapley_value() {'A': 2, 'C': 35, 'B': 5} @@ -529,6 +529,28 @@ def is_efficient(self): sage: letter_game.is_efficient() False + A longer example for is_efficient. :: + + sage: long_function = {(): 0, + ....: (1,): 0, + ....: (2,): 0, + ....: (3,): 0, + ....: (4,): 0, + ....: (1, 2): 0, + ....: (1, 3): 0, + ....: (1, 4): 0, + ....: (2, 3): 0, + ....: (2, 4): 0, + ....: (3, 4): 0, + ....: (1, 2, 3): 0, + ....: (1, 2, 4): 45, + ....: (1, 3, 4): 40, + ....: (2, 3, 4): 0, + ....: (1, 2, 3, 4): 65} + sage: long_game = CooperativeGame(long_function, {1: 20, 2: 20, 3: 5, 4: 20}) + sage: long_game.is_efficient() + True + TESTS:: Checks that a game has a payoff_vector. :: @@ -584,6 +606,28 @@ def nullplayer(self): sage: A_game.nullplayer() False + A longer example for nullplayer. :: + + sage: long_function = {(): 0, + ....: (1,): 0, + ....: (2,): 0, + ....: (3,): 0, + ....: (4,): 0, + ....: (1, 2): 0, + ....: (1, 3): 0, + ....: (1, 4): 0, + ....: (2, 3): 0, + ....: (2, 4): 0, + ....: (3, 4): 0, + ....: (1, 2, 3): 0, + ....: (1, 2, 4): 45, + ....: (1, 3, 4): 40, + ....: (2, 3, 4): 0, + ....: (1, 2, 3, 4): 65} + sage: long_game = CooperativeGame(long_function, {1: 20, 2: 20, 3: 5, 4: 20}) + sage: long_game.nullplayer() + True + TESTS:: Checks that a game has a payoff_vector. :: @@ -653,6 +697,28 @@ def symmetry(self): sage: integer_game.symmetry() False + A longer example for symmetry. :: + + sage: long_function = {(): 0, + ....: (1,): 0, + ....: (2,): 0, + ....: (3,): 0, + ....: (4,): 0, + ....: (1, 2): 0, + ....: (1, 3): 0, + ....: (1, 4): 0, + ....: (2, 3): 0, + ....: (2, 4): 0, + ....: (3, 4): 0, + ....: (1, 2, 3): 0, + ....: (1, 2, 4): 45, + ....: (1, 3, 4): 40, + ....: (2, 3, 4): 0, + ....: (1, 2, 3, 4): 65} + sage: long_game = CooperativeGame(long_function, {1: 20, 2: 20, 3: 5, 4: 20}) + sage: long_game.symmetry() + True + TESTS:: Checks that a game has a payoff_vector. :: From a8396311d508b73daf6238d77bcb03974a038f5e Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 16:09:53 +0100 Subject: [PATCH 171/546] creates another test in __init__, this one for power set --- src/sage/game_theory/cooperative_game.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 26ff2c1fd8a..edceda56413 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -132,7 +132,8 @@ def __init__(self, characteristic_function, payoff_vector=False): TypeError: Characteristic function must be a dictionary This test checks that an error is raised when a key in the - Characteristic Function is not a tuple. :: + Characteristic Function is not a tuple. It fails because `(1)` + is not a tuple, `(1,)` is however. :: sage: tuple_function = {(): 0, ....: (1): 6, @@ -147,7 +148,18 @@ def __init__(self, characteristic_function, payoff_vector=False): ... TypeError: Key must be a tuple - The above test failed as `(1)` is not read as a tuple. + A test to ensure that the Characteristic Function is the power + set (ie all possible coalitions). :: + + sage: simple_function = {(): 0, + ....: (1): 6, + ....: (2,): 12, + ....: (3,): 42, + ....: (1, 2, 3,): 42} + sage: simple_game = CooperativeGame(simple_function) + Traceback (most recent call last): + ... + ValueError: Characteristic Function must be the power set """ if type(characteristic_function) is not dict: raise TypeError("Characteristic function must be a dictionary") From 6987a743c049be01bba436631cd2cdd06fa7173e Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 4 Jun 2014 16:18:06 +0100 Subject: [PATCH 172/546] potential output added to tests --- src/sage/game_theory/cooperative_game.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index edceda56413..056e6a94477 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -377,7 +377,7 @@ def marginal_contributions(self, player): ....: (1, 2, 3,): 42} sage: integer_game = CooperativeGame(integer_function) sage: integer_game.marginal_contributions(1) - + [6, 6, 0, 0, 0, 0] """ contributions = [] for pi in permutations(self.player_list): @@ -406,7 +406,7 @@ def marginal_of_pi(self, player, pi): ....: (1, 2, 3,): 42} sage: integer_game = CooperativeGame(integer_function) sage: integer_game.marginal_of_pi(2, (2, 3, 1)) - + 12 """ predecessors, player_and_pred = self.get_predecessors(player, pi) if predecessors is None: From a26fee164b101d62e864b9b48d78914b2dc915a6 Mon Sep 17 00:00:00 2001 From: drvinceknight Date: Wed, 4 Jun 2014 16:51:17 +0100 Subject: [PATCH 173/546] get preds tidied slightly --- src/sage/game_theory/cooperative_game.py | 25 +++++++----------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 056e6a94477..fb094981125 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -149,7 +149,7 @@ def __init__(self, characteristic_function, payoff_vector=False): TypeError: Key must be a tuple A test to ensure that the Characteristic Function is the power - set (ie all possible coalitions). :: + set of the grand coalition (ie all possible sub-coalitions). :: sage: simple_function = {(): 0, ....: (1): 6, @@ -169,11 +169,9 @@ def __init__(self, characteristic_function, payoff_vector=False): raise TypeError("Key must be a tuple") players = set(max(characteristic_function.keys(), key=lambda key: len(key))) - for com in chain.from_iterable(combinations(players, r) for r in range(len(players)+1)): - if z in characteristic_function.keys(): - pass - else: - raise ValueError("Characteristic Function must be the power set") +# for com in chain.from_iterable(combinations(players, r) for r in range(len(players)+1)): +# if com not in characteristic_function.keys(): +# raise ValueError("Characteristic Function must be the power set") self.ch_f = characteristic_function self.player_list = max(characteristic_function.keys(), key=lambda key: len(key)) @@ -439,19 +437,10 @@ def get_predecessors(self, player, permutation): ....: (1, 2, 3,): 42} sage: integer_game = CooperativeGame(integer_function) sage: integer_game.get_predecessors(1, (2, 3, 1)) - [2, 3] + ([2, 3], [1, 2, 3]) """ - pred = [] - play_and_pred = [] - for k in permutation: - if k == player: - play_and_pred.append(k) - pred.sort() - play_and_pred.sort() - return pred, play_and_pred - else: - pred.append(k) - play_and_pred.append(k) + pred = list(permutation[:permutation.index(player)]) + return sorted(pred), sorted(pred + [player]) def show(self): r""" From 7393a97b92ff27cac85d7ea3fbbcb9b8cc27afbf Mon Sep 17 00:00:00 2001 From: drvinceknight Date: Wed, 4 Jun 2014 16:53:01 +0100 Subject: [PATCH 174/546] more tests written for get_pred --- src/sage/game_theory/cooperative_game.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index fb094981125..561091cb692 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -417,7 +417,7 @@ def marginal_of_pi(self, player, pi): def get_predecessors(self, player, permutation): r""" - Returns a list of all the predecessors of a player in a certain permutation. + Returns a list of all the predecessors of a player in a certain permutation and the same list including the original player (used elsewhere). INPUT: @@ -438,6 +438,10 @@ def get_predecessors(self, player, permutation): sage: integer_game = CooperativeGame(integer_function) sage: integer_game.get_predecessors(1, (2, 3, 1)) ([2, 3], [1, 2, 3]) + sage: integer_game.get_predecessors(2, (2, 3, 1)) + ([], [2]) + sage: integer_game.get_predecessors(3, (2, 3, 1)) + ([2], [2, 3]) """ pred = list(permutation[:permutation.index(player)]) return sorted(pred), sorted(pred + [player]) From 000195936b415265e979f1d61bcf2ad67da0ba04 Mon Sep 17 00:00:00 2001 From: drvinceknight Date: Wed, 4 Jun 2014 17:05:05 +0100 Subject: [PATCH 175/546] get preds tidied slightly --- src/sage/game_theory/cooperative_game.py | 32 ++++++++++++++++++++---- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 561091cb692..cabf9dfef5c 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -325,7 +325,7 @@ def is_superadditive(self): sage: A_game.is_superadditive() True - An example for is_superadditive with a longer game. :: + An example for is_superadditive with a longer game that returns True. :: sage: long_function = {(): 0, ....: (1,): 0, @@ -346,13 +346,35 @@ def is_superadditive(self): sage: long_game = CooperativeGame(long_function) sage: long_game.is_superadditive() False + + An example for is_superadditive with a longer game that returns False. :: + + sage: long_function = {(): 0, + ....: (1,): 0, + ....: (2,): 0, + ....: (3,): 0, + ....: (4,): 0, + ....: (1, 2): 0, + ....: (1, 3): 0, + ....: (1, 4): 0, + ....: (2, 3): 0, + ....: (2, 4): 0, + ....: (3, 4): 0, + ....: (1, 2, 3): 0, + ....: (1, 2, 4): 45, + ....: (1, 3, 4): 40, + ....: (2, 3, 4): 0, + ....: (1, 2, 3, 4): 85} + sage: long_game = CooperativeGame(long_function) + sage: long_game.is_superadditive() + True """ sets = list(self.ch_f.keys()) for p1, p2 in permutations(sets, 2): - if set(p1) & set(p2) == set(): - union = tuple(sorted(list(set(p1) | set(p2)))) - if self.ch_f[union] < self.ch_f[p1] + self.ch_f[p2]: - return False + #if set(p1) & set(p2) == set(): + union = tuple(sorted(list(set(p1) | set(p2)))) + if union not in [p1, p2] and self.ch_f[union] < self.ch_f[p1] + self.ch_f[p2]: + return False return True def marginal_contributions(self, player): From e5988eaa5d133ea8d3df433e071d162730900474 Mon Sep 17 00:00:00 2001 From: drvinceknight Date: Wed, 4 Jun 2014 17:11:34 +0100 Subject: [PATCH 176/546] superadditivity extra test added --- src/sage/game_theory/cooperative_game.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index cabf9dfef5c..49a9e4f96ea 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -345,14 +345,14 @@ def is_superadditive(self): ....: (1, 2, 3, 4): 65} sage: long_game = CooperativeGame(long_function) sage: long_game.is_superadditive() - False + True An example for is_superadditive with a longer game that returns False. :: sage: long_function = {(): 0, ....: (1,): 0, ....: (2,): 0, - ....: (3,): 0, + ....: (3,): 55, ....: (4,): 0, ....: (1, 2): 0, ....: (1, 3): 0, @@ -367,14 +367,14 @@ def is_superadditive(self): ....: (1, 2, 3, 4): 85} sage: long_game = CooperativeGame(long_function) sage: long_game.is_superadditive() - True + False """ sets = list(self.ch_f.keys()) - for p1, p2 in permutations(sets, 2): - #if set(p1) & set(p2) == set(): - union = tuple(sorted(list(set(p1) | set(p2)))) - if union not in [p1, p2] and self.ch_f[union] < self.ch_f[p1] + self.ch_f[p2]: - return False + for p1, p2 in combinations(sets, 2): + if set(p1) & set(p2) == set(): + union = tuple(sorted(list(set(p1) | set(p2)))) + if self.ch_f[union] < self.ch_f[p1] + self.ch_f[p2]: + return False return True def marginal_contributions(self, player): From 1d3f909e49a21daaaa98e6bf4aebab31f0c9befd Mon Sep 17 00:00:00 2001 From: drvinceknight Date: Wed, 4 Jun 2014 17:24:17 +0100 Subject: [PATCH 177/546] Null player is broken --- src/sage/game_theory/cooperative_game.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 49a9e4f96ea..0ad981c3a28 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -778,6 +778,4 @@ def symmetry(self): pass elif all(results): return False - else: - pass return True From cc636b40df02050ea6f594673f16ab8cd61aa7d7 Mon Sep 17 00:00:00 2001 From: vince Date: Wed, 4 Jun 2014 19:53:14 +0100 Subject: [PATCH 178/546] Completely re wrote the nullplayer method: passed all tests --- src/sage/game_theory/cooperative_game.py | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 0ad981c3a28..567a4898f39 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -675,20 +675,8 @@ def nullplayer(self): if not self.payoff_vector: raise ValueError("Game must have a payoff_vector") - allsets = list(self.ch_f.keys()) - players = [(i,) for i in self.player_list if self.ch_f[(i,)] == 0] - nulls = [] - for player in players: - test = [set(m) for m in allsets if player in m] - results = [] - for sets in test: - results.append(self.ch_f[tuple(sets.remove(player))] == self.ch_f[tuple(sets)]) - if all(results): - nulls.append(player) - - for player in nulls: - if self.payoff_vector[player[0]] != 0: - return False + for player in self.player_list: + return not (all([self.ch_f[coalition] == self.ch_f[tuple(sorted(list(set(coalition) - {player})))] for coalition in [coalition for coalition in self.ch_f if player in coalition]]) and self.payoff_vector[player] != 0) return True def symmetry(self): From cbedc9d0391a203f9066b54f25321ecd22813ba2 Mon Sep 17 00:00:00 2001 From: vince Date: Wed, 4 Jun 2014 20:03:57 +0100 Subject: [PATCH 179/546] Fixed shapley_value: test wasnt correct --- src/sage/game_theory/cooperative_game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 567a4898f39..a02e7077c91 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -223,7 +223,7 @@ def shapley_value(self): ....: (1, 2, 3, 4): 65} sage: long_game = CooperativeGame(long_function) sage: long_game.shapley_value() - {1: 20, 2: 20, 3: 5, 4: 20} + {1: 70/3, 2: 10, 3: 25/3, 4: 70/3} """ payoff_vector = {} for player in self.player_list: From 2d073ce2205ca32da2ac69e573de708620407911 Mon Sep 17 00:00:00 2001 From: vince Date: Wed, 4 Jun 2014 20:06:44 +0100 Subject: [PATCH 180/546] Removed instance of keys() being used when not needed --- src/sage/game_theory/cooperative_game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index a02e7077c91..c79fd923141 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -164,7 +164,7 @@ def __init__(self, characteristic_function, payoff_vector=False): if type(characteristic_function) is not dict: raise TypeError("Characteristic function must be a dictionary") - for key in list(characteristic_function.keys()): + for key in characteristic_function: if type(key) is not tuple: raise TypeError("Key must be a tuple") From 98f04eaa3f6759eb7a98d539dee83839288779fd Mon Sep 17 00:00:00 2001 From: vince Date: Wed, 4 Jun 2014 20:30:26 +0100 Subject: [PATCH 181/546] Init tests now pass - used the sage powerset function --- src/sage/game_theory/cooperative_game.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index c79fd923141..884ca55ee00 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -24,6 +24,7 @@ #***************************************************************************** from itertools import chain, permutations, combinations from sage.structure.sage_object import SageObject +from sage.misc.misc import powerset class CooperativeGame(SageObject): @@ -151,12 +152,12 @@ def __init__(self, characteristic_function, payoff_vector=False): A test to ensure that the Characteristic Function is the power set of the grand coalition (ie all possible sub-coalitions). :: - sage: simple_function = {(): 0, - ....: (1): 6, + sage: incorrect_function = {(): 0, + ....: (1,): 6, ....: (2,): 12, ....: (3,): 42, ....: (1, 2, 3,): 42} - sage: simple_game = CooperativeGame(simple_function) + sage: incorrect_function = CooperativeGame(incorrect_function) Traceback (most recent call last): ... ValueError: Characteristic Function must be the power set @@ -168,13 +169,12 @@ def __init__(self, characteristic_function, payoff_vector=False): if type(key) is not tuple: raise TypeError("Key must be a tuple") - players = set(max(characteristic_function.keys(), key=lambda key: len(key))) -# for com in chain.from_iterable(combinations(players, r) for r in range(len(players)+1)): -# if com not in characteristic_function.keys(): -# raise ValueError("Characteristic Function must be the power set") - - self.ch_f = characteristic_function self.player_list = max(characteristic_function.keys(), key=lambda key: len(key)) + self.ch_f = characteristic_function + + if sorted([tuple(coalition) for coalition in powerset(self.player_list)]) != sorted(self.ch_f.keys()): + raise ValueError("Characteristic Function must be the power set") + self.number_players = len(self.player_list) self.payoff_vector = payoff_vector From 748c46f706c37a82f06b520d5a4e744a219326a9 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Thu, 5 Jun 2014 11:54:40 +0100 Subject: [PATCH 182/546] tidies up nullplayer fucntion --- src/sage/game_theory/cooperative_game.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 884ca55ee00..facd9b12d8e 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -157,7 +157,7 @@ def __init__(self, characteristic_function, payoff_vector=False): ....: (2,): 12, ....: (3,): 42, ....: (1, 2, 3,): 42} - sage: incorrect_function = CooperativeGame(incorrect_function) + sage: incorrect_game = CooperativeGame(incorrect_function) Traceback (most recent call last): ... ValueError: Characteristic Function must be the power set @@ -290,7 +290,7 @@ def is_monotone(self): """ sets = list(self.ch_f.keys()) return not any([set(p1) <= set(p2) and self.ch_f[p1] > self.ch_f[p2] - for p1, p2 in permutations(sets, 2)]) + for p1, p2 in permutations(sets, 2)]) def is_superadditive(self): r""" @@ -439,7 +439,9 @@ def marginal_of_pi(self, player, pi): def get_predecessors(self, player, permutation): r""" - Returns a list of all the predecessors of a player in a certain permutation and the same list including the original player (used elsewhere). + Returns a list of all the predecessors of a player in a certain + permutation and the same list including the original player + (used elsewhere). INPUT: @@ -602,7 +604,8 @@ def is_efficient(self): def nullplayer(self): r""" - Returns True if the current payoff_vector possesses the null player property. + Returns True if the current payoff_vector possesses the null + player property. EXAMPLES:: A payoff_vector that returns True. :: @@ -676,7 +679,13 @@ def nullplayer(self): raise ValueError("Game must have a payoff_vector") for player in self.player_list: - return not (all([self.ch_f[coalition] == self.ch_f[tuple(sorted(list(set(coalition) - {player})))] for coalition in [coalition for coalition in self.ch_f if player in coalition]]) and self.payoff_vector[player] != 0) + coalitions = [coal for coal in self.ch_f if player in coal] + results = [] + for coalit in coalitions: + results.append(self.ch_f[coalit] == self.ch_f[tuple( + sorted(list(set(coalit) - {player})))]) + if all(results) and self.payoff_vector[player] != 0: + return False return True def symmetry(self): From 0f3c842d5b4c64f1b2ff5604db05de5c9baf409c Mon Sep 17 00:00:00 2001 From: James Campbell Date: Thu, 5 Jun 2014 12:02:29 +0100 Subject: [PATCH 183/546] tidies up symmetry --- src/sage/game_theory/cooperative_game.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index facd9b12d8e..aadda915cd3 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -771,8 +771,7 @@ def symmetry(self): junion = tuple(sorted(list(set(c1) | set(m)))) kunion = tuple(sorted(list(set(c2) | set(m)))) results.append(self.ch_f[junion] == self.ch_f[kunion]) - if all(results) and self.payoff_vector[c1[0]] == self.payoff_vector[c2[0]]: - pass - elif all(results): + if (all(results) and self.payoff_vector[c1[0]] != + self.payoff_vector[c2[0]]): return False return True From 0ef8feb87ce40a40b95259deab79815abae98f35 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Thu, 5 Jun 2014 12:09:00 +0100 Subject: [PATCH 184/546] adds extra test to null player --- src/sage/game_theory/cooperative_game.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index aadda915cd3..41f8da9be3c 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -674,6 +674,20 @@ def nullplayer(self): Traceback (most recent call last): ... ValueError: Game must have a payoff_vector + + Checks that the function is going through all players. :: + + sage: A_function = {(): 0, + ....: (1,): 42, + ....: (2,): 12, + ....: (3,): 0, + ....: (1, 2,): 55, + ....: (1, 3,): 42, + ....: (2, 3,): 12, + ....: (1, 2, 3,): 55} + sage: A_game = CooperativeGame(A_function, {1: 10, 2: 10, 3: 25}) + sage: A_game.nullplayer() + False """ if not self.payoff_vector: raise ValueError("Game must have a payoff_vector") From b5989a48b0c1e78d957bbf7b7efecd0cd101a142 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Thu, 5 Jun 2014 12:22:41 +0100 Subject: [PATCH 185/546] adds test for characteristic function being unordered --- src/sage/game_theory/cooperative_game.py | 45 ++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 41f8da9be3c..7e1ba64a552 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -117,6 +117,51 @@ class CooperativeGame(SageObject): True sage: letter_game.symmetry() True + + TESTS:: + Checks that the same game works with its characteristic_function in a + different order. :: + + sage: order_function = {(): 0, + ....: ('A', 'B', 'C',): 42, + ....: ('B',): 12, + ....: ('A', 'B',): 12, + ....: ('A', 'C',): 42, + ....: ('A',): 6, + ....: ('B', 'C',): 42, + ....: ('C',): 42} + sage: order_game = CooperativeGame(order_function) + sage: order_game.shapley_value() + {'A': 2, 'C': 35, 'B': 5} + sage: order_game.is_monotone() + True + sage: order_game.is_superadditive() + False + sage: order_game.show() + A Co-operative Game with 3 players + Characteristic Function is + ('A',) : 6 + ('B', 'C') : 42 + () : 0 + ('C',) : 42 + ('A', 'B', 'C') : 42 + ('B',) : 12 + ('A', 'B') : 12 + ('A', 'C') : 42 + Payoff vector is {'A': 2, 'C': 35, 'B': 5} + sage: order_game.is_efficient() + True + sage: order_game.nullplayer() + True + sage: order_game.symmetry() + True + sage: order_game.payoff_vector = {'A': 0, 'C': 35, 'B': 3} + sage: order_game.is_efficient() + False + sage: order_game.nullplayer() + True + sage: order_game.symmetry() + True """ def __init__(self, characteristic_function, payoff_vector=False): From bc16b147d7e86c7c61528103689dace56e2c2160 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 5 Jun 2014 14:13:35 +0200 Subject: [PATCH 186/546] trac #16446: BIBD constructor in lower case --- src/sage/combinat/designs/bibd.py | 75 +++++++++++---------- src/sage/combinat/designs/design_catalog.py | 24 ++++++- 2 files changed, 60 insertions(+), 39 deletions(-) diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index 18f19286795..144a55234dc 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -6,16 +6,16 @@ * Steiner Triple Systems, i.e. `(v,3,1)`-BIBD. * `K_4`-decompositions of `K_v`, i.e. `(v,4,1)`-BIBD. -These BIBD can be obtained through the :meth:`BalancedIncompleteBlockDesign` -method, available in Sage as ``designs.BalancedIncompleteBlockDesign``. +These BIBD can be obtained through the :func:`balanced_incomplete_block_design` +method, available in Sage as ``designs.balanced_incomplete_block_design``. EXAMPLES:: - sage: designs.BalancedIncompleteBlockDesign(7,3) + sage: designs.balanced_incomplete_block_design(7,3) Incidence structure with 7 points and 7 blocks - sage: designs.BalancedIncompleteBlockDesign(7,3).blocks() + sage: designs.balanced_incomplete_block_design(7,3).blocks() [[0, 1, 3], [0, 2, 4], [0, 5, 6], [1, 2, 6], [1, 4, 5], [2, 3, 5], [3, 4, 6]] - sage: designs.BalancedIncompleteBlockDesign(13,4).blocks() + sage: designs.balanced_incomplete_block_design(13,4).blocks() [[0, 1, 2, 12], [0, 3, 6, 9], [0, 4, 8, 10], [0, 5, 7, 11], [1, 3, 8, 11], [1, 4, 7, 9], [1, 5, 6, 10], [2, 3, 7, 10], [2, 4, 6, 11], [2, 5, 8, 9], [3, 4, 5, 12], [6, 7, 8, 12], [9, 10, 11, 12]] @@ -26,7 +26,7 @@ Decompositions of `K_v` into `K_4` (i.e. `(v,4,1)`-BIBD) are built following Douglas Stinson's construction as presented in [Stinson2004]_ page 167. It is based upon the construction of `(v\{4,5,8,9,12\})`-PBD (see the doc of -:meth:`PBD_4_5_8_9_12`), knowing that a `(v\{4,5,8,9,12\})`-PBD on `v` points +:func:`PBD_4_5_8_9_12`), knowing that a `(v\{4,5,8,9,12\})`-PBD on `v` points can always be transformed into a `((k-1)v+1,4,1)`-BIBD, which covers all possible cases of `(v,4,1)`-BIBD. @@ -52,7 +52,7 @@ from sage.rings.arith import binomial from sage.rings.arith import is_prime_power -def BalancedIncompleteBlockDesign(v,k,existence=False,use_LJCR=False): +def balanced_incomplete_block_design(v,k,existence=False,use_LJCR=False): r""" Returns a BIBD of parameters `v,k`. @@ -82,7 +82,7 @@ def BalancedIncompleteBlockDesign(v,k,existence=False,use_LJCR=False): - ``use_LJCR`` (boolean) -- whether to query the La Jolla Covering Repository for the design when Sage does not know how to build it (see - :meth:`~sage.combinat.designs.covering_design.best_known_covering_design_www`). This + :func:`~sage.combinat.designs.covering_design.best_known_covering_design_www`). This requires internet. .. SEEALSO:: @@ -98,9 +98,9 @@ def BalancedIncompleteBlockDesign(v,k,existence=False,use_LJCR=False): EXAMPLES:: - sage: designs.BalancedIncompleteBlockDesign(7,3).blocks() + sage: designs.balanced_incomplete_block_design(7,3).blocks() [[0, 1, 3], [0, 2, 4], [0, 5, 6], [1, 2, 6], [1, 4, 5], [2, 3, 5], [3, 4, 6]] - sage: B = designs.BalancedIncompleteBlockDesign(21,5, use_LJCR=True) # optional - internet + sage: B = designs.balanced_incomplete_block_design(21,5, use_LJCR=True) # optional - internet sage: B # optional - internet Incidence structure with 21 points and 21 blocks sage: B.blocks() # optional - internet @@ -111,47 +111,48 @@ def BalancedIncompleteBlockDesign(v,k,existence=False,use_LJCR=False): [2, 7, 8, 13, 19], [3, 4, 10, 13, 18], [3, 5, 8, 14, 17], [3, 6, 9, 12, 19], [3, 7, 11, 15, 16], [4, 5, 6, 7, 20], [8, 9, 10, 11, 20], [12, 13, 14, 15, 20], [16, 17, 18, 19, 20]] - sage: designs.BalancedIncompleteBlockDesign(20,5, use_LJCR=True) # optional - internet + sage: designs.balanced_incomplete_block_design(20,5, use_LJCR=True) # optional - internet Traceback (most recent call last): ... ValueError: No such design exists ! - sage: designs.BalancedIncompleteBlockDesign(16,6) + sage: designs.balanced_incomplete_block_design(16,6) Traceback (most recent call last): ... NotImplementedError: I don't know how to build a (16,6,1)-BIBD! TESTS:: - sage: designs.BalancedIncompleteBlockDesign(85,5,existence=True) + sage: designs.balanced_incomplete_block_design(85,5,existence=True) True - sage: _ = designs.BalancedIncompleteBlockDesign(85,5) + sage: _ = designs.balanced_incomplete_block_design(85,5) A BIBD from a Finite Projective Plane:: - sage: _ = designs.BalancedIncompleteBlockDesign(21,5) + sage: _ = designs.balanced_incomplete_block_design(21,5) Some trivial BIBD:: - sage: designs.BalancedIncompleteBlockDesign(10,10) + sage: designs.balanced_incomplete_block_design(10,10) Incidence structure with 10 points and 1 blocks - sage: designs.BalancedIncompleteBlockDesign(1,10) + sage: designs.balanced_incomplete_block_design(1,10) Incidence structure with 1 points and 0 blocks Existence of BIBD with `k=3,4,5`:: - sage: [v for v in xrange(50) if designs.BalancedIncompleteBlockDesign(v,3,existence=True)] + sage: [v for v in xrange(50) if designs.balanced_incomplete_block_design(v,3,existence=True)] [1, 3, 7, 9, 13, 15, 19, 21, 25, 27, 31, 33, 37, 39, 43, 45, 49] - sage: [v for v in xrange(100) if designs.BalancedIncompleteBlockDesign(v,4,existence=True)] + sage: [v for v in xrange(100) if designs.balanced_incomplete_block_design(v,4,existence=True)] [1, 4, 13, 16, 25, 28, 37, 40, 49, 52, 61, 64, 73, 76, 85, 88, 97] - sage: [v for v in xrange(150) if designs.BalancedIncompleteBlockDesign(v,5,existence=True)] + sage: [v for v in xrange(150) if designs.balanced_incomplete_block_design(v,5,existence=True)] [1, 5, 21, 25, 41, 45, 61, 65, 81, 85, 101, 105, 121, 125, 141, 145] For `k > 5` there are currently very few constructions:: - sage: [v for v in xrange(150) if designs.BalancedIncompleteBlockDesign(v,6,existence=True) is True] + sage: [v for v in xrange(150) if designs.balanced_incomplete_block_design(v,6,existence=True) is True] [1, 6, 31] - sage: [v for v in xrange(150) if designs.BalancedIncompleteBlockDesign(v,6,existence=True) is Unknown] + sage: [v for v in xrange(150) if designs.balanced_incomplete_block_design(v,6,existence=True) is Unknown] [16, 21, 36, 46, 51, 61, 66, 76, 81, 91, 96, 106, 111, 121, 126, 136, 141] + """ if v == 1: if existence: @@ -382,14 +383,14 @@ def BIBD_from_TD(v,k,existence=False): # First construction if (v%k == 0 and - BalancedIncompleteBlockDesign(v//k,k,existence=True) and + balanced_incomplete_block_design(v//k,k,existence=True) and transversal_design(k,v//k,existence=True)): if existence: return True v = v//k - BIBDvk = BalancedIncompleteBlockDesign(v,k) + BIBDvk = balanced_incomplete_block_design(v,k) TDkv = transversal_design(k,v,check=False) BIBD = TDkv @@ -398,14 +399,14 @@ def BIBD_from_TD(v,k,existence=False): # Second construction elif ((v-1)%k == 0 and - BalancedIncompleteBlockDesign((v-1)//k+1,k,existence=True) and + balanced_incomplete_block_design((v-1)//k+1,k,existence=True) and transversal_design(k,(v-1)//k,existence=True)): if existence: return True v = (v-1)//k - BIBDv1k = BalancedIncompleteBlockDesign(v+1,k) + BIBDv1k = balanced_incomplete_block_design(v+1,k) TDkv = transversal_design(k,v,check=False) inf = v*k @@ -415,14 +416,14 @@ def BIBD_from_TD(v,k,existence=False): # Third construction elif ((v-k)%k == 0 and - BalancedIncompleteBlockDesign((v-k)//k+k,k,existence=True) and + balanced_incomplete_block_design((v-k)//k+k,k,existence=True) and transversal_design(k,(v-k)//k,existence=True)): if existence: return True v = (v-k)//k - BIBDvpkk = BalancedIncompleteBlockDesign(v+k,k) + BIBDvpkk = balanced_incomplete_block_design(v+k,k) TDkv = transversal_design(k,v,check=False) inf = v*k BIBD = TDkv @@ -518,14 +519,14 @@ def v_4_1_BIBD(v, check=True): A `(v,4,1)`-BIBD is an edge-decomposition of the complete graph `K_v` into copies of `K_4`. For more information, see - :meth:`BalancedIncompleteBlockDesign`. It exists if and only if `v\equiv 1,4 + :func:`balanced_incomplete_block_design`. It exists if and only if `v\equiv 1,4 \pmod {12}`. See page 167 of [Stinson2004]_ for the construction details. .. SEEALSO:: - * :meth:`BalancedIncompleteBlockDesign` + * :func:`balanced_incomplete_block_design` INPUT: @@ -663,7 +664,7 @@ def BIBD_from_PBD(PBD,v,k,check=True,base_cases={}): n = len(X) N = (k-1)*n+1 if not (n,k) in base_cases: - base_cases[n,k] = _relabel_bibd(BalancedIncompleteBlockDesign(N,k).blcks,N) + base_cases[n,k] = _relabel_bibd(balanced_incomplete_block_design(N,k).blcks,N) for XX in base_cases[n,k]: if N-1 in XX: @@ -692,7 +693,7 @@ def _check_pbd(B,v,S): EXAMPLE:: - sage: designs.BalancedIncompleteBlockDesign(40,4).blocks() # indirect doctest + sage: designs.balanced_incomplete_block_design(40,4).blocks() # indirect doctest [[0, 1, 2, 12], [0, 3, 6, 9], [0, 4, 8, 10], [0, 5, 7, 11], [0, 13, 26, 39], [0, 14, 25, 28], [0, 15, 27, 38], [0, 16, 22, 32], [0, 17, 23, 34], @@ -733,7 +734,7 @@ def _relabel_bibd(B,n,p=None): EXAMPLE:: - sage: designs.BalancedIncompleteBlockDesign(40,4).blocks() # indirect doctest + sage: designs.balanced_incomplete_block_design(40,4).blocks() # indirect doctest [[0, 1, 2, 12], [0, 3, 6, 9], [0, 4, 8, 10], [0, 5, 7, 11], [0, 13, 26, 39], [0, 14, 25, 28], [0, 15, 27, 38], [0, 16, 22, 32], [0, 17, 23, 34], @@ -774,7 +775,7 @@ def PBD_4_5_8_9_12(v, check=True): EXAMPLES:: - sage: designs.BalancedIncompleteBlockDesign(40,4).blocks() # indirect doctest + sage: designs.balanced_incomplete_block_design(40,4).blocks() # indirect doctest [[0, 1, 2, 12], [0, 3, 6, 9], [0, 4, 8, 10], [0, 5, 7, 11], [0, 13, 26, 39], [0, 14, 25, 28], [0, 15, 27, 38], [0, 16, 22, 32], [0, 17, 23, 34], @@ -841,13 +842,13 @@ def _PBD_4_5_8_9_12_closure(B): r""" Makes sure all blocks of `B` have size in `\{4,5,8,9,12\}`. - This is a helper function for :meth:`PBD_4_5_8_9_12`. Given that + This is a helper function for :func:`PBD_4_5_8_9_12`. Given that `\{4,5,8,9,12\}` is PBD-closed, any block of size not in `\{4,5,8,9,12\}` can be decomposed further. EXAMPLES:: - sage: designs.BalancedIncompleteBlockDesign(40,4).blocks() # indirect doctest + sage: designs.balanced_incomplete_block_design(40,4).blocks() # indirect doctest [[0, 1, 2, 12], [0, 3, 6, 9], [0, 4, 8, 10], [0, 5, 7, 11], [0, 13, 26, 39], [0, 14, 25, 28], [0, 15, 27, 38], [0, 16, 22, 32], [0, 17, 23, 34], @@ -933,7 +934,7 @@ def v_5_1_BIBD(v, check=True): .. SEEALSO:: - * :meth:`BalancedIncompleteBlockDesign` + * :func:`balanced_incomplete_block_design` EXAMPLES:: diff --git a/src/sage/combinat/designs/design_catalog.py b/src/sage/combinat/designs/design_catalog.py index 050fba46da7..3c517333df7 100644 --- a/src/sage/combinat/designs/design_catalog.py +++ b/src/sage/combinat/designs/design_catalog.py @@ -40,7 +40,7 @@ :meth:`~sage.combinat.designs.block_design.ProjectiveGeometryDesign` :meth:`~sage.combinat.designs.block_design.ProjectivePlaneDesign` - :meth:`~sage.combinat.designs.bibd.BalancedIncompleteBlockDesign` + :meth:`~sage.combinat.designs.bibd.balanced_incomplete_block_design` :meth:`~sage.combinat.designs.block_design.AffineGeometryDesign` :meth:`~sage.combinat.designs.block_design.WittDesign` :meth:`~sage.combinat.designs.block_design.HadamardDesign` @@ -80,4 +80,24 @@ from sage.combinat.designs.orthogonal_arrays import transversal_design, orthogonal_array -from sage.combinat.designs.bibd import BalancedIncompleteBlockDesign, steiner_triple_system +from sage.combinat.designs.bibd import balanced_incomplete_block_design, steiner_triple_system + +# deprecated in june 2014 (#16446) +def BalancedIncompleteBlockDesign(v,k,existence=False,use_LJCR=False): + r""" + This function is deprecated. + + TESTS:: + + sage: bibd = designs.BalancedIncompleteBlockDesign(21,5) + doctest:...: DeprecationWarning: designs.BalancedIncompleteBlockDesign is + deprecated. Please use designs.balanced_incomplete_block_design instead. + See http://trac.sagemath.org/16446 for details. + sage: bibd + Incidence structure with 21 points and 21 blocks + """ + from sage.misc.superseded import deprecation + deprecation(16446, "designs.BalancedIncompleteBlockDesign is deprecated. Please use designs.balanced_incomplete_block_design instead.") + + return balanced_incomplete_block_design(v, k, existence=existence, use_LJCR=use_LJCR) + From 7d0e789a5b4b3d6b5f08aaa8d92f45dd3dbd0331 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Thu, 5 Jun 2014 16:26:32 +0200 Subject: [PATCH 187/546] 15954: replace TypeError with more informative message --- src/sage/rings/fraction_field_FpT.pyx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/fraction_field_FpT.pyx b/src/sage/rings/fraction_field_FpT.pyx index ae66b5e69f5..b24f3d00a56 100644 --- a/src/sage/rings/fraction_field_FpT.pyx +++ b/src/sage/rings/fraction_field_FpT.pyx @@ -1117,7 +1117,11 @@ cdef class Polyring_FpT_coerce(RingHomomorphism_coercion): ... ZeroDivisionError: fraction has denominator 0 """ - cdef Polynomial_zmod_flint x = _x + cdef Polynomial_zmod_flint x + try: + x = _x + except TypeError: + raise NotImplementedError('Fraction fields not implemented for this type.') cdef FpTElement ans = PY_NEW(FpTElement) ans._parent = self.codomain() ans.p = self.p From 22af996d1917dfb56620d94e1d4353bfa5628b12 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Thu, 5 Jun 2014 16:43:43 +0200 Subject: [PATCH 188/546] 13781: fix doctest --- src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx index 26ceef1ad28..f9ae60d3232 100644 --- a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx @@ -817,10 +817,14 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): sage: (x-1)^5 x^5 + 95*x^4 + 10*x^3 + 90*x^2 + 5*x + 99 + Negative powers will not work:: + sage: R. = PolynomialRing(Integers(101), implementation='ntl') sage: (x-1)^(-5) - 1/(x^5 + 96*x^4 + 10*x^3 + 91*x^2 + 5*x + 100) - + Traceback (most recent call last): + ... + NotImplementedError: Fraction fields not implemented for this type. + We define ``0^0`` to be unity, :trac:`13895`:: sage: R. = PolynomialRing(Integers(100), implementation='NTL') From 0fc261b44e0a56a8755e785adab754ddf57a086c Mon Sep 17 00:00:00 2001 From: James Campbell Date: Thu, 5 Jun 2014 16:02:16 +0100 Subject: [PATCH 189/546] removes useless test --- src/sage/game_theory/cooperative_game.py | 45 ------------------------ 1 file changed, 45 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 7e1ba64a552..41f8da9be3c 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -117,51 +117,6 @@ class CooperativeGame(SageObject): True sage: letter_game.symmetry() True - - TESTS:: - Checks that the same game works with its characteristic_function in a - different order. :: - - sage: order_function = {(): 0, - ....: ('A', 'B', 'C',): 42, - ....: ('B',): 12, - ....: ('A', 'B',): 12, - ....: ('A', 'C',): 42, - ....: ('A',): 6, - ....: ('B', 'C',): 42, - ....: ('C',): 42} - sage: order_game = CooperativeGame(order_function) - sage: order_game.shapley_value() - {'A': 2, 'C': 35, 'B': 5} - sage: order_game.is_monotone() - True - sage: order_game.is_superadditive() - False - sage: order_game.show() - A Co-operative Game with 3 players - Characteristic Function is - ('A',) : 6 - ('B', 'C') : 42 - () : 0 - ('C',) : 42 - ('A', 'B', 'C') : 42 - ('B',) : 12 - ('A', 'B') : 12 - ('A', 'C') : 42 - Payoff vector is {'A': 2, 'C': 35, 'B': 5} - sage: order_game.is_efficient() - True - sage: order_game.nullplayer() - True - sage: order_game.symmetry() - True - sage: order_game.payoff_vector = {'A': 0, 'C': 35, 'B': 3} - sage: order_game.is_efficient() - False - sage: order_game.nullplayer() - True - sage: order_game.symmetry() - True """ def __init__(self, characteristic_function, payoff_vector=False): From f9bd4362c6228e6aefb31ceac9bd5660a02a588f Mon Sep 17 00:00:00 2001 From: James Campbell Date: Thu, 5 Jun 2014 17:21:48 +0100 Subject: [PATCH 190/546] sorts the keys in char_fun --- src/sage/game_theory/cooperative_game.py | 58 ++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 41f8da9be3c..f51be439f7a 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -108,6 +108,53 @@ class CooperativeGame(SageObject): sage: letter_game.symmetry() True + Any Payoff Vector can be passed to the game and these properties can once again be tested: + + sage: letter_game.payoff_vector = {'A': 0, 'C': 35, 'B': 3} + sage: letter_game.is_efficient() + False + sage: letter_game.nullplayer() + True + sage: letter_game.symmetry() + True + + TESTS:: + Checks that the order within a key does not affect other functions. :: + + sage: letter_function = {(): 0, + ....: ('A',): 6, + ....: ('B',): 12, + ....: ('C',): 42, + ....: ('A', 'B',): 12, + ....: ('C', 'A',): 42, + ....: ('B', 'C',): 42, + ....: ('B', 'A', 'C',): 42} + sage: letter_game = CooperativeGame(letter_function) + sage: letter_game.shapley_value() + {'A': 2, 'C': 35, 'B': 5} + sage: letter_game.is_monotone() + True + sage: letter_game.is_superadditive() + False + sage: letter_game.show() + A Co-operative Game with 3 players + Characteristic Function is + ('A',) : 6 + ('B', 'C') : 42 + () : 0 + ('C',) : 42 + ('A', 'B') : 12 + ('B',) : 12 + ('A', 'B', 'C') : 42 + ('A', 'C') : 42 + Payoff vector is {'A': 2, 'C': 35, 'B': 5} + sage: letter_game.is_efficient() + True + sage: letter_game.nullplayer() + True + sage: letter_game.symmetry() + True + Any Payoff Vector can be passed to the game and these properties can once again be tested: sage: letter_game.payoff_vector = {'A': 0, 'C': 35, 'B': 3} @@ -171,9 +218,13 @@ def __init__(self, characteristic_function, payoff_vector=False): self.player_list = max(characteristic_function.keys(), key=lambda key: len(key)) self.ch_f = characteristic_function + for key in self.ch_f: + sortedkey = tuple(sorted(list(key))) + self.ch_f[sortedkey] = self.ch_f.pop(key) - if sorted([tuple(coalition) for coalition in powerset(self.player_list)]) != sorted(self.ch_f.keys()): - raise ValueError("Characteristic Function must be the power set") + for coalition in powerset(self.player_list): + if tuple(sorted(list(coalition))) not in sorted(self.ch_f.keys()): + raise ValueError("Characteristic Function must be the power set") self.number_players = len(self.player_list) self.payoff_vector = payoff_vector @@ -600,7 +651,8 @@ def is_efficient(self): if not self.payoff_vector: raise ValueError("Game must have a payoff_vector") - return sum(self.payoff_vector.values()) == self.ch_f[self.player_list] + pl = tuple(sorted(list(self.player_list))) + return sum(self.payoff_vector.values()) == self.ch_f[pl] def nullplayer(self): r""" From 000c2a563d73eb2523f321493b607b4cdff196ac Mon Sep 17 00:00:00 2001 From: James Campbell Date: Thu, 5 Jun 2014 18:13:08 +0100 Subject: [PATCH 191/546] replaces show with _repr_ --- src/sage/game_theory/cooperative_game.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index f51be439f7a..316dc8f4b37 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -80,9 +80,9 @@ class CooperativeGame(SageObject): sage: letter_game.is_superadditive() False - The show function will display basic information about the game. :: + The _repr_ function will display basic information about the game. :: - sage: letter_game.show() + sage: letter_game._repr_() A Co-operative Game with 3 players Characteristic Function is ('A',) : 6 @@ -136,7 +136,7 @@ class CooperativeGame(SageObject): True sage: letter_game.is_superadditive() False - sage: letter_game.show() + sage: letter_game._repr_() A Co-operative Game with 3 players Characteristic Function is ('A',) : 6 @@ -521,8 +521,11 @@ def get_predecessors(self, player, permutation): pred = list(permutation[:permutation.index(player)]) return sorted(pred), sorted(pred + [player]) - def show(self): + def _repr_(self): r""" + + Returns a concise description of the Game. + EXAMPLES:: Typical use of the show function with a given Payoff Vector. :: @@ -535,7 +538,7 @@ def show(self): ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} sage: letter_game = CooperativeGame(letter_function, {'A': 14, 'B': 14, 'C': 14}) - sage: letter_game.show() + sage: letter_game._repr_() A Co-operative Game with 3 players Characteristic Function is ('A',) : 6 @@ -552,7 +555,7 @@ def show(self): sage: letter_game.shapley_value() {'A': 2, 'C': 35, 'B': 5} - sage: letter_game.show() + sage: letter_game._repr_() A Co-operative Game with 3 players Characteristic Function is ('A',) : 6 From de4ef370dd307abae62f0a123e3592c2d637a722 Mon Sep 17 00:00:00 2001 From: Jayant Date: Fri, 6 Jun 2014 00:14:20 -0400 Subject: [PATCH 192/546] Added a file matroids+plot_helpers.py and plot() method to abstract matroids class. --- src/sage/matroids/matroids_plot_helpers.py | 169 +++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 src/sage/matroids/matroids_plot_helpers.py diff --git a/src/sage/matroids/matroids_plot_helpers.py b/src/sage/matroids/matroids_plot_helpers.py new file mode 100644 index 00000000000..27f09203bb5 --- /dev/null +++ b/src/sage/matroids/matroids_plot_helpers.py @@ -0,0 +1,169 @@ +import scipy +import scipy.interpolate + +def initial_triangle(M,B1,nB1,lps): + """takes a matroid of rank=3, a basis B1, non-basis elements that are not loops or parallel elements nB1 and loops lps. + returns a dictionary of points with M.rank() elements from B1 and those spanned by any two-subset of basis as keys and + their cartesian co-ordinates in R^2 as values along with non-triangle points and remaining lines that will be (mostly) + drawn curved + """ + tripts=[(0, 0),(1, 2),(2, 0)] + pts={} + j=0 + for i in B1: + pts[i]=tripts[j] + j=j+1 + #nB1=[j for j in gnd if j not in B1] + pairs=[[0, 1],[1,2],[0,2]] + L1=[] + L2=[] + L3=[] + for i in nB1: + if M.is_dependent([i,B1[pairs[0][0]],B1[pairs[0][1]]]): + L1.append(i) + elif M.is_dependent([i,B1[pairs[1][0]],B1[pairs[1][1]]]): + # Add to L2 + L2.append(i) + elif M.is_dependent([i,B1[pairs[2][0]],B1[pairs[2][1]]]): + # Add to L3 + L3.append(i) + L=[L1,L2,L3] + lines=[] #the list of lines + for i in range(1,len(L)+1): + lines.append([B1[pairs[i-1][0]]]) + lines[i-1].extend(L[i-1]) + lines[i-1].extend([B1[pairs[i-1][1]]]) + # place triangle and L1,L2,L3 + for i in L:# loop over megalist + interval=1/(len(i)+1) + pt1=list(tripts[pairs[L.index(i)][0]]) + pt2=list(tripts[pairs[L.index(i)][1]]) + for j in range(1,len(i)+1): + #Loop over L1,L2,L3 + cc= interval*j + pts[i[j-1]]=(cc*pt1[0]+(1-cc)*pt2[0],cc*pt1[1]+(1-cc)*pt2[1]) + trilines=[list(set(x)) for x in lines if len(x)>=3] + curvedlines = [list(set(list(x)).difference(set(lps))) for x in M.flats(2) if set(list(x)) not in trilines if len(list(x))>=3] + nontripts= [i for i in nB1 if i not in pts.keys()] + return pts,trilines,nontripts,curvedlines + +def trigrid(tripts): + """tripts is a list of 3 points(each a 2-tuple) in R^2. returns 4 points that are in the convex hull of tripts + and are arranged in a grid inside the triangle + """ + n=0 + pairs=[[0,1],[1,2],[0,2]] + cpt=list(((tripts[0][0]+tripts[1][0]+tripts[2][0])/3,(tripts[0][1]+tripts[1][1]+tripts[2][1])/3)) + grid=[cpt] + for p in pairs: + pt=list(((tripts[p[0]][0]+tripts[p[1]][0]+cpt[0])/3,(tripts[p[0]][1]+tripts[p[1]][1]+cpt[1])/3)) + grid.append(pt) + return grid + +def addnontripts(M,ptsdict,tripts,nontripts): + pairs=[[0,1],[1,2],[0,2]] + q=[tripts] + num=len(nontripts) + gridpts=[[(tripts[0][0]+tripts[1][0]+tripts[2][0])/3,(tripts[0][1]+tripts[1][1]+tripts[2][1])/3]] + n=0 + while n (float(max(ypts))-float(min(ypts))): + sortedind= sorted(range(len(xpts)), key=lambda k: float(xpts[k])) + else: + sortedind= sorted(range(len(ypts)), key=lambda k: float(ypts[k])) + flip = True + sortedlinepts=[linepts[i] for i in sortedind] + sortedx=[k[0] for k in sortedlinepts] + sortedy=[k[1] for k in sortedlinepts] + else: + linepts=[list(ptsdict[i]) for i in lo] + sortedx=[k[0] for k in linepts] + sortedy=[k[1] for k in linepts] + + if flip==True: + tck,u=scipy.interpolate.splprep([sortedy,sortedx],s=0.0,k=2) + y_i,x_i= scipy.interpolate.splev(np.linspace(0,1,100),tck) + else: + tck,u=scipy.interpolate.splprep([sortedx,sortedy],s=0.0,k=2) + x_i,y_i= scipy.interpolate.splev(np.linspace(0,1,100),tck) + return sortedx,sortedy,x_i,y_i + +def slp(M1): + """Takes a matroid M1 and returns it corresponding simple matroid, loops and parallel elements + """ + L=set(M1.loops()) + sg=sorted(M1.simplify().groundset()) + nP=L|set(M1.simplify().groundset()) + P= set(M1.groundset())-nP + return [M1.delete(L|P),L,P] + +def addlp(G,M,ptsdict,L,P): + """Takes a graphics object G, points dictionary, loops and parallel elements and adds loops and + parallel elements to graphics object and returns the modified graphics object + """ + M1=M.simplify() + # deal with loops + if len(L)>0: + loops=L + looptext=", ".join([str(l) for l in loops]) + rectx=-1 + recty=-1 + rectw= 0.5+0.4*len(loops)+ 0.5 # control this based on len(loops) + recth=0.6 + G+=polygon2d([[rectx,recty], [rectx,recty+recth], [rectx+rectw,recty+recth], [rectx+rectw,recty]], color='black',fill=False,thickness=4) + G+=text(looptext,(rectx+0.5,recty+0.3),color='black',fontsize=13) + G+=point((rectx+0.2, recty+0.3),color='black', size=300,zorder=2) + G+=text('Loop(s)',(rectx+0.5+0.4*len(loops)+0.1,recty+0.3),fontsize=13,color='black') + if len(P)>0: + # create list of lists where inner lists are parallel classes + pcls=[] + gnd=sorted(M1.groundset_list()) + for g in gnd: + pcl=[g] + for p in P: + if M.rank([g,p])==1: + pcl.extend([p]) + pcls.append(pcl) + # add parallel elements to axis object + for pcl in pcls: + if len(pcl)>1: + basept=list(ptsdict[pcl[0]]) + if len(pcl)<=2: + # add side by side + ptsdict[pcl[1]]=(basept[0],basept[1]-0.13) + G+=points(zip([basept[0]], [basept[1]-0.13]),color='black', size=300,zorder=2) + G+=text(pcl[1],(float(basept[0]), float(basept[1])-0.13),color='white',fontsize=13) + else: + # add in a bracket + G+=text('{ '+", ".join(sorted([str(kk) for kk in pcl[1:]]))+' }',(float(basept[0]), float(basept[1]-0.2)-0.034),color='black',fontsize=13) + return G + +def line_hasorder(l,lodrs=None): + if lodrs!=None: + if len(lodrs) > 0: + for i in lodrs: + if Set(i)==Set(l): + return True,i + return False,[] From d1b48a8e5d609b073d54fd7e7b33c5f10a65be68 Mon Sep 17 00:00:00 2001 From: Jayant Date: Fri, 6 Jun 2014 02:00:35 -0400 Subject: [PATCH 193/546] Committed changed matroid.pyx and matroid.pxd which were left out of previous commit. --- src/sage/matroids/matroid.pxd | 3 + src/sage/matroids/matroid.pyx | 13 ++ src/sage/matroids/matroids_plot_helpers.py | 204 ++++++++++++++------- 3 files changed, 150 insertions(+), 70 deletions(-) diff --git a/src/sage/matroids/matroid.pxd b/src/sage/matroids/matroid.pxd index 3daccc324bf..182ee67e7f4 100644 --- a/src/sage/matroids/matroid.pxd +++ b/src/sage/matroids/matroid.pxd @@ -142,3 +142,6 @@ cdef class Matroid(SageObject): cpdef _external(self, B) cpdef tutte_polynomial(self, x=*, y=*) cpdef flat_cover(self) + + # visualization + cpdef plot(self,B=*,lineorders=*) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index 36012494411..ca7e5f352fd 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -4748,3 +4748,16 @@ cdef class Matroid(SageObject): eps = 0.00000001 return [F for F in FF if fsol[F] > 1 - eps] + + cpdef plot(self,B=None,lineorders=None): + """Return a sage graphics object: + that corresponds to the matroid's geometric representation + """ + if self.rank() >3: + return + elif B==None: + B=list(self.basis()) + elif self.rank() != self.rank(B): + return + import matroids_plot_helpers + return matroids_plot_helpers.geomrep(self,B,lineorders) diff --git a/src/sage/matroids/matroids_plot_helpers.py b/src/sage/matroids/matroids_plot_helpers.py index 27f09203bb5..25ac698e728 100644 --- a/src/sage/matroids/matroids_plot_helpers.py +++ b/src/sage/matroids/matroids_plot_helpers.py @@ -1,5 +1,9 @@ import scipy import scipy.interpolate +import numpy as np +import operator +from sage.structure.sage_object import SageObject +from sage.plot.all import Graphics, line, text, polygon2d, point,points def initial_triangle(M,B1,nB1,lps): """takes a matroid of rank=3, a basis B1, non-basis elements that are not loops or parallel elements nB1 and loops lps. @@ -7,19 +11,20 @@ def initial_triangle(M,B1,nB1,lps): their cartesian co-ordinates in R^2 as values along with non-triangle points and remaining lines that will be (mostly) drawn curved """ - tripts=[(0, 0),(1, 2),(2, 0)] - pts={} + tripts = [(0, 0),(1, 2),(2, 0)] + pts = {} j=0 for i in B1: - pts[i]=tripts[j] - j=j+1 + pts[i] = tripts[j] + j = j + 1 #nB1=[j for j in gnd if j not in B1] pairs=[[0, 1],[1,2],[0,2]] - L1=[] - L2=[] - L3=[] + L1 = [] + L2 = [] + L3 = [] for i in nB1: if M.is_dependent([i,B1[pairs[0][0]],B1[pairs[0][1]]]): + # Add to L1 L1.append(i) elif M.is_dependent([i,B1[pairs[1][0]],B1[pairs[1][1]]]): # Add to L2 @@ -27,137 +32,136 @@ def initial_triangle(M,B1,nB1,lps): elif M.is_dependent([i,B1[pairs[2][0]],B1[pairs[2][1]]]): # Add to L3 L3.append(i) - L=[L1,L2,L3] - lines=[] #the list of lines + L = [L1,L2,L3] + lines = [] #the list of lines for i in range(1,len(L)+1): lines.append([B1[pairs[i-1][0]]]) lines[i-1].extend(L[i-1]) lines[i-1].extend([B1[pairs[i-1][1]]]) # place triangle and L1,L2,L3 for i in L:# loop over megalist - interval=1/(len(i)+1) - pt1=list(tripts[pairs[L.index(i)][0]]) - pt2=list(tripts[pairs[L.index(i)][1]]) + interval = 1/float(len(i)+1) + pt1 = list(tripts[pairs[L.index(i)][0]]) + pt2 = list(tripts[pairs[L.index(i)][1]]) for j in range(1,len(i)+1): #Loop over L1,L2,L3 - cc= interval*j - pts[i[j-1]]=(cc*pt1[0]+(1-cc)*pt2[0],cc*pt1[1]+(1-cc)*pt2[1]) - trilines=[list(set(x)) for x in lines if len(x)>=3] + cc = interval*j + pts[i[j-1]] = (cc*pt1[0]+(1-cc)*pt2[0],cc*pt1[1]+(1-cc)*pt2[1]) + trilines = [list(set(x)) for x in lines if len(x)>=3] curvedlines = [list(set(list(x)).difference(set(lps))) for x in M.flats(2) if set(list(x)) not in trilines if len(list(x))>=3] - nontripts= [i for i in nB1 if i not in pts.keys()] + nontripts = [i for i in nB1 if i not in pts.keys()] return pts,trilines,nontripts,curvedlines def trigrid(tripts): """tripts is a list of 3 points(each a 2-tuple) in R^2. returns 4 points that are in the convex hull of tripts and are arranged in a grid inside the triangle """ - n=0 - pairs=[[0,1],[1,2],[0,2]] - cpt=list(((tripts[0][0]+tripts[1][0]+tripts[2][0])/3,(tripts[0][1]+tripts[1][1]+tripts[2][1])/3)) - grid=[cpt] + n = 0 + pairs = [[0,1],[1,2],[0,2]] + cpt = list((float(tripts[0][0]+tripts[1][0]+tripts[2][0])/3,float(tripts[0][1]+tripts[1][1]+tripts[2][1])/3)) + grid = [cpt] for p in pairs: - pt=list(((tripts[p[0]][0]+tripts[p[1]][0]+cpt[0])/3,(tripts[p[0]][1]+tripts[p[1]][1]+cpt[1])/3)) + pt = list((float(tripts[p[0]][0]+tripts[p[1]][0]+cpt[0])/3,float(tripts[p[0]][1]+tripts[p[1]][1]+cpt[1])/3)) grid.append(pt) return grid def addnontripts(M,ptsdict,tripts,nontripts): - pairs=[[0,1],[1,2],[0,2]] - q=[tripts] - num=len(nontripts) - gridpts=[[(tripts[0][0]+tripts[1][0]+tripts[2][0])/3,(tripts[0][1]+tripts[1][1]+tripts[2][1])/3]] + pairs = [[0,1],[1,2],[0,2]] + q = [tripts] + num = len(nontripts) + gridpts = [[float((tripts[0][0]+tripts[1][0]+tripts[2][0])/3),float(tripts[0][1]+tripts[1][1]+tripts[2][1])/3]] n=0 while n (float(max(ypts))-float(min(ypts))): - sortedind= sorted(range(len(xpts)), key=lambda k: float(xpts[k])) + sortedind = sorted(range(len(xpts)), key=lambda k: float(xpts[k])) else: - sortedind= sorted(range(len(ypts)), key=lambda k: float(ypts[k])) + sortedind = sorted(range(len(ypts)), key=lambda k: float(ypts[k])) flip = True - sortedlinepts=[linepts[i] for i in sortedind] - sortedx=[k[0] for k in sortedlinepts] - sortedy=[k[1] for k in sortedlinepts] + sortedlinepts = [linepts[i] for i in sortedind] + sortedx = [k[0] for k in sortedlinepts] + sortedy = [k[1] for k in sortedlinepts] else: - linepts=[list(ptsdict[i]) for i in lo] - sortedx=[k[0] for k in linepts] - sortedy=[k[1] for k in linepts] + linepts = [list(ptsdict[i]) for i in lo] + sortedx = [k[0] for k in linepts] + sortedy = [k[1] for k in linepts] if flip==True: - tck,u=scipy.interpolate.splprep([sortedy,sortedx],s=0.0,k=2) - y_i,x_i= scipy.interpolate.splev(np.linspace(0,1,100),tck) + tck,u = scipy.interpolate.splprep([sortedy,sortedx],s=0.0,k=2) + y_i,x_i = scipy.interpolate.splev(np.linspace(0,1,100),tck) else: - tck,u=scipy.interpolate.splprep([sortedx,sortedy],s=0.0,k=2) - x_i,y_i= scipy.interpolate.splev(np.linspace(0,1,100),tck) + tck,u = scipy.interpolate.splprep([sortedx,sortedy],s=0.0,k=2) + x_i,y_i = scipy.interpolate.splev(np.linspace(0,1,100),tck) return sortedx,sortedy,x_i,y_i def slp(M1): """Takes a matroid M1 and returns it corresponding simple matroid, loops and parallel elements """ - L=set(M1.loops()) - sg=sorted(M1.simplify().groundset()) - nP=L|set(M1.simplify().groundset()) - P= set(M1.groundset())-nP + L = set(M1.loops()) + sg = sorted(M1.simplify().groundset()) + nP = L|set(M1.simplify().groundset()) + P = set(M1.groundset())-nP return [M1.delete(L|P),L,P] def addlp(G,M,ptsdict,L,P): """Takes a graphics object G, points dictionary, loops and parallel elements and adds loops and parallel elements to graphics object and returns the modified graphics object """ - M1=M.simplify() + M1 = M.simplify() # deal with loops if len(L)>0: - loops=L - looptext=", ".join([str(l) for l in loops]) - rectx=-1 - recty=-1 - rectw= 0.5+0.4*len(loops)+ 0.5 # control this based on len(loops) - recth=0.6 - G+=polygon2d([[rectx,recty], [rectx,recty+recth], [rectx+rectw,recty+recth], [rectx+rectw,recty]], color='black',fill=False,thickness=4) - G+=text(looptext,(rectx+0.5,recty+0.3),color='black',fontsize=13) - G+=point((rectx+0.2, recty+0.3),color='black', size=300,zorder=2) - G+=text('Loop(s)',(rectx+0.5+0.4*len(loops)+0.1,recty+0.3),fontsize=13,color='black') + loops = L + looptext = ", ".join([str(l) for l in loops]) + rectx = -1 + recty = -1 + rectw = 0.5 + 0.4*len(loops) + 0.5 # control this based on len(loops) + recth = 0.6 + G += polygon2d([[rectx,recty], [rectx,recty+recth], [rectx+rectw,recty+recth], [rectx+rectw,recty]], color='black',fill=False,thickness=4) + G += text(looptext,(rectx+0.5,recty+0.3),color='black',fontsize=13) + G += point((rectx+0.2, recty+0.3),color='black', size=300,zorder=2) + G += text('Loop(s)',(rectx+0.5+0.4*len(loops)+0.1,recty+0.3),fontsize=13,color='black') if len(P)>0: # create list of lists where inner lists are parallel classes pcls=[] - gnd=sorted(M1.groundset_list()) + gnd = sorted(M1.groundset_list()) for g in gnd: - pcl=[g] + pcl = [g] for p in P: if M.rank([g,p])==1: pcl.extend([p]) pcls.append(pcl) - # add parallel elements to axis object for pcl in pcls: if len(pcl)>1: - basept=list(ptsdict[pcl[0]]) + basept = list(ptsdict[pcl[0]]) if len(pcl)<=2: # add side by side - ptsdict[pcl[1]]=(basept[0],basept[1]-0.13) - G+=points(zip([basept[0]], [basept[1]-0.13]),color='black', size=300,zorder=2) - G+=text(pcl[1],(float(basept[0]), float(basept[1])-0.13),color='white',fontsize=13) + ptsdict[pcl[1]] = (basept[0],basept[1]-0.13) + G += points(zip([basept[0]], [basept[1]-0.13]),color='black', size=300,zorder=2) + G += text(pcl[1],(float(basept[0]), float(basept[1])-0.13),color='white',fontsize=13) else: # add in a bracket - G+=text('{ '+", ".join(sorted([str(kk) for kk in pcl[1:]]))+' }',(float(basept[0]), float(basept[1]-0.2)-0.034),color='black',fontsize=13) + G += text('{ '+", ".join(sorted([str(kk) for kk in pcl[1:]]))+' }',(float(basept[0]), float(basept[1]-0.2)-0.034),color='black',fontsize=13) return G def line_hasorder(l,lodrs=None): @@ -165,5 +169,65 @@ def line_hasorder(l,lodrs=None): if len(lodrs) > 0: for i in lodrs: if Set(i)==Set(l): - return True,i + return True,i return False,[] + +def geomrep(M1,B1=None,lineorders1=None): + G = Graphics() + # create lists of loops and parallel elements and simplify given matroid + [M,L,P] = slp(M1) + if M.rank()==1: + pts = {} + gnd = sorted(M.groundset()) + pts[gnd[0]]=(1,float(2)/3) + G += point((1, float(2)/3),size=300,zorder=2) + pts2 = pts + elif M.rank() == 2: + pts2 = {} + pts2[B1[0]] = (0,0) + pts2[B1[1]] = (2,0) + nB1=list(set(M.groundset_list())-set(B1)) + bline = [] + for j in nB1: + if M.is_dependent([j,B1[0],B1[1]]): + bline.append(j) + interval = len(bline)+1 + lpt = list(pts2[B1[0]]) + rpt = list(pts2[B1[1]]) + for k in range(len(bline)): + cc = (float(1)/interval)*(k+1) + pts2[bline[k]] = (cc*lpt[0]+(1-cc)*rpt[0],cc*lpt[1]+(1-cc)*rpt[1]) + bline.extend(B1) + ptsx,ptsy,x_i,y_i = createline(bline,pts2,lineorders1) + G += line(zip(x_i, y_i),color='black',thickness=3,zorder=1) + allpts = [list(pts2[i]) for i in M.groundset()] + xpts = [float(k[0]) for k in allpts] + ypts = [float(k[1]) for k in allpts] + G += points(zip(xpts, ypts), color='black', size=300,zorder=2) + for i in pts2: + pt = list(pts2[i]) + G += text(i,(float(pt[0]), float(pt[1])), color='white',fontsize=13) + else: + pts,trilines,nontripts,curvedlines = initial_triangle(M1,B1,list(set(M.groundset())-set(B1)), list(set(L)|set(P))) #[i for i in sorted(M.groundset()) if i not in B1]) + pts2= addnontripts(M,pts,[pts[B1[0]],pts[B1[1]],pts[B1[2]]],nontripts) + trilines.extend(curvedlines) + j = 0 + for ll in trilines: + if len(ll)>=3: + ptsx,ptsy,x_i,y_i=createline(ll,pts2,lineorders1) + G += line(zip(x_i,y_i),color='black',thickness=3,zorder=1) + allpts = [list(pts2[i]) for i in M.groundset()] + xpts = [float(k[0]) for k in allpts] + ypts = [float(k[1]) for k in allpts] + G += points(zip(xpts,ypts),color='black', size=300,zorder=2) + for i in pts2: + pt = list(pts2[i]) + G += text(i,(float(pt[0]), float(pt[1])),color='white',fontsize=13) + #deal with loops and parallel elements + G = addlp(G,M1,pts2,L,P) + G.axes(False) + G.xmax(3) + G.ymax(3) + G.xmin = (-2) + G.ymin = (-2) + return G From b34ee393ad0e55dea7b6a384eb7d96ca2d059c11 Mon Sep 17 00:00:00 2001 From: Punarbasu Purkayastha Date: Fri, 6 Jun 2014 16:47:44 +0800 Subject: [PATCH 194/546] allow some trigonometric functions to take complex as input This fixes bugs in arctan, arcsec, arccsc, where complex numbers as input would result in an AttributeError --- src/sage/functions/trig.py | 24 +++++++++++++++++++++--- src/sage/symbolic/function.pyx | 11 ++++++++--- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/sage/functions/trig.py b/src/sage/functions/trig.py index 4860d02e623..7eb76cef565 100644 --- a/src/sage/functions/trig.py +++ b/src/sage/functions/trig.py @@ -601,8 +601,14 @@ def _evalf_(self, x, parent=None, algorithm=None): """ if parent is float: return math.pi/2 - math.atan(x) + from sage.symbolic.constants import pi - return parent(pi/2 - x.arctan()) + try: + return parent(pi/2 - x.arctan()) + except AttributeError: + # Usually this means that x is of type 'complex' + from sage.rings.complex_double import CDF + return complex(pi/2 - CDF(x).arctan()) def _eval_numpy_(self, x): """ @@ -669,7 +675,13 @@ def _evalf_(self, x, parent=None, algorithm=None): """ if parent is float: return math.asin(1/x) - return (1/x).arcsin() + + try: + return (1/x).arcsin() + except AttributeError: + # Usually this means that x is of type 'complex' + from sage.rings.complex_double import CDF + return complex(CDF(1/x).arcsin()) def _eval_numpy_(self, x): """ @@ -731,7 +743,13 @@ def _evalf_(self, x, parent=None, algorithm=None): """ if parent is float: return math.acos(1/x) - return (1/x).arccos() + + try: + return (1/x).arccos() + except AttributeError: + # Usually this means that x is of type 'complex' + from sage.rings.complex_double import CDF + return complex(CDF(1/x).arccos()) def _eval_numpy_(self, x): """ diff --git a/src/sage/symbolic/function.pyx b/src/sage/symbolic/function.pyx index f774812bf62..2a3e37181fb 100644 --- a/src/sage/symbolic/function.pyx +++ b/src/sage/symbolic/function.pyx @@ -934,11 +934,16 @@ cdef class BuiltinFunction(Function): # conversion to the original parent failed # we try if it works with the corresponding complex domain - if org_parent is float: + if org_parent is float or org_parent is complex: try: - return complex(res) + from sage.rings.real_double import RDF + return org_parent(RDF(res)) except (TypeError, ValueError): - pass + try: + from sage.rings.complex_double import CDF + return org_parent(CDF(res)) + except (TypeError, ValueError): + pass elif hasattr(org_parent, 'complex_field'): try: return org_parent.complex_field()(res) From c4ba30d802f932b29205ec3f49f3eeeeb0942e76 Mon Sep 17 00:00:00 2001 From: Punarbasu Purkayastha Date: Fri, 6 Jun 2014 17:06:33 +0800 Subject: [PATCH 195/546] add couple of doctests for complex input --- src/sage/functions/trig.py | 57 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/sage/functions/trig.py b/src/sage/functions/trig.py index 7eb76cef565..020c560f19c 100644 --- a/src/sage/functions/trig.py +++ b/src/sage/functions/trig.py @@ -36,6 +36,9 @@ def __init__(self): sage: conjugate(sin(x)) sin(conjugate(x)) + sage: sin(complex(1,1)) + (1.2984575814159773+0.6349639147847361j) + """ GinacFunction.__init__(self, "sin", latex_name=r"\sin", conversions=dict(maxima='sin',mathematica='Sin')) @@ -73,6 +76,9 @@ def __init__(self): sage: conjugate(cos(x)) cos(conjugate(x)) + sage: cos(complex(1,1)) + (0.8337300251311491-0.9888977057628651j) + """ GinacFunction.__init__(self, "cos", latex_name=r"\cos", conversions=dict(maxima='cos',mathematica='Cos')) @@ -114,6 +120,9 @@ def __init__(self): sage: conjugate(tan(x)) tan(conjugate(x)) + sage: tan(complex(1,1)) + (0.2717525853195118+1.0839233273386946j) + """ GinacFunction.__init__(self, "tan", latex_name=r"\tan") @@ -162,6 +171,14 @@ def _evalf_(self, x, parent=None, algorithm=None): 1.4142135623730950488016887242 sage: float(sec(pi/4)) 1.4142135623730951 + + TESTS: + + Test complex input:: + + sage: sec(complex(1,1)) + (0.49833703055518686+0.5910838417210451j) + """ if parent is float: return 1/math.cos(x) @@ -253,6 +270,14 @@ def _evalf_(self, x, parent=None, algorithm=None): 1.4142135623730950488016887242 sage: float(csc(pi/4)) 1.4142135623730951 + + TESTS: + + Test complex input:: + + sage: csc(complex(1,1)) + (0.6215180171704284-0.30393100162842646j) + """ if parent is float: return 1/math.sin(x) @@ -373,6 +398,14 @@ def _evalf_(self, x, parent=None, algorithm=None): 1.0000000000000000000000000000 sage: float(cot(1)) 0.64209261593433... + + TESTS: + + Test complex input:: + + sage: cot(complex(1,1)) + (0.21762156185440273-0.8680141428959249j) + """ if parent is float: return 1/math.tan(x) @@ -598,6 +631,14 @@ def _evalf_(self, x, parent=None, algorithm=None): 1.1071487177940905030170654602 sage: float(arccot(1/2)) 1.1071487177940904 + + TESTS: + + Test complex input:: + + sage: arccot(complex(1,1)) + (0.5535743588970452-0.4023594781085251j) + """ if parent is float: return math.pi/2 - math.atan(x) @@ -672,6 +713,14 @@ def _evalf_(self, x, parent=None, algorithm=None): 0.52359877559829887307710723055 sage: float(arccsc(2)) 0.52359877559829... + + TESTS: + + Test complex input:: + + sage: arccsc(complex(1,1)) + (0.45227844715119064-0.5306375309525178j) + """ if parent is float: return math.asin(1/x) @@ -740,6 +789,14 @@ def _evalf_(self, x, parent=None, algorithm=None): sage: arcsec(2).n(100) 1.0471975511965977461542144611 + + TESTS: + + Test complex input:: + + sage: arcsec(complex(1,1)) + (1.118517879643706+0.5306375309525178j) + """ if parent is float: return math.acos(1/x) From 148aafbc155f5fceb9f156a725067e1b9f5c5436 Mon Sep 17 00:00:00 2001 From: Punarbasu Purkayastha Date: Fri, 6 Jun 2014 17:29:17 +0800 Subject: [PATCH 196/546] add more doctests for auto exclusion functionality --- src/sage/plot/plot.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index 273a4858c76..8484cfb00d4 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -1062,6 +1062,12 @@ def plot(funcs, *args, **kwds): sage: plot(arcsec, (x, -2, 2), exclude=[1.5]) # x=1.5 is also excluded + sage: plot(arcsec(x/2), -2, 2) # plot should be empty; no valid points + + sage: plot(sqrt(x^2-1), -2, 2) # [-1, 1] is excluded automatically + + sage: plot(arccsc, -2, 2) # [-1, 1] is excluded automatically + sage: set_verbose(0) TESTS: From 66c066f1ad0336ed6acbd2fa33fa0ef65d827457 Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 6 Jun 2014 10:53:31 +0100 Subject: [PATCH 197/546] Tweaked _repr_ to be more concise, docs might need checking --- src/sage/game_theory/cooperative_game.py | 72 +++--------------------- 1 file changed, 8 insertions(+), 64 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 316dc8f4b37..fd8667eb61d 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -80,20 +80,10 @@ class CooperativeGame(SageObject): sage: letter_game.is_superadditive() False - The _repr_ function will display basic information about the game. :: - - sage: letter_game._repr_() - A Co-operative Game with 3 players - Characteristic Function is - ('A',) : 6 - ('B', 'C') : 42 - () : 0 - ('C',) : 42 - ('A', 'B') : 12 - ('B',) : 12 - ('A', 'C') : 42 - ('A', 'B', 'C') : 42 - Payoff vector is {'A': 2, 'C': 35, 'B': 5} + Instances have a basic representation that will display basic information about the game. :: + + sage: letter_game + A 3 player Co-operative Game. We can test 3 basic properties of the Payoff Vector. They are * Is it is efficient? @@ -136,18 +126,6 @@ class CooperativeGame(SageObject): True sage: letter_game.is_superadditive() False - sage: letter_game._repr_() - A Co-operative Game with 3 players - Characteristic Function is - ('A',) : 6 - ('B', 'C') : 42 - () : 0 - ('C',) : 42 - ('A', 'B') : 12 - ('B',) : 12 - ('A', 'B', 'C') : 42 - ('A', 'C') : 42 - Payoff vector is {'A': 2, 'C': 35, 'B': 5} sage: letter_game.is_efficient() True sage: letter_game.nullplayer() @@ -527,7 +505,7 @@ def _repr_(self): Returns a concise description of the Game. EXAMPLES:: - Typical use of the show function with a given Payoff Vector. :: + Basic description of the game shown when calling the game instance. :: sage: letter_function = {(): 0, ....: ('A',): 6, @@ -538,47 +516,13 @@ def _repr_(self): ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} sage: letter_game = CooperativeGame(letter_function, {'A': 14, 'B': 14, 'C': 14}) - sage: letter_game._repr_() - A Co-operative Game with 3 players - Characteristic Function is - ('A',) : 6 - ('B', 'C') : 42 - () : 0 - ('C',) : 42 - ('A', 'B') : 12 - ('B',) : 12 - ('A', 'C') : 42 - ('A', 'B', 'C') : 42 - Payoff vector is {'A': 14, 'C': 14, 'B': 14} - - Typical use of the show function after calculating the Shapley value. :: - - sage: letter_game.shapley_value() - {'A': 2, 'C': 35, 'B': 5} - sage: letter_game._repr_() - A Co-operative Game with 3 players - Characteristic Function is - ('A',) : 6 - ('B', 'C') : 42 - () : 0 - ('C',) : 42 - ('A', 'B') : 12 - ('B',) : 12 - ('A', 'C') : 42 - ('A', 'B', 'C') : 42 - Payoff vector is {'A': 2, 'C': 35, 'B': 5} + sage: letter_game + A 3 player Co-operative Game. """ np = self.number_players cf = self.ch_f pv = self.payoff_vector - print "A Co-operative Game with %s players" % np - print "Characteristic Function is" - for key in cf: - print "\t %s : %s" %(key, cf[key]) - if pv is False: - print "And it has no payoff vector" - else: - print "Payoff vector is %s" % pv + return "A %s player Co-operative Game." % np def is_efficient(self): r""" From 036a08256ddf14c01e9b318a9e591d97c19db962 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Fri, 6 Jun 2014 11:09:28 +0100 Subject: [PATCH 198/546] makes minor changes to docs --- src/sage/game_theory/cooperative_game.py | 36 ++++++++++++++++-------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index fd8667eb61d..e941155f9f7 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -42,7 +42,8 @@ class CooperativeGame(SageObject): - payoff_vector - default = ``False``, a dictionary can be passed instead but this will be overwritten if shapley_value is called. - EXAMPLES:: + EXAMPLES: + Basic example of how to implement a co-operative game. These functions will be used repeatedly in other examples. :: @@ -108,7 +109,8 @@ class CooperativeGame(SageObject): sage: letter_game.symmetry() True - TESTS:: + TESTS: + Checks that the order within a key does not affect other functions. :: sage: letter_function = {(): 0, @@ -211,7 +213,8 @@ def shapley_value(self): r""" Return the payoff vector for co-operative game. - EXAMPLES:: + EXAMPLES: + A typical example of the use of shapley_value. :: sage: integer_function = {(): 0, @@ -266,7 +269,8 @@ def is_monotone(self): r""" Returns True if co-operative game is monotonic. - EXAMPLES:: + EXAMPLES: + Shows the use of is_monotone on a simple game that is monotone. :: sage: integer_function = {(): 0, @@ -325,7 +329,8 @@ def is_superadditive(self): r""" Returns True if co-operative game is superadditive. - EXAMPLES:: + EXAMPLES: + An example that returns False. :: sage: integer_function = {(): 0, @@ -504,7 +509,8 @@ def _repr_(self): Returns a concise description of the Game. - EXAMPLES:: + EXAMPLES: + Basic description of the game shown when calling the game instance. :: sage: letter_function = {(): 0, @@ -528,7 +534,8 @@ def is_efficient(self): r""" Returns True if the current payoff_vector is efficient. - EXAMPLES:: + EXAMPLES: + An efficient payoff_vector.:: sage: letter_function = {(): 0, @@ -578,7 +585,8 @@ def is_efficient(self): sage: long_game.is_efficient() True - TESTS:: + TESTS: + Checks that a game has a payoff_vector. :: sage: A_function = {(): 0, @@ -606,7 +614,8 @@ def nullplayer(self): Returns True if the current payoff_vector possesses the null player property. - EXAMPLES:: + EXAMPLES: + A payoff_vector that returns True. :: sage: letter_function = {(): 0, @@ -657,7 +666,8 @@ def nullplayer(self): sage: long_game.nullplayer() True - TESTS:: + TESTS: + Checks that a game has a payoff_vector. :: sage: A_function = {(): 0, @@ -705,7 +715,8 @@ def symmetry(self): r""" Returns True if the current payoff_vector possesses the symmetry property. - EXAMPLES:: + EXAMPLES: + A Payoff Vector that returns True. :: sage: letter_function = {(): 0, @@ -756,7 +767,8 @@ def symmetry(self): sage: long_game.symmetry() True - TESTS:: + TESTS: + Checks that a game has a payoff_vector. :: sage: A_function = {(): 0, From 977f3d9f95558c7a08bfe68c6be0947c4f0a9170 Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 6 Jun 2014 11:21:58 +0100 Subject: [PATCH 199/546] Incorporated _latex_ method --- src/sage/game_theory/cooperative_game.py | 46 ++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index fd8667eb61d..fc6e42a904a 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -25,6 +25,7 @@ from itertools import chain, permutations, combinations from sage.structure.sage_object import SageObject from sage.misc.misc import powerset +from sage.misc.latex import latex class CooperativeGame(SageObject): @@ -524,6 +525,51 @@ def _repr_(self): pv = self.payoff_vector return "A %s player Co-operative Game." % np + def _latex_(self): + r""" + + Returns the LaTeX code representing the characteristic function. + + EXAMPLES:: + Basic description of the game shown when calling the game instance. :: + + sage: letter_function = {(): 0, + ....: ('A',): 6, + ....: ('B',): 12, + ....: ('C',): 42, + ....: ('A', 'B',): 12, + ....: ('A', 'C',): 42, + ....: ('B', 'C',): 42, + ....: ('A', 'B', 'C',): 42} + sage: letter_game = CooperativeGame(letter_function, {'A': 14, 'B': 14, 'C': 14}) + sage: latex(letter_game) + v(c) = \begin{cases} + 0,&\text{ if }c=\emptyset\\ + 6,&\text{ if }c=\{A\}\\ + 42,&\text{ if }c=\{C\}\\ + 12,&\text{ if }c=\{B\}\\ + 42,&\text{ if }c=\{B, C\}\\ + 12,&\text{ if }c=\{A, B\}\\ + 42,&\text{ if }c=\{A, C\}\\ + 42,&\text{ if }c=\{A, B, C\}\\ + \end{cases} + """ + np = self.number_players + cf = self.ch_f + pv = self.payoff_vector + output = "v(c) = \\begin{cases}\n" + for key in sorted(cf.keys(), key=lambda key: len(key)) : + if key == (): + coalition = "\\emptyset" + else: + coalition = "\{" + for player in key[:-1]: + coalition += "%s, " % player + coalition += "%s\}" % key[-1] + output += "%s,&\\text{ if }c=%s\\\\\n" % (cf[key], coalition) + output += "\\end{cases}" + return output + def is_efficient(self): r""" Returns True if the current payoff_vector is efficient. From 9fb339f5d1c8b7f3325c4235df6b82984aae4d09 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Fri, 6 Jun 2014 12:29:40 +0100 Subject: [PATCH 200/546] converts singular into a one element tuple --- src/sage/game_theory/cooperative_game.py | 28 +++++++++++++++++------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index e941155f9f7..6e2a4516ece 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -159,9 +159,8 @@ def __init__(self, characteristic_function, payoff_vector=False): ... TypeError: Characteristic function must be a dictionary - This test checks that an error is raised when a key in the - Characteristic Function is not a tuple. It fails because `(1)` - is not a tuple, `(1,)` is however. :: + This test checks that an incorrectly entered singularly tuple will be + changed into a tuple. In this case `(1)` becomes `(1,)`. :: sage: tuple_function = {(): 0, ....: (1): 6, @@ -172,6 +171,18 @@ def __init__(self, characteristic_function, payoff_vector=False): ....: (2, 3,): 42, ....: (1, 2, 3,): 42} sage: tuple_game = CooperativeGame(tuple_function) + + This test checks that if a key is not a tuple an error is raised. :: + + sage: error_function = {(): 0, + ....: (1,): 6, + ....: (2,): 12, + ....: (3,): 42, + ....: 12: 12, + ....: (1, 3,): 42, + ....: (2, 3,): 42, + ....: (1, 2, 3,): 42} + sage: error_game = CooperativeGame(error_function) Traceback (most recent call last): ... TypeError: Key must be a tuple @@ -192,16 +203,17 @@ def __init__(self, characteristic_function, payoff_vector=False): if type(characteristic_function) is not dict: raise TypeError("Characteristic function must be a dictionary") - for key in characteristic_function: - if type(key) is not tuple: - raise TypeError("Key must be a tuple") - - self.player_list = max(characteristic_function.keys(), key=lambda key: len(key)) self.ch_f = characteristic_function + for key in self.ch_f: + if len(str(key)) == 1 and type(key) is not tuple: + self.ch_f[key, ] = self.ch_f.pop(key) + elif type(key) is not tuple: + raise TypeError("Key must be a tuple") for key in self.ch_f: sortedkey = tuple(sorted(list(key))) self.ch_f[sortedkey] = self.ch_f.pop(key) + self.player_list = max(characteristic_function.keys(), key=lambda key: len(key)) for coalition in powerset(self.player_list): if tuple(sorted(list(coalition))) not in sorted(self.ch_f.keys()): raise ValueError("Characteristic Function must be the power set") From 56a56399a528a811e90905349a3c0323e30f69d8 Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 6 Jun 2014 12:30:28 +0100 Subject: [PATCH 201/546] Added more discussion about cooperative game and cf --- src/sage/game_theory/cooperative_game.py | 27 ++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 773742b7511..22cda887859 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -23,9 +23,9 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from itertools import chain, permutations, combinations -from sage.structure.sage_object import SageObject -from sage.misc.misc import powerset from sage.misc.latex import latex +from sage.misc.misc import powerset +from sage.structure.sage_object import SageObject class CooperativeGame(SageObject): @@ -45,6 +45,29 @@ class CooperativeGame(SageObject): EXAMPLES: + The type of game that is currently implemented is referred to as a Characteristic Function Game. + This is a game on a set $\omega$ of players that is defined by a value function $v:C\to \mathbb{R}$ + where $C=2^{\Omega}$ is set of all coalitions of players. + An example of such a game is shown below: + + $$ + v(c) = \begin{cases} + 0,&\text{ if }c=\emptyset\\ + 6,&\text{ if }c=\{1\}\\ + 12,&\text{ if }c=\{2\}\\ + 42,&\text{ if }c=\{3\}\\ + 12,&\text{ if }c=\{1,2\}\\ + 42,&\text{ if }c=\{1,3\}\\ + 42,&\text{ if }c=\{2,3\}\\ + 42,&\text{ if }c=\{1, 2,3\}\\ + \end{cases} + $$ + + The function $v$ can be thought of as as a record of contribution of individuals + and coalitions of individuals. + Of interest, becomes how to fairly share the value of the grand coalition ($\omega$)? + This class allows for such an answer to be formulated by calculating the Shapley value of the game. + Basic example of how to implement a co-operative game. These functions will be used repeatedly in other examples. :: From bc38a21f7110493c9854913f76fde880505eb850 Mon Sep 17 00:00:00 2001 From: Jayant Date: Fri, 6 Jun 2014 07:35:10 -0400 Subject: [PATCH 202/546] Added documentation to plot() and show() methods of the abstract matroids class. Added documentation (except examples) for functions defined in src/sage/matroids/matroids_plot_helpers.py --- src/sage/matroids/matroid.pxd | 1 + src/sage/matroids/matroid.pyx | 63 +++++- src/sage/matroids/matroids_plot_helpers.py | 213 +++++++++++++++++++-- 3 files changed, 251 insertions(+), 26 deletions(-) diff --git a/src/sage/matroids/matroid.pxd b/src/sage/matroids/matroid.pxd index 182ee67e7f4..98e9bbb3ae5 100644 --- a/src/sage/matroids/matroid.pxd +++ b/src/sage/matroids/matroid.pxd @@ -145,3 +145,4 @@ cdef class Matroid(SageObject): # visualization cpdef plot(self,B=*,lineorders=*) + cpdef show(self,B=*,lineorders=*) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index ca7e5f352fd..9bffb378251 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -4750,14 +4750,67 @@ cdef class Matroid(SageObject): return [F for F in FF if fsol[F] > 1 - eps] cpdef plot(self,B=None,lineorders=None): - """Return a sage graphics object: - that corresponds to the matroid's geometric representation """ - if self.rank() >3: + Return geomrtric representation as a sage graphics object. + + INPUT: + + - ``B`` -- (optional) a list containing elements of the groundset + not in any particular order. + - ``lineorders`` -- (optional) A list of lists where each of the inner lists + specify ground set elements in a certain order which will be used to draw the + corresponding line in geometric representation (if it exists). + + OUTPUT: + + A sage graphics object of type that + corresponds to the geometric representation of the matroid + + EXAMPLES:: + + sage: M=matroids.named_matroids.Fano() + sage: G=M.plot() + sage: type(G) + + sage: G.show() + + """ + if self.rank() > 3: return - elif B==None: - B=list(self.basis()) + elif B == None: + B = list(self.basis()) elif self.rank() != self.rank(B): return import matroids_plot_helpers return matroids_plot_helpers.geomrep(self,B,lineorders) + + cpdef show(self,B=None,lineorders=None): + """ + Show the geometric representation of the matroid. + + INPUT: + + - ``B`` -- (optional) a list containing elements of the groundset + not in any particular order. + - ``lineorders`` -- (optional) A list of lists where each of the inner lists + specify ground set elements in a certain order which will be used to draw the + corresponding line in geometric representation (if it exists). + + EXAMPLES:: + + sage: M=matroids.named_matroids.TernaryDowling3() + sage: M.show(B=['a','b','c']) + sage: M.show(B=['a','b','c'],lineorders=[['f','e','i']]) + + """ + if self.rank() > 3: + return + elif B == None: + B = list(self.basis()) + elif self.rank() != self.rank(B): + return + B1=B + lineorders1=lineorders + G=self.plot(B1,lineorders1) + G.show(xmin=-2, xmax=3, ymin=-2, ymax=3) + return diff --git a/src/sage/matroids/matroids_plot_helpers.py b/src/sage/matroids/matroids_plot_helpers.py index 25ac698e728..01b86f04ad9 100644 --- a/src/sage/matroids/matroids_plot_helpers.py +++ b/src/sage/matroids/matroids_plot_helpers.py @@ -1,15 +1,60 @@ +r""" +Helper functions for plotting the geometric representation of matroids + + +AUTHORS: + +- Jayant Apte (2014-06-06): initial version + + +EXAMPLES:: + +""" + +#***************************************************************************** +# Copyright (C) 2013 Jayant Apte +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + + import scipy import scipy.interpolate import numpy as np import operator from sage.structure.sage_object import SageObject from sage.plot.all import Graphics, line, text, polygon2d, point,points +from sage.sets.set import Set def initial_triangle(M,B1,nB1,lps): - """takes a matroid of rank=3, a basis B1, non-basis elements that are not loops or parallel elements nB1 and loops lps. - returns a dictionary of points with M.rank() elements from B1 and those spanned by any two-subset of basis as keys and - their cartesian co-ordinates in R^2 as values along with non-triangle points and remaining lines that will be (mostly) - drawn curved + """ + Return points on and off the triangle and lines to be drawn for a rank 3 matroid + + INPUT: + + - ``M`` -- A matroid. + - ``B1``-- A list of labels of groundset elements of M that corresponds to a basis of matroid + returned by ``M.simplify()``. + - ``nB1``-- A list of labes of elements in the ground set of M that corresponds to ``M.simplify.groundset() \ B1``. + - ``lps``-- A list of labels of elements in the ground set of matroid M that are loops. + + OUTPUT: + + A tuple containing 4 elements in this order: + + 1. A dictionary containing 2-tuple (x,y) co-ordinates with ``M.simplify.groundset()`` elements that can be placed on the sides of the triangle as keys + 2. A list of 3 lists of elements of ``M.simplify.groundset()`` that can be placed on the 3 sides of the triangle + 3. A list of elements of `M.simplify.groundset()`` that cane be placed inside the triangle in the geometric representation + 4. A list of lists of elements of ``M.simplify.groundset()`` that correspond to lines in the geometric representation other than the sides of the triangle + + .. NOTE:: + + This method does NOT do any checks. + """ tripts = [(0, 0),(1, 2),(2, 0)] pts = {} @@ -53,8 +98,25 @@ def initial_triangle(M,B1,nB1,lps): return pts,trilines,nontripts,curvedlines def trigrid(tripts): - """tripts is a list of 3 points(each a 2-tuple) in R^2. returns 4 points that are in the convex hull of tripts - and are arranged in a grid inside the triangle + """ + Return a grid of 4 points inside given 3 points as a list + + + INPUT: + + - ``tripts`` -- A list of 3 lists of the form [x,y] where x and y are the cartesian co-ordinates of a point + + OUTPUT: + + A list of lists containing 4 points in following order: + + 1. Barycenter of 3 input points + 2,3,4. Barycenters of 1. with 3 different 2-subsets of input points respectively + + .. NOTE:: + + This method does NOT do any checks. + """ n = 0 pairs = [[0,1],[1,2],[0,2]] @@ -65,7 +127,27 @@ def trigrid(tripts): grid.append(pt) return grid -def addnontripts(M,ptsdict,tripts,nontripts): +def addnontripts(M,tripts,ptsdict,nontripts): + """ + Return a grid of 4 points inside given 3 points as a list + + + INPUT: + + - ``M`` -- A matroid. + - ``tripts`` -- A list of labels in ``M.simplify().groundset()`` that can be placed on the sides of the triangle + - ``ptsdict`` -- A dictionary containing labels in ``tripts`` as keys and their (x,y) position as values + - ``nontripts``-- A list of labels in ``M.simplify().groundset()`` that can be placed inside the triangle + + OUTPUT: + + A dictionary containing labels in ``tripts`` as keys and their (x,y) position as values allong with all keys and respective values in ``ptsdict`` + + .. NOTE:: + + This method does NOT do any checks. + + """ pairs = [[0,1],[1,2],[0,2]] q = [tripts] num = len(nontripts) @@ -84,9 +166,30 @@ def addnontripts(M,ptsdict,tripts,nontripts): j = j + 1 return ptsdict -def createline(ll,ptsdict,lineorders2=None): +def createline(ptsdict,ll,lineorders2=None): """ - Flips x-y if necessary and returns x,y, x_i, y_i lists to be passed to plot function + Return ordered lists of co-ordinates of points to be traversed to draw a 2D line + INPUT: + + - ``ptsdict`` -- A dictionary containing keys and their (x,y) position as values. + - ``ll`` -- A list of keys in ``ptsdict`` through which a line is to be drawn. + - ``lineorders2``-- (optional) A list of ordered lists of keys in ``ptsdict`` such that + if ll is setwise same as any of these then points corresponding to values of the keys + will be traversed in that order thus overriding internal order deciding heuristic. + + OUTPUT: + + A tuple containing 4 elements in this order: + + 1. Ordered list of x-coordinates of values of keys in ``ll`` specified in ptsdict + 2. Ordered list of y-coordinates of values of keys in ``ll`` specified in ptsdict + 3. Ordered list of interpolated x-coordinates of points through which a line can be drawn + 4. Ordered list of interpolated y-coordinates of points through which a line can be drawn + + .. NOTE:: + + This method does NOT do any checks. + """ x,lo = line_hasorder(ll,lineorders2) flip = False @@ -116,7 +219,24 @@ def createline(ll,ptsdict,lineorders2=None): return sortedx,sortedy,x_i,y_i def slp(M1): - """Takes a matroid M1 and returns it corresponding simple matroid, loops and parallel elements + """ + Return simple matroid, loops and parallel elements of given matroid + + INPUT: + + - ``M1`` -- A matroid. + + OUTPUT: + + A tuple containing 3 elements in this order: + 1. Simple matroid corresponding to M1 + 2. Loops of matroid ``M1`` + 3. Elements that are in `M1.groundset()` but not in ground set of 1. or in 2. + + .. NOTE:: + + This method does NOT do any checks. + """ L = set(M1.loops()) sg = sorted(M1.simplify().groundset()) @@ -124,10 +244,28 @@ def slp(M1): P = set(M1.groundset())-nP return [M1.delete(L|P),L,P] -def addlp(G,M,ptsdict,L,P): - """Takes a graphics object G, points dictionary, loops and parallel elements and adds loops and - parallel elements to graphics object and returns the modified graphics object +def addlp(M,L,P,ptsdict,G=None): """ + Return a graphics object containing loops (in inset) and parallel elements of matroid + + INPUT: + + - ``M`` -- A matroid. + - ``L`` -- List of labels of elements in ``M.groundset()`` that are loops of matroid ``M`` + - ``P`` -- List of labels of elements in ``M.groundset()`` not in ``M.simplify.groundset()`` or ``L`` + - ``ptsdict`` -- A dictionary containing elements in ``M.groundset()`` not necessarily containing elements of ``L`` + - ``G`` -- (optional) A sage graphics object to which loops and parallel elements of matroid `M` added + + OUTPUT: + A sage graphics object containing loops and parallel elements of matroid ``M`` + + .. NOTE:: + + This method does NOT do any checks. + + """ + if G == None: + G=Graphics() M1 = M.simplify() # deal with loops if len(L)>0: @@ -165,6 +303,25 @@ def addlp(G,M,ptsdict,L,P): return G def line_hasorder(l,lodrs=None): + """ + Determine if and order is specified for a line + + INPUT: + + - ``l`` -- A line containing specified as a list of labels + - ``lordrs`` -- (optional) A list of lists each specifying an order on the subset of labels corresponding to that list + + OUTPUT: + + A tuple containing 3 elements in this order: + 1. A boolean indicating whether there is any list in ``lordrs`` that is setwise equal to ``l`` + 2. A list specifying an order on ``set(l)`` if 1. is True, otherwise an empty list + + .. NOTE:: + + This method does NOT do any checks. + + """ if lodrs!=None: if len(lodrs) > 0: for i in lodrs: @@ -173,6 +330,24 @@ def line_hasorder(l,lodrs=None): return False,[] def geomrep(M1,B1=None,lineorders1=None): + """ + Return a sage graphics object containing geometric representation of matroid M1 + + INPUT: + + - ``M1`` -- A matroid. + - ``B1`` -- (optional) A list of labels in ``M1.groundset()`` that correspond to a basis + of ``M1`` and will be placed as vertices of the triangle in the geometric representation of ``M1`` + - ``lineorders1`` -- (optional) A list of ordered lists of elements of ``M1.grondset()`` such that + if a line in geometric representation is setwise same as any of these then points contained will be + traversed in that order thus overriding internal order deciding heuristic. + + OUTPUT: + + A sage graphics object of type that + corresponds to the geometric representation of the matroid + + """ G = Graphics() # create lists of loops and parallel elements and simplify given matroid [M,L,P] = slp(M1) @@ -198,7 +373,7 @@ def geomrep(M1,B1=None,lineorders1=None): cc = (float(1)/interval)*(k+1) pts2[bline[k]] = (cc*lpt[0]+(1-cc)*rpt[0],cc*lpt[1]+(1-cc)*rpt[1]) bline.extend(B1) - ptsx,ptsy,x_i,y_i = createline(bline,pts2,lineorders1) + ptsx,ptsy,x_i,y_i = createline(pts2,bline,lineorders1) G += line(zip(x_i, y_i),color='black',thickness=3,zorder=1) allpts = [list(pts2[i]) for i in M.groundset()] xpts = [float(k[0]) for k in allpts] @@ -209,12 +384,12 @@ def geomrep(M1,B1=None,lineorders1=None): G += text(i,(float(pt[0]), float(pt[1])), color='white',fontsize=13) else: pts,trilines,nontripts,curvedlines = initial_triangle(M1,B1,list(set(M.groundset())-set(B1)), list(set(L)|set(P))) #[i for i in sorted(M.groundset()) if i not in B1]) - pts2= addnontripts(M,pts,[pts[B1[0]],pts[B1[1]],pts[B1[2]]],nontripts) + pts2= addnontripts(M,[pts[B1[0]],pts[B1[1]],pts[B1[2]]],pts,nontripts) trilines.extend(curvedlines) j = 0 for ll in trilines: if len(ll)>=3: - ptsx,ptsy,x_i,y_i=createline(ll,pts2,lineorders1) + ptsx,ptsy,x_i,y_i=createline(pts2,ll,lineorders1) G += line(zip(x_i,y_i),color='black',thickness=3,zorder=1) allpts = [list(pts2[i]) for i in M.groundset()] xpts = [float(k[0]) for k in allpts] @@ -224,10 +399,6 @@ def geomrep(M1,B1=None,lineorders1=None): pt = list(pts2[i]) G += text(i,(float(pt[0]), float(pt[1])),color='white',fontsize=13) #deal with loops and parallel elements - G = addlp(G,M1,pts2,L,P) + G = addlp(M1,L,P,pts2,G) G.axes(False) - G.xmax(3) - G.ymax(3) - G.xmin = (-2) - G.ymin = (-2) return G From b56cc83f1236f66f4c80fa456941710b490a19f8 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Fri, 6 Jun 2014 12:59:28 +0100 Subject: [PATCH 203/546] removes payoff_vector and updates docs --- src/sage/game_theory/cooperative_game.py | 170 ++++++++--------------- 1 file changed, 59 insertions(+), 111 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 754244c28d5..0490721373b 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -116,21 +116,23 @@ class CooperativeGame(SageObject): * Does it possess the symmetry property? :: - sage: letter_game.is_efficient() + sage: letter_game.shapley_value() + {'A': 2, 'C': 35, 'B': 5} + sage: letter_game.is_efficient({'A': 2, 'C': 35, 'B': 5}) True - sage: letter_game.nullplayer() + sage: letter_game.nullplayer({'A': 2, 'C': 35, 'B': 5}) True - sage: letter_game.symmetry() + sage: letter_game.symmetry({'A': 2, 'C': 35, 'B': 5}) True Any Payoff Vector can be passed to the game and these properties can once again be tested: sage: letter_game.payoff_vector = {'A': 0, 'C': 35, 'B': 3} - sage: letter_game.is_efficient() + sage: letter_game.is_efficient({'A': 0, 'C': 35, 'B': 3}) False - sage: letter_game.nullplayer() + sage: letter_game.nullplayer({'A': 0, 'C': 35, 'B': 3}) True - sage: letter_game.symmetry() + sage: letter_game.symmetry({'A': 0, 'C': 35, 'B': 3}) True TESTS: @@ -152,25 +154,24 @@ class CooperativeGame(SageObject): True sage: letter_game.is_superadditive() False - sage: letter_game.is_efficient() + sage: letter_game.is_efficient({'A': 2, 'C': 35, 'B': 5}) True - sage: letter_game.nullplayer() + sage: letter_game.nullplayer({'A': 2, 'C': 35, 'B': 5}) True - sage: letter_game.symmetry() + sage: letter_game.symmetry({'A': 2, 'C': 35, 'B': 5}) True Any Payoff Vector can be passed to the game and these properties can once again be tested: - sage: letter_game.payoff_vector = {'A': 0, 'C': 35, 'B': 3} - sage: letter_game.is_efficient() + sage: letter_game.is_efficient({'A': 0, 'C': 35, 'B': 3}) False - sage: letter_game.nullplayer() + sage: letter_game.nullplayer({'A': 0, 'C': 35, 'B': 3}) True - sage: letter_game.symmetry() + sage: letter_game.symmetry({'A': 0, 'C': 35, 'B': 3}) True """ - def __init__(self, characteristic_function, payoff_vector=False): + def __init__(self, characteristic_function): r""" Initializes a co-operative game and checks the inputs. @@ -243,7 +244,6 @@ def __init__(self, characteristic_function, payoff_vector=False): raise ValueError("Characteristic Function must be the power set") self.number_players = len(self.player_list) - self.payoff_vector = payoff_vector def shapley_value(self): r""" @@ -264,8 +264,6 @@ def shapley_value(self): sage: integer_game = CooperativeGame(integer_function) sage: integer_game.player_list (1, 2, 3) - sage: integer_game.payoff_vector - False sage: integer_game.shapley_value() {1: 2, 2: 5, 3: 35} sage: integer_game.payoff_vector @@ -557,13 +555,11 @@ def _repr_(self): ....: ('A', 'C',): 42, ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} - sage: letter_game = CooperativeGame(letter_function, {'A': 14, 'B': 14, 'C': 14}) + sage: letter_game = CooperativeGame(letter_function) sage: letter_game A 3 player Co-operative Game. """ np = self.number_players - cf = self.ch_f - pv = self.payoff_vector return "A %s player Co-operative Game." % np def _latex_(self): @@ -582,7 +578,7 @@ def _latex_(self): ....: ('A', 'C',): 42, ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} - sage: letter_game = CooperativeGame(letter_function, {'A': 14, 'B': 14, 'C': 14}) + sage: letter_game = CooperativeGame(letter_function) sage: latex(letter_game) v(c) = \begin{cases} 0,&\text{ if }c=\emptyset\\ @@ -597,7 +593,6 @@ def _latex_(self): """ np = self.number_players cf = self.ch_f - pv = self.payoff_vector output = "v(c) = \\begin{cases}\n" for key in sorted(cf.keys(), key=lambda key: len(key)) : if key == (): @@ -611,10 +606,15 @@ def _latex_(self): output += "\\end{cases}" return output - def is_efficient(self): + def is_efficient(self, payoff_vector): r""" Returns True if the current payoff_vector is efficient. + INPUT: + + - payoff_vector - a dictionary where the key is the player and the + value is their payoff. + EXAMPLES: An efficient payoff_vector.:: @@ -627,8 +627,8 @@ def is_efficient(self): ....: ('A', 'C',): 42, ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} - sage: letter_game = CooperativeGame(letter_function, {'A': 14, 'B': 14, 'C': 14}) - sage: letter_game.is_efficient() + sage: letter_game = CooperativeGame(letter_function) + sage: letter_game.is_efficient({'A': 14, 'B': 14, 'C': 14}) True sage: letter_function = {(): 0, @@ -640,8 +640,7 @@ def is_efficient(self): ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} sage: letter_game = CooperativeGame(letter_function) - sage: letter_game.payoff_vector = {'A': 10, 'B': 14, 'C': 14} - sage: letter_game.is_efficient() + sage: letter_game.is_efficient({'A': 10, 'B': 14, 'C': 14}) False A longer example for is_efficient. :: @@ -662,39 +661,23 @@ def is_efficient(self): ....: (1, 3, 4): 40, ....: (2, 3, 4): 0, ....: (1, 2, 3, 4): 65} - sage: long_game = CooperativeGame(long_function, {1: 20, 2: 20, 3: 5, 4: 20}) - sage: long_game.is_efficient() + sage: long_game = CooperativeGame(long_function) + sage: long_game.is_efficient({1: 20, 2: 20, 3: 5, 4: 20}) True - - TESTS: - - Checks that a game has a payoff_vector. :: - - sage: A_function = {(): 0, - ....: (1,): 0, - ....: (2,): 12, - ....: (3,): 42, - ....: (1, 2,): 12, - ....: (1, 3,): 42, - ....: (2, 3,): 55, - ....: (1, 2, 3,): 55} - sage: A_game = CooperativeGame(A_function) - sage: A_game.is_efficient() - Traceback (most recent call last): - ... - ValueError: Game must have a payoff_vector """ - if not self.payoff_vector: - raise ValueError("Game must have a payoff_vector") - pl = tuple(sorted(list(self.player_list))) - return sum(self.payoff_vector.values()) == self.ch_f[pl] + return sum(payoff_vector.values()) == self.ch_f[pl] - def nullplayer(self): + def nullplayer(self, payoff_vector): r""" Returns True if the current payoff_vector possesses the null player property. + INPUT: + + - payoff_vector - a dictionary where the key is the player and the + value is their payoff. + EXAMPLES: A payoff_vector that returns True. :: @@ -707,8 +690,8 @@ def nullplayer(self): ....: ('A', 'C',): 42, ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} - sage: letter_game = CooperativeGame(letter_function, {'A': 0, 'B': 14, 'C': 14}) - sage: letter_game.nullplayer() + sage: letter_game = CooperativeGame(letter_function) + sage: letter_game.nullplayer({'A': 0, 'B': 14, 'C': 14}) True A payoff_vector that returns False. :: @@ -721,8 +704,8 @@ def nullplayer(self): ....: (1, 3,): 42, ....: (2, 3,): 55, ....: (1, 2, 3,): 55} - sage: A_game = CooperativeGame(A_function, {1: 10, 2: 10, 3: 25}) - sage: A_game.nullplayer() + sage: A_game = CooperativeGame(A_function) + sage: A_game.nullplayer({1: 10, 2: 10, 3: 25}) False A longer example for nullplayer. :: @@ -743,28 +726,12 @@ def nullplayer(self): ....: (1, 3, 4): 40, ....: (2, 3, 4): 0, ....: (1, 2, 3, 4): 65} - sage: long_game = CooperativeGame(long_function, {1: 20, 2: 20, 3: 5, 4: 20}) - sage: long_game.nullplayer() + sage: long_game = CooperativeGame(long_function) + sage: long_game.nullplayer({1: 20, 2: 20, 3: 5, 4: 20}) True TESTS: - Checks that a game has a payoff_vector. :: - - sage: A_function = {(): 0, - ....: (1,): 0, - ....: (2,): 12, - ....: (3,): 42, - ....: (1, 2,): 12, - ....: (1, 3,): 42, - ....: (2, 3,): 55, - ....: (1, 2, 3,): 55} - sage: A_game = CooperativeGame(A_function) - sage: A_game.nullplayer() - Traceback (most recent call last): - ... - ValueError: Game must have a payoff_vector - Checks that the function is going through all players. :: sage: A_function = {(): 0, @@ -775,26 +742,29 @@ def nullplayer(self): ....: (1, 3,): 42, ....: (2, 3,): 12, ....: (1, 2, 3,): 55} - sage: A_game = CooperativeGame(A_function, {1: 10, 2: 10, 3: 25}) - sage: A_game.nullplayer() + sage: A_game = CooperativeGame(A_function) + sage: A_game.nullplayer({1: 10, 2: 10, 3: 25}) False """ - if not self.payoff_vector: - raise ValueError("Game must have a payoff_vector") - for player in self.player_list: coalitions = [coal for coal in self.ch_f if player in coal] results = [] for coalit in coalitions: results.append(self.ch_f[coalit] == self.ch_f[tuple( sorted(list(set(coalit) - {player})))]) - if all(results) and self.payoff_vector[player] != 0: + if all(results) and payoff_vector[player] != 0: return False return True - def symmetry(self): + def symmetry(self, payoff_vector): r""" - Returns True if the current payoff_vector possesses the symmetry property. + Returns True if the current payoff_vector possesses the symmetry + property. + + INPUT: + + - payoff_vector - a dictionary where the key is the player and the + value is their payoff. EXAMPLES: @@ -808,8 +778,8 @@ def symmetry(self): ....: ('A', 'C',): 42, ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} - sage: letter_game = CooperativeGame(letter_function, {'A': 5, 'B': 14, 'C': 20}) - sage: letter_game.symmetry() + sage: letter_game = CooperativeGame(letter_function) + sage: letter_game.symmetry({'A': 5, 'B': 14, 'C': 20}) True A Payoff Vector that returns False. :: @@ -822,8 +792,8 @@ def symmetry(self): ....: (1, 3,): 42, ....: (2, 3,): 42, ....: (1, 2, 3,): 42} - sage: integer_game = CooperativeGame(integer_function, {1: 2, 2: 5, 3: 35}) - sage: integer_game.symmetry() + sage: integer_game = CooperativeGame(integer_function) + sage: integer_game.symmetry({1: 2, 2: 5, 3: 35}) False A longer example for symmetry. :: @@ -844,31 +814,10 @@ def symmetry(self): ....: (1, 3, 4): 40, ....: (2, 3, 4): 0, ....: (1, 2, 3, 4): 65} - sage: long_game = CooperativeGame(long_function, {1: 20, 2: 20, 3: 5, 4: 20}) - sage: long_game.symmetry() + sage: long_game = CooperativeGame(long_function) + sage: long_game.symmetry({1: 20, 2: 20, 3: 5, 4: 20}) True - - TESTS: - - Checks that a game has a payoff_vector. :: - - sage: A_function = {(): 0, - ....: (1,): 0, - ....: (2,): 12, - ....: (3,): 42, - ....: (1, 2,): 12, - ....: (1, 3,): 42, - ....: (2, 3,): 55, - ....: (1, 2, 3,): 55} - sage: A_game = CooperativeGame(A_function) - sage: A_game.is_efficient() - Traceback (most recent call last): - ... - ValueError: Game must have a payoff_vector """ - if not self.payoff_vector: - raise ValueError("Game must have a payoff_vector") - sets = list(self.ch_f.keys()) element = [i for i in sets if len(i) == 1] for c1, c2 in combinations(element, 2): @@ -877,7 +826,6 @@ def symmetry(self): junion = tuple(sorted(list(set(c1) | set(m)))) kunion = tuple(sorted(list(set(c2) | set(m)))) results.append(self.ch_f[junion] == self.ch_f[kunion]) - if (all(results) and self.payoff_vector[c1[0]] != - self.payoff_vector[c2[0]]): + if (all(results) and payoff_vector[c1[0]] != payoff_vector[c2[0]]): return False return True From a4c9a5ecf53d1ef9d7d35e24f3e1e199bbb1145a Mon Sep 17 00:00:00 2001 From: James Campbell Date: Fri, 6 Jun 2014 13:08:29 +0100 Subject: [PATCH 204/546] makes marginal_contributions hidden --- src/sage/game_theory/cooperative_game.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 0490721373b..53744eca4df 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -293,7 +293,7 @@ def shapley_value(self): """ payoff_vector = {} for player in self.player_list: - player_contribution = self.marginal_contributions(player) + player_contribution = self._marginal_contributions(player) average = sum(player_contribution) / len(player_contribution) payoff_vector[player] = average self.payoff_vector = payoff_vector @@ -445,7 +445,7 @@ def is_superadditive(self): return False return True - def marginal_contributions(self, player): + def _marginal_contributions(self, player): r""" Returns a list of contributions specific to one player. @@ -464,7 +464,7 @@ def marginal_contributions(self, player): ....: (2, 3,): 42, ....: (1, 2, 3,): 42} sage: integer_game = CooperativeGame(integer_function) - sage: integer_game.marginal_contributions(1) + sage: integer_game._marginal_contributions(1) [6, 6, 0, 0, 0, 0] """ contributions = [] From b83d5f3d1dcccd041b4299460c1e65c04d80c2e7 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Fri, 6 Jun 2014 13:30:57 +0100 Subject: [PATCH 205/546] removes useless import --- src/sage/game_theory/cooperative_game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 53744eca4df..5e639453ce4 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -22,7 +22,7 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from itertools import chain, permutations, combinations +from itertools import permutations, combinations from sage.misc.latex import latex from sage.misc.misc import powerset from sage.structure.sage_object import SageObject From d2ffe550e5517e00b16860bf6f28dc64ec18110f Mon Sep 17 00:00:00 2001 From: James Campbell Date: Fri, 6 Jun 2014 13:44:23 +0100 Subject: [PATCH 206/546] renames a variable --- src/sage/game_theory/cooperative_game.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 5e639453ce4..0d8a456a30c 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -505,7 +505,7 @@ def marginal_of_pi(self, player, pi): value = self.ch_f[player_and_pred] - self.ch_f[predecessors] return value - def get_predecessors(self, player, permutation): + def get_predecessors(self, player, pi): r""" Returns a list of all the predecessors of a player in a certain permutation and the same list including the original player @@ -515,7 +515,7 @@ def get_predecessors(self, player, permutation): -player - A real number or string. - -permutation - A tuple which is the permutation that should be used. + -pi - A tuple which is the permutation that should be used. EXAMPLES:: @@ -535,7 +535,7 @@ def get_predecessors(self, player, permutation): sage: integer_game.get_predecessors(3, (2, 3, 1)) ([2], [2, 3]) """ - pred = list(permutation[:permutation.index(player)]) + pred = list(pi[:pi.index(player)]) return sorted(pred), sorted(pred + [player]) def _repr_(self): From cec9681b1ebb379ed6eccec389981512acd527cb Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Fri, 6 Jun 2014 15:13:21 +0200 Subject: [PATCH 207/546] 8734: merge conflicts --- src/sage/calculus/calculus.py | 5 +-- src/sage/interfaces/maxima_lib.py | 40 ++++------------------- src/sage/symbolic/integration/integral.py | 12 ++----- 3 files changed, 11 insertions(+), 46 deletions(-) diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index 91c2f501747..8d3a003e145 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -1048,8 +1048,9 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): Traceback (most recent call last): ... ValueError: Computation failed since Maxima requested additional - constraints; using the 'assume' command before limit evaluation - *may* help (see `assume?` for more details) + constraints; using the 'assume' command before evaluation + *may* help (example of legal syntax is 'assume(a>0)', see + `assume?` for more details) Is a positive, negative or zero? With this example, Maxima is looking for a LOT of information:: diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 2e38d3320e4..4a934cbf379 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -660,7 +660,7 @@ def sr_integral(self,*args): Traceback (most recent call last): ... ValueError: Computation failed since Maxima requested additional - constraints; using the 'assume' command before integral evaluation + constraints; using the 'assume' command before evaluation *may* help (example of legal syntax is 'assume(a>0)', see `assume?` for more details) Is a positive or negative? @@ -672,16 +672,11 @@ def sr_integral(self,*args): sage: integral(x^n,x) Traceback (most recent call last): ... -<<<<<<< HEAD - ValueError: Computation failed ... - Is n+1 zero or nonzero? -======= ValueError: Computation failed since Maxima requested additional - constraints; using the 'assume' command before integral evaluation + constraints; using the 'assume' command before evaluation *may* help (example of legal syntax is 'assume(n>0)', see `assume?` for more details) Is n equal to -1? ->>>>>>> develop sage: assume(n+1>0) sage: integral(x^n,x) x^(n + 1)/(n + 1) @@ -781,14 +776,7 @@ def sr_integral(self,*args): # if "divergent" in s or 'Principal Value' in s: raise ValueError("Integral is divergent.") elif "Is" in s: # Maxima asked for a condition -<<<<<<< HEAD self._missing_assumption(s) -======= - j = s.find('Is ') - s = s[j:] - k = s.find(' ', 3) - raise ValueError("Computation failed since Maxima requested additional constraints; using the 'assume' command before integral evaluation *may* help (example of legal syntax is 'assume(" + s[3:k] + ">0)', see `assume?` for more details)\n" + s) ->>>>>>> develop else: raise @@ -813,16 +801,11 @@ def sr_sum(self,*args): sage: sum(a*q^k, k, 0, oo) Traceback (most recent call last): ... -<<<<<<< HEAD - ValueError: Computation failed ... - Is abs(q)-1 positive, negative, or zero? -======= ValueError: Computation failed since Maxima requested additional - constraints; using the 'assume' command before summation *may* help + constraints; using the 'assume' command before evaluation *may* help (example of legal syntax is 'assume(abs(q)-1>0)', see `assume?` for more details) Is abs(q)-1 positive, negative or zero? ->>>>>>> develop sage: assume(q > 1) sage: sum(a*q^k, k, 0, oo) Traceback (most recent call last): @@ -872,14 +855,7 @@ def sr_sum(self,*args): # if "divergent" in s or 'Pole encountered' in s: raise ValueError("Sum is divergent.") elif "Is" in s: # Maxima asked for a condition -<<<<<<< HEAD self._missing_assumption(s) -======= - j = s.find('Is ') - s = s[j:] - k = s.find(' ', 3) - raise ValueError("Computation failed since Maxima requested additional constraints; using the 'assume' command before summation *may* help (example of legal syntax is 'assume(" + s[3:k] + ">0)', see `assume?` for more details)\n" + s) ->>>>>>> develop else: raise @@ -901,15 +877,11 @@ def sr_limit(self,expr,v,a,dir=None): sage: limit(x^a,x=0) Traceback (most recent call last): ... -<<<<<<< HEAD - ValueError: Computation failed ... - Is a positive, negative, or zero? -======= ValueError: Computation failed since Maxima requested additional - constraints; using the 'assume' command before limit evaluation - *may* help (see `assume?` for more details) + constraints; using the 'assume' command before evaluation + *may* help (example of legal syntax is 'assume(a>0)', see `assume?` + for more details) Is a positive, negative or zero? ->>>>>>> develop sage: assume(a>0) sage: limit(x^a,x=0) Traceback (most recent call last): diff --git a/src/sage/symbolic/integration/integral.py b/src/sage/symbolic/integration/integral.py index b4b6b3e720f..bcfb90e7a25 100644 --- a/src/sage/symbolic/integration/integral.py +++ b/src/sage/symbolic/integration/integral.py @@ -393,7 +393,7 @@ def integrate(expression, v=None, a=None, b=None, algorithm=None): Traceback (most recent call last): ... ValueError: Computation failed since Maxima requested additional - constraints; using the 'assume' command before integral evaluation + constraints; using the 'assume' command before evaluation *may* help (example of legal syntax is 'assume(n>0)', see `assume?` for more details) Is n equal to -1? @@ -555,16 +555,8 @@ def integrate(expression, v=None, a=None, b=None, algorithm=None): sage: res = integral(f,x,0.0001414, 1.); res Traceback (most recent call last): ... -<<<<<<< HEAD - ValueError: Computation failed since Maxima requested additional - constraints; using the 'assume' command before evaluation - *may* help (example of legal syntax is 'assume(50015104*y^2-50015103>0)', - see `assume?` for more details) - Is 50015104*y^2-50015103 positive, negative, or zero? -======= - ValueError: Computation failed since Maxima requested additional constraints; using the 'assume' command before integral evaluation *may* help (example of legal syntax is 'assume(50015104*y^2-50015103>0)', see `assume?` for more details) + ValueError: Computation failed since Maxima requested additional constraints; using the 'assume' command before evaluation *may* help (example of legal syntax is 'assume(50015104*y^2-50015103>0)', see `assume?` for more details) Is 50015104*y^2-50015103 positive, negative or zero? ->>>>>>> develop sage: assume(y>1) sage: res = integral(f,x,0.0001414, 1.); res -2*y*arctan(0.0001414/y) + 2*y*arctan(1/y) + log(y^2 + 1.0) - 0.0001414*log(y^2 + 1.999396e-08) - 1.9997172 From f70208835cb64305fc90524a9b67c5a86aed0c1e Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Fri, 6 Jun 2014 15:40:42 +0200 Subject: [PATCH 208/546] take back unrelated change --- build/pkgs/ipython/checksums.ini | 6 +++--- build/pkgs/ipython/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/ipython/checksums.ini b/build/pkgs/ipython/checksums.ini index 45794134321..0a7c076f47b 100644 --- a/build/pkgs/ipython/checksums.ini +++ b/build/pkgs/ipython/checksums.ini @@ -1,4 +1,4 @@ tarball=ipython-VERSION.tar.gz -sha1=e80c30388cbe576038a78fc8ebb159cddae84fb4 -md5=785c7b6364c6a0dd34aa4ea970cf83b9 -cksum=2529846191 +sha1=0041de4de5cac2fb5d0522af84958c78c1dfd9bf +md5=4ffb36697f7ca8cb4a2de0f5b30bc89c +cksum=1840805192 diff --git a/build/pkgs/ipython/package-version.txt b/build/pkgs/ipython/package-version.txt index 7ec1d6db408..6085e946503 100644 --- a/build/pkgs/ipython/package-version.txt +++ b/build/pkgs/ipython/package-version.txt @@ -1 +1 @@ -2.1.0 +1.2.1 From c08a2f2e8658510673d740de9fbf557948834a6f Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 6 Jun 2014 14:54:23 +0100 Subject: [PATCH 209/546] More mathematics added to docs, just about to start on monotone games --- src/sage/game_theory/cooperative_game.py | 28 +++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 0d8a456a30c..2ef40247f5f 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -50,7 +50,7 @@ class CooperativeGame(SageObject): where $C=2^{\Omega}$ is set of all coalitions of players. An example of such a game is shown below: - $$ + \[ v(c) = \begin{cases} 0,&\text{ if }c=\emptyset\\ 6,&\text{ if }c=\{1\}\\ @@ -61,7 +61,7 @@ class CooperativeGame(SageObject): 42,&\text{ if }c=\{2,3\}\\ 42,&\text{ if }c=\{1, 2,3\}\\ \end{cases} - $$ + \] The function $v$ can be thought of as as a record of contribution of individuals and coalitions of individuals. @@ -93,10 +93,9 @@ class CooperativeGame(SageObject): ....: ('A', 'B', 'C',): 42} sage: letter_game = CooperativeGame(letter_function) - From this we can now compute the Shapley Value. :: + Characteristic function games can be of various types. - sage: letter_game.shapley_value() - {'A': 2, 'C': 35, 'B': 5} + In a monotone game... We can test if it is Monotonic or Superadditive. :: @@ -110,6 +109,25 @@ class CooperativeGame(SageObject): sage: letter_game A 3 player Co-operative Game. + + It can be shown that the 'fair' payoff vector, referred to as the Shapley value is given by the following formula: + + \[ + \phi_i(G)=\frac{1}{N!}\sum_{\pi\in\Pi_n}\Delta_\pi^G(i) + \] + + where the summation is over the permutations of the players and the marginal + contributions of a player for a given permutation is given as: + + \[ + \Delta_\pi^G(i)=v(S_{\pi}(i)\cup i)-v(S_{\pi}(i)) + \] + + To compute the Shapley value in Sage is simple: + + sage: letter_game.shapley_value() + {'A': 2, 'C': 35, 'B': 5} + We can test 3 basic properties of the Payoff Vector. They are * Is it is efficient? * Does it possess the nullplayer property? From 10d407c2fbc5d8f8bdaaaa77c21f53148c4d1bc9 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Fri, 6 Jun 2014 16:27:56 +0100 Subject: [PATCH 210/546] starts to get docs ready to build properly --- src/doc/en/reference/game_theory/conf.py | 73 ++++++++++++++++++++++ src/doc/en/reference/game_theory/index.rst | 10 +++ src/doc/en/reference/index.rst | 1 + src/sage/game_theory/cooperative_game.py | 1 - 4 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/doc/en/reference/game_theory/conf.py create mode 100644 src/doc/en/reference/game_theory/index.rst diff --git a/src/doc/en/reference/game_theory/conf.py b/src/doc/en/reference/game_theory/conf.py new file mode 100644 index 00000000000..ae3b7eaf67f --- /dev/null +++ b/src/doc/en/reference/game_theory/conf.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# +# Sage documentation build configuration file, created by +# sphinx-quickstart on Thu Aug 21 20:15:55 2008. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# The contents of this file are pickled, so don't put values in the namespace +# that aren't pickleable (module imports are okay, they're removed automatically). +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os +sys.path.append(os.environ['SAGE_DOC']) +from common.conf import * + +# settings for the intersphinx extension: + +ref_src = os.path.join(SAGE_DOC, 'en', 'reference') +ref_out = os.path.join(SAGE_DOC, 'output', 'html', 'en', 'reference') +intersphinx_mapping[ref_out] = None + +for doc in os.listdir(ref_src): + if os.path.exists(os.path.join(ref_src, doc, 'index.rst')): + intersphinx_mapping[os.path.join(ref_out, doc)] = None + +# We use the main document's title, if we can find it. +rst_file = open('index.rst', 'r') +rst_lines = rst_file.read().splitlines() +rst_file.close() + +title = u'' +for i in xrange(len(rst_lines)): + if rst_lines[i].startswith('==') and i > 0: + title = rst_lines[i-1].strip() + break + +# Otherwise, we use this directory's name. +name = os.path.basename(os.path.abspath('.')) +if not title: + title = name.capitalize() +title = title.replace(u'`', u'$') + +# General information about the project. +project = u'Sage Reference Manual: ' + title + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +html_title = u'Sage Reference Manual v' + release + ': ' + title + +# A shorter title for the navigation bar. Default is the same as html_title. +html_short_title = title + +# HTML theme (e.g., 'default', 'sphinxdoc'). The pages for the +# reference manual use a custom theme, a slight variant on the 'sage' +# theme, to set the links in the top line. +html_theme = 'sageref' + +# Output file base name for HTML help builder. +htmlhelp_basename = name + +# Grouping the document tree into LaTeX files. List of tuples (source +# start file, target name, title, author, document class +# [howto/manual]). +latex_documents = [ +('index', name + '.tex', project, u'The Sage Development Team', 'manual') +] + +#Ignore all .rst in the _sage subdirectory +exclude_trees = exclude_trees + ['_sage'] + +multidocs_is_master = False diff --git a/src/doc/en/reference/game_theory/index.rst b/src/doc/en/reference/game_theory/index.rst new file mode 100644 index 00000000000..8761718df6e --- /dev/null +++ b/src/doc/en/reference/game_theory/index.rst @@ -0,0 +1,10 @@ +Games +===== + +.. toctree:: + :maxdepth: 2 + + sage/game_theory/cooperative_game + + +.. include:: ../footer.txt \ No newline at end of file diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst index 8e1583bfdfc..fcd7302a40b 100644 --- a/src/doc/en/reference/index.rst +++ b/src/doc/en/reference/index.rst @@ -120,6 +120,7 @@ Miscellaneous Mathematics * :doc:`Statistics ` * :doc:`Quantitative Finance ` * :doc:`Coding Theory ` +* :doc:`Game Theory ` Doctesting, Interfaces, Databases, Miscellany --------------------------------------------- diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 0d8a456a30c..fb5f6c839a2 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -23,7 +23,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from itertools import permutations, combinations -from sage.misc.latex import latex from sage.misc.misc import powerset from sage.structure.sage_object import SageObject From 1a63955b64aa3d3d8228d70da0c6ea6ec51196a2 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Fri, 6 Jun 2014 16:40:43 +0100 Subject: [PATCH 211/546] small fix to docs --- src/sage/game_theory/cooperative_game.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index fb5f6c839a2..d0fa8b5a65b 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -7,10 +7,11 @@ resources: https://www.youtube.com/watch?v=aThG4YAFErw) as well as test properties of the game (monotonicity, super additivity) are also included. -AUTHOR:: -- James Campbell 06-2014: Original version +AUTHOR: + + - James Campbell 06-2014: Original version + - Vince Knight 06-2014 -- Vince Knight 06-2014 """ #***************************************************************************** @@ -113,6 +114,7 @@ class CooperativeGame(SageObject): * Is it is efficient? * Does it possess the nullplayer property? * Does it possess the symmetry property? + :: sage: letter_game.shapley_value() From 698e31eee05cedcc3a4cc080712eb83450ace941 Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 6 Jun 2014 16:50:21 +0100 Subject: [PATCH 212/546] Added description of super additivie and monotone. --- src/sage/game_theory/cooperative_game.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 2ef40247f5f..52c634a9c9b 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -95,9 +95,10 @@ class CooperativeGame(SageObject): Characteristic function games can be of various types. - In a monotone game... + A characteristic function game \(G=(N,v)\) is monotone if it satisfies \(v(C_2)\geq v(C_1) for all \(C_1\subseteq C_2\). + A characteristic function game \(G=(N,v)\) is super-additive if it satisfies \(v(C_2)\geq v(C_1) for all \(C_1\subseteq C_2\). - We can test if it is Monotonic or Superadditive. :: + We can test if a game is Monotonic or Superadditive. :: sage: letter_game.is_monotone() True From 0e30dbc241623a907b9d87b8df431470f50c6392 Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 6 Jun 2014 16:55:23 +0100 Subject: [PATCH 213/546] Tweak to example for nullplayer and symmetry --- src/sage/game_theory/cooperative_game.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 52c634a9c9b..a44a63a2181 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -129,29 +129,28 @@ class CooperativeGame(SageObject): sage: letter_game.shapley_value() {'A': 2, 'C': 35, 'B': 5} - We can test 3 basic properties of the Payoff Vector. They are + We can test 3 basic properties of a Payoff Vector. They are * Is it is efficient? * Does it possess the nullplayer property? * Does it possess the symmetry property? :: - sage: letter_game.shapley_value() - {'A': 2, 'C': 35, 'B': 5} - sage: letter_game.is_efficient({'A': 2, 'C': 35, 'B': 5}) + sage: payoff_vector = letter_game.shapley_value() + sage: letter_game.is_efficient(payoff_vector) True - sage: letter_game.nullplayer({'A': 2, 'C': 35, 'B': 5}) + sage: letter_game.nullplayer(payoff_vector) True - sage: letter_game.symmetry({'A': 2, 'C': 35, 'B': 5}) + sage: letter_game.symmetry(payoff_vector) True Any Payoff Vector can be passed to the game and these properties can once again be tested: - sage: letter_game.payoff_vector = {'A': 0, 'C': 35, 'B': 3} - sage: letter_game.is_efficient({'A': 0, 'C': 35, 'B': 3}) + sage: payoff_vector = {'A': 0, 'C': 35, 'B': 3} + sage: letter_game.is_efficient(payoff_vector) False - sage: letter_game.nullplayer({'A': 0, 'C': 35, 'B': 3}) + sage: letter_game.nullplayer(payoff_vector) True - sage: letter_game.symmetry({'A': 0, 'C': 35, 'B': 3}) + sage: letter_game.symmetry(payoff_vector) True TESTS: From 5682a13f1af3ca36dcfddb3a30480a5f6ab8be74 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Fri, 6 Jun 2014 16:59:15 +0100 Subject: [PATCH 214/546] minor change --- src/sage/game_theory/cooperative_game.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index d0fa8b5a65b..39a009b669e 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -112,7 +112,9 @@ class CooperativeGame(SageObject): We can test 3 basic properties of the Payoff Vector. They are * Is it is efficient? + * Does it possess the nullplayer property? + * Does it possess the symmetry property? :: From a0d4e9646f81211aeff92154178dcbb4c2f720ab Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 6 Jun 2014 17:00:52 +0100 Subject: [PATCH 215/546] Efficiency and null player written --- src/sage/game_theory/cooperative_game.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index a44a63a2181..8e14e2359e8 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -129,9 +129,19 @@ class CooperativeGame(SageObject): sage: letter_game.shapley_value() {'A': 2, 'C': 35, 'B': 5} - We can test 3 basic properties of a Payoff Vector. They are - * Is it is efficient? - * Does it possess the nullplayer property? + We can test 3 basic properties of a Payoff Vector $lambda$. They are + * Efficiency: + + \[sum_{i=1}^N\lambda_i=v(\Omega)\] + + No value of the total coalition is lost. + + * The nullplayer property: + + If \(\exists\) \(i\) such that \(\(v(C\cup i)=v(C)\)\) for all \(C\in 2^{\Omega}\) then: + + \[\lambda_i=0\] + * Does it possess the symmetry property? :: From 73c9877841b3f86befbccb8c57b3817a640f956e Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 6 Jun 2014 17:04:53 +0100 Subject: [PATCH 216/546] symmetry written --- src/sage/game_theory/cooperative_game.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 8e14e2359e8..81c2e9d9329 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -96,7 +96,7 @@ class CooperativeGame(SageObject): Characteristic function games can be of various types. A characteristic function game \(G=(N,v)\) is monotone if it satisfies \(v(C_2)\geq v(C_1) for all \(C_1\subseteq C_2\). - A characteristic function game \(G=(N,v)\) is super-additive if it satisfies \(v(C_2)\geq v(C_1) for all \(C_1\subseteq C_2\). + A characteristic function game \(G=(N,v)\) is super-additive if it satisfies \(v(C_2)\geq v(C_1) for all \(C_1\subseteq C_2\) such that \(C_1\cap\C_2=\emptyset\). We can test if a game is Monotonic or Superadditive. :: @@ -134,7 +134,7 @@ class CooperativeGame(SageObject): \[sum_{i=1}^N\lambda_i=v(\Omega)\] - No value of the total coalition is lost. + In other words: no value of the total coalition is lost. * The nullplayer property: @@ -142,7 +142,16 @@ class CooperativeGame(SageObject): \[\lambda_i=0\] + In other words: if a player does not contribute to any coalition then that player should receive no payoff. + * Does it possess the symmetry property? + + A payoff vector possesses the symmetry property if \(v(C\cup i)=v(C\cup j)\) for all \(C\in 2^{\Omega}\setminus\{i,j\}\) then: + + \[x_i=x_j\] + + If players contribute symmetrically then they should get the same payoff. + :: sage: payoff_vector = letter_game.shapley_value() From e9e3367aa63f42fe79a7ae5e2c8e098f5e9d6cf2 Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 6 Jun 2014 17:09:46 +0100 Subject: [PATCH 217/546] Word basic added to docstring --- src/sage/game_theory/cooperative_game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 81c2e9d9329..31c9deec891 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -1,7 +1,7 @@ r""" Co-operative games with N players. -This module implements characteristic function cooperative games. +This module implements a **basic** implementation of a characteristic function cooperative game. The main contribution is a class for a characteristic function game. Methods to calculate the Shapley value (a fair way of sharing common resources: https://www.youtube.com/watch?v=aThG4YAFErw) as well as From 64df8ab0f34f827d6f52da7a8d3e4fe1c92583e9 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Fri, 6 Jun 2014 17:09:53 +0100 Subject: [PATCH 218/546] improves default in docs --- src/doc/en/reference/game_theory/index.rst | 1 - src/sage/game_theory/cooperative_game.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/doc/en/reference/game_theory/index.rst b/src/doc/en/reference/game_theory/index.rst index 8761718df6e..7abf79974fb 100644 --- a/src/doc/en/reference/game_theory/index.rst +++ b/src/doc/en/reference/game_theory/index.rst @@ -6,5 +6,4 @@ Games sage/game_theory/cooperative_game - .. include:: ../footer.txt \ No newline at end of file diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 39a009b669e..4c884b084a5 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -40,7 +40,7 @@ class CooperativeGame(SageObject): tuple must end with a comma. * Value - A real number representing each set of players contribution. - - payoff_vector - default = ``False``, a dictionary can be passed instead but + - payoff_vector -- (default: False), a dictionary can be passed instead but this will be overwritten if shapley_value is called. EXAMPLES: From 4aeab22c17d0b3a301bebc6b135e3cd994f2c2f7 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Fri, 6 Jun 2014 17:53:50 +0100 Subject: [PATCH 219/546] sorts out indentation in docs --- src/sage/game_theory/cooperative_game.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 4c884b084a5..8bf718cd631 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -516,9 +516,9 @@ def get_predecessors(self, player, pi): INPUT: - -player - A real number or string. + - player - A real number or string. - -pi - A tuple which is the permutation that should be used. + - pi - A tuple which is the permutation that should be used. EXAMPLES:: @@ -616,7 +616,7 @@ def is_efficient(self, payoff_vector): INPUT: - payoff_vector - a dictionary where the key is the player and the - value is their payoff. + value is their payoff. EXAMPLES: @@ -679,7 +679,7 @@ def nullplayer(self, payoff_vector): INPUT: - payoff_vector - a dictionary where the key is the player and the - value is their payoff. + value is their payoff. EXAMPLES: @@ -767,7 +767,7 @@ def symmetry(self, payoff_vector): INPUT: - payoff_vector - a dictionary where the key is the player and the - value is their payoff. + value is their payoff. EXAMPLES: From 86d5ad914284cd73841067c5dfbda4811a0ba69c Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 6 Jun 2014 22:39:00 +0100 Subject: [PATCH 220/546] Added simple 8 player game --- src/sage/game_theory/cooperative_game.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index acc760d3b35..fece590cf24 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -95,6 +95,22 @@ class CooperativeGame(SageObject): Characteristic function games can be of various types. + The following example implements a (trivial) 8 player characteristic function game: + + \[v(c)=|c|\text{ for all }c\in 2^{\omega}\] + + sage: def simple_characteristic_function(N): + ....: return {tuple(coalition) : len(coalition) + ....: for coalition in subsets(range(N))} + sage: g = CooperativeGame(simple_characteristic_function(10)) + sage: g.shapley_value(8) + {0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1} + + The above is slow to run and this is due to the dimensionality + of the calculations involved. There are various approximation + approaches to obtaining the Shapley value of a game. Implementing + this would be a worthwhile development. + A characteristic function game \(G=(N,v)\) is monotone if it satisfies \(v(C_2)\geq v(C_1) for all \(C_1\subseteq C_2\). A characteristic function game \(G=(N,v)\) is super-additive if it satisfies \(v(C_2)\geq v(C_1) for all \(C_1\subseteq C_2\) such that \(C_1\cap\C_2=\emptyset\). From 2476b7c8c060e67a1a8b5a3af70982f64e7148da Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 6 Jun 2014 22:42:57 +0100 Subject: [PATCH 221/546] Had failed a test, now it doesnt. Turning my machine off for 24 hours+ --- src/sage/game_theory/cooperative_game.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index fece590cf24..8c51dc1597a 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -102,14 +102,14 @@ class CooperativeGame(SageObject): sage: def simple_characteristic_function(N): ....: return {tuple(coalition) : len(coalition) ....: for coalition in subsets(range(N))} - sage: g = CooperativeGame(simple_characteristic_function(10)) - sage: g.shapley_value(8) + sage: g = CooperativeGame(simple_characteristic_function(8)) + sage: g.shapley_value() {0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1} The above is slow to run and this is due to the dimensionality of the calculations involved. There are various approximation approaches to obtaining the Shapley value of a game. Implementing - this would be a worthwhile development. + these would be a worthwhile development. A characteristic function game \(G=(N,v)\) is monotone if it satisfies \(v(C_2)\geq v(C_1) for all \(C_1\subseteq C_2\). A characteristic function game \(G=(N,v)\) is super-additive if it satisfies \(v(C_2)\geq v(C_1) for all \(C_1\subseteq C_2\) such that \(C_1\cap\C_2=\emptyset\). From 4a6ef0ba815c77321ad2d92d5e4e5e74035e37ce Mon Sep 17 00:00:00 2001 From: Punarbasu Purkayastha Date: Sat, 7 Jun 2014 12:51:59 +0800 Subject: [PATCH 222/546] Need to convert to complex only --- src/sage/symbolic/function.pyx | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/sage/symbolic/function.pyx b/src/sage/symbolic/function.pyx index 2a3e37181fb..08bea5be158 100644 --- a/src/sage/symbolic/function.pyx +++ b/src/sage/symbolic/function.pyx @@ -936,14 +936,10 @@ cdef class BuiltinFunction(Function): # we try if it works with the corresponding complex domain if org_parent is float or org_parent is complex: try: - from sage.rings.real_double import RDF - return org_parent(RDF(res)) + from sage.rings.complex_double import CDF + return complex(CDF(res)) except (TypeError, ValueError): - try: - from sage.rings.complex_double import CDF - return org_parent(CDF(res)) - except (TypeError, ValueError): - pass + pass elif hasattr(org_parent, 'complex_field'): try: return org_parent.complex_field()(res) From e776b188e54d567b2112aadb7cd8e13e6e6cbe59 Mon Sep 17 00:00:00 2001 From: Punarbasu Purkayastha Date: Sat, 7 Jun 2014 12:54:42 +0800 Subject: [PATCH 223/546] fix unneeded line number in doctest --- src/sage/symbolic/function.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/symbolic/function.pyx b/src/sage/symbolic/function.pyx index 08bea5be158..408b6da9cf0 100644 --- a/src/sage/symbolic/function.pyx +++ b/src/sage/symbolic/function.pyx @@ -697,7 +697,7 @@ cdef class Function(SageObject): sage: import numpy sage: a = numpy.arange(5) sage: csc(a) - doctest:270: RuntimeWarning: divide by zero encountered in divide + doctest:...: RuntimeWarning: divide by zero encountered in divide array([ inf, 1.18839511, 1.09975017, 7.0861674 , -1.32134871]) sage: factorial(a) From 2d31fb12b7a2932624a4ba2b310582d190d70824 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sat, 7 Jun 2014 07:22:38 +0200 Subject: [PATCH 224/546] 8734: doc cosmetics --- src/sage/interfaces/maxima_abstract.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/interfaces/maxima_abstract.py b/src/sage/interfaces/maxima_abstract.py index 95f2c89e1f5..503134322e4 100644 --- a/src/sage/interfaces/maxima_abstract.py +++ b/src/sage/interfaces/maxima_abstract.py @@ -2184,14 +2184,14 @@ def _add_(self, f): sage: f+3 sin(x)+3 - The parameter ``x`` is different from the symbolic variable:: + The Maxima variable ``x`` is different from the Sage symbolic variable:: sage: (f+maxima.cos(x)) cos(_SAGE_VAR_x)+sin(x) sage: (f+maxima.cos(y)) cos(_SAGE_VAR_y)+sin(x) - Note that you make get unexpected results when calling symbolic expressions + Note that you may get unexpected results when calling symbolic expressions and not explicitly giving the variables:: sage: (f+maxima.cos(x))(2) @@ -2210,14 +2210,14 @@ def _sub_(self, f): sage: x,y = var('x,y') sage: f = maxima.function('x','sin(x)') - The parameter ``x`` is different from the symbolic variable:: + The Maxima variable ``x`` is different from the Sage symbolic variable:: sage: (f-maxima.cos(x)) sin(x)-cos(_SAGE_VAR_x) sage: (f-maxima.cos(y)) sin(x)-cos(_SAGE_VAR_y) - Note that you make get unexpected results when calling symbolic expressions + Note that you may get unexpected results when calling symbolic expressions and not explicitly giving the variables:: sage: (f-maxima.cos(x))(2) From 630ea9299705f8aa029e6ef38d8055b7c5794f52 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sat, 7 Jun 2014 08:43:17 +0200 Subject: [PATCH 225/546] 16007: fix doctests; add documentation --- src/sage/calculus/calculus.py | 6 ++++ src/sage/calculus/desolvers.py | 52 ++++++++++++++++++---------------- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index f28dfd055b3..7b9a81c514d 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -1778,6 +1778,12 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): 2 sage: var('my_new_var').full_simplify() my_new_var + + ODE solution constants are treated differently (:trac:`16007`):: + + sage: from sage.calculus.calculus import symbolic_expression_from_maxima_string as sefms + sage: sefms('%k1*%x + %k2*%y + %c') + _K1*x + _K2*y + _C """ syms = sage.symbolic.pynac.symbol_table.get('maxima', {}).copy() diff --git a/src/sage/calculus/desolvers.py b/src/sage/calculus/desolvers.py index ef5ff3b0ac9..a78a0a7e787 100644 --- a/src/sage/calculus/desolvers.py +++ b/src/sage/calculus/desolvers.py @@ -3,8 +3,12 @@ This file contains functions useful for solving differential equations which occur commonly in a 1st semester differential equations -course. For another numerical solver see :meth:`ode_solver` function -and optional package Octave. +course. For another numerical solver see the :meth:`ode_solver` function +and the optional package Octave. + +Solutions from the Maxima package can contain the three constants +``_C``, ``_K1``, and ``_K2`` where the underscore is used to distinguish +them from symbolic variables that the user might have used. Commands: @@ -123,7 +127,7 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) sage: x = var('x') sage: y = function('y', x) sage: desolve(diff(y,x) + y - 1, y) - (c + e^x)*e^(-x) + (_C + e^x)*e^(-x) :: @@ -140,7 +144,7 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) sage: y = function('y', x) sage: de = diff(y,x,2) - y == x sage: desolve(de, y) - k2*e^(-x) + k1*e^x - x + _K2*e^(-x) + _K1*e^x - x :: @@ -162,7 +166,7 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) sage: de = diff(y,x,2) + y == 0 sage: desolve(de, y) - k2*cos(x) + k1*sin(x) + _K2*cos(x) + _K1*sin(x) :: @@ -172,12 +176,12 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) :: sage: desolve(y*diff(y,x)+sin(x)==0,y) - -1/2*y(x)^2 == c - cos(x) + -1/2*y(x)^2 == _C - cos(x) Clairaut equation: general and singular solutions:: sage: desolve(diff(y,x)^2+x*diff(y,x)-y==0,y,contrib_ode=True,show_method=True) - [[y(x) == c^2 + c*x, y(x) == -1/4*x^2], 'clairault'] + [[y(x) == _C^2 + _C*x, y(x) == -1/4*x^2], 'clairault'] For equations involving more variables we specify an independent variable:: @@ -194,7 +198,7 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) Higher order equations, not involving independent variable:: sage: desolve(diff(y,x,2)+y*(diff(y,x,1))^3==0,y).expand() - 1/6*y(x)^3 + k1*y(x) == k2 + x + 1/6*y(x)^3 + _K1*y(x) == _K2 + x :: @@ -209,12 +213,12 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) Separable equations - Sage returns solution in implicit form:: sage: desolve(diff(y,x)*sin(y) == cos(x),y) - -cos(y(x)) == c + sin(x) + -cos(y(x)) == _C + sin(x) :: sage: desolve(diff(y,x)*sin(y) == cos(x),y,show_method=True) - [-cos(y(x)) == c + sin(x), 'separable'] + [-cos(y(x)) == _C + sin(x), 'separable'] :: @@ -224,12 +228,12 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) Linear equation - Sage returns the expression on the right hand side only:: sage: desolve(diff(y,x)+(y) == cos(x),y) - 1/2*((cos(x) + sin(x))*e^x + 2*c)*e^(-x) + 1/2*((cos(x) + sin(x))*e^x + 2*_C)*e^(-x) :: sage: desolve(diff(y,x)+(y) == cos(x),y,show_method=True) - [1/2*((cos(x) + sin(x))*e^x + 2*c)*e^(-x), 'linear'] + [1/2*((cos(x) + sin(x))*e^x + 2*_C)*e^(-x), 'linear'] :: @@ -241,16 +245,16 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) into `e^{x}e^{y}`:: sage: desolve(diff(y,x)==exp(x-y),y,show_method=True) - [-e^x + e^y(x) == c, 'exact'] + [-e^x + e^y(x) == _C, 'exact'] You can solve Bessel equations, also using initial conditions, but you cannot put (sometimes desired) the initial condition at x=0, since this point is a singular point of the equation. Anyway, if the solution should be bounded at x=0, then - k2=0.:: + _K2=0.:: sage: desolve(x^2*diff(y,x,x)+x*diff(y,x)+(x^2-4)*y==0,y) - k1*bessel_J(2, x) + k2*bessel_Y(2, x) + _K1*bessel_J(2, x) + _K2*bessel_Y(2, x) Example of difficult ODE producing an error:: @@ -266,12 +270,12 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) Some more types of ODE's:: sage: desolve(x*diff(y,x)^2-(1+x*y)*diff(y,x)+y==0,y,contrib_ode=True,show_method=True) - [[y(x) == c*e^x, y(x) == c + log(x)], 'factor'] + [[y(x) == _C*e^x, y(x) == _C + log(x)], 'factor'] :: sage: desolve(diff(y,x)==(x+y)^2,y,contrib_ode=True,show_method=True) - [[[x == c - arctan(sqrt(t)), y(x) == -x - sqrt(t)], [x == c + arctan(sqrt(t)), y(x) == -x + sqrt(t)]], 'lagrange'] + [[[x == _C - arctan(sqrt(t)), y(x) == -x - sqrt(t)], [x == _C + arctan(sqrt(t)), y(x) == -x + sqrt(t)]], 'lagrange'] These two examples produce an error (as expected, Maxima 5.18 cannot solve equations from initial conditions). Maxima 5.18 @@ -292,12 +296,12 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) Second order linear ODE:: sage: desolve(diff(y,x,2)+2*diff(y,x)+y == cos(x),y) - (k2*x + k1)*e^(-x) + 1/2*sin(x) + (_K2*x + _K1)*e^(-x) + 1/2*sin(x) :: sage: desolve(diff(y,x,2)+2*diff(y,x)+y == cos(x),y,show_method=True) - [(k2*x + k1)*e^(-x) + 1/2*sin(x), 'variationofparameters'] + [(_K2*x + _K1)*e^(-x) + 1/2*sin(x), 'variationofparameters'] :: @@ -322,12 +326,12 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) :: sage: desolve(diff(y,x,2)+2*diff(y,x)+y == 0,y) - (k2*x + k1)*e^(-x) + (_K2*x + _K1)*e^(-x) :: sage: desolve(diff(y,x,2)+2*diff(y,x)+y == 0,y,show_method=True) - [(k2*x + k1)*e^(-x), 'constcoeff'] + [(_K2*x + _K1)*e^(-x), 'constcoeff'] :: @@ -357,7 +361,7 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) sage: sage.calculus.calculus.maxima('domain:real') # needed since Maxima 5.26.0 to get the answer as below real sage: desolve(x*diff(y,x)-x*sqrt(y^2+x^2)-y == 0, y, contrib_ode=True) - [x - arcsinh(y(x)/x) == c] + [x - arcsinh(y(x)/x) == _C] Trac #10682 updated Maxima to 5.26, and it started to show a different solution in the complex domain for the ODE above:: @@ -368,7 +372,7 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) [1/2*(2*x^2*sqrt(x^(-2)) - 2*x*sqrt(x^(-2))*arcsinh(y(x)/sqrt(x^2)) - 2*x*sqrt(x^(-2))*arcsinh(y(x)^2/(x*sqrt(y(x)^2))) + log(4*(2*x^2*sqrt((x^2*y(x)^2 + y(x)^4)/x^2)*sqrt(x^(-2)) + x^2 + - 2*y(x)^2)/x^2))/(x*sqrt(x^(-2))) == c] + 2*y(x)^2)/x^2))/(x*sqrt(x^(-2))) == _C] Trac #6479 fixed:: @@ -395,7 +399,7 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) sage: x=var('x'); f=function('f',x); k=var('k'); assume(k>0) sage: desolve(diff(f,x,2)/f==k,f,ivar=x) - k1*e^(sqrt(k)*x) + k2*e^(-sqrt(k)*x) + _K1*e^(sqrt(k)*x) + _K2*e^(-sqrt(k)*x) AUTHORS: From b5f372e4610123ead04a49786fd77d5ac045793f Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Sat, 7 Jun 2014 14:45:00 +0200 Subject: [PATCH 226/546] #16454 : update openssl to 1.0.1h, remove inapplicable doc patch, remove old-spkg-style changelog --- build/pkgs/openssl/SPKG.txt | 20 ----------- build/pkgs/openssl/checksums.ini | 6 ++-- build/pkgs/openssl/package-version.txt | 2 +- .../openssl-1.0.1c-pod_syntax_error.patch | 36 ------------------- 4 files changed, 4 insertions(+), 60 deletions(-) delete mode 100644 build/pkgs/openssl/patches/openssl-1.0.1c-pod_syntax_error.patch diff --git a/build/pkgs/openssl/SPKG.txt b/build/pkgs/openssl/SPKG.txt index 47ea62f0b9a..7a4f0eba994 100644 --- a/build/pkgs/openssl/SPKG.txt +++ b/build/pkgs/openssl/SPKG.txt @@ -26,23 +26,3 @@ library in a variety of computer languages are available. while Apple's gcc does, so we remove this flag. * src/config: patched to fix a problem on Solaris. -== Changelog == - -=== openssl-1.0.1c.p0 (John Palmieri, 17 June 2012) === - * Trac #13126: Update source to 1.0.1c - * Created hg repository - * Cleaned up spkg-install - * Created spkg-check - * Patch to build on OS X Lion - * Patch to build on Solaris - -=== openssl-1.0.1a (Mariah Lenox, 23 April 2012) === - * upgraded source - -=== openssl-1.0.0.p0 (William Stein, June 3, 2010) === - * Fix issue with openssl libraries getting installed to lib64, - as recommended by Mariah (the referee). - -=== openssl-1.0.0 (William Stein, April 27, 2010) === - * Initial version in modernized format - diff --git a/build/pkgs/openssl/checksums.ini b/build/pkgs/openssl/checksums.ini index f92cfbec1bc..bdded2bab89 100644 --- a/build/pkgs/openssl/checksums.ini +++ b/build/pkgs/openssl/checksums.ini @@ -1,4 +1,4 @@ tarball=openssl-VERSION.tar.gz -sha1=3f1b1223c9e8189bfe4e186d86449775bd903460 -md5=66bf6f10f060d561929de96f9dfe5b8c -cksum=4124470397 +sha1=b2239599c8bf8f7fc48590a55205c26abe560bf8 +md5=8d6d684a9430d5cc98a62a5d8fbda8cf +cksum=3773835410 diff --git a/build/pkgs/openssl/package-version.txt b/build/pkgs/openssl/package-version.txt index 2d615642512..36beb0f0e4e 100644 --- a/build/pkgs/openssl/package-version.txt +++ b/build/pkgs/openssl/package-version.txt @@ -1 +1 @@ -1.0.1e +1.0.1h diff --git a/build/pkgs/openssl/patches/openssl-1.0.1c-pod_syntax_error.patch b/build/pkgs/openssl/patches/openssl-1.0.1c-pod_syntax_error.patch deleted file mode 100644 index db11bba65d0..00000000000 --- a/build/pkgs/openssl/patches/openssl-1.0.1c-pod_syntax_error.patch +++ /dev/null @@ -1,36 +0,0 @@ -diff -uNr openssl-1.0.1c-orig/crypto/des/des.pod openssl-1.0.1c-new/crypto/des/des.pod ---- openssl-1.0.1c-orig/crypto/des/des.pod 2013-01-23 11:17:32.000000000 +0100 -+++ openssl-1.0.1c-new/crypto/des/des.pod 2013-01-23 11:17:36.000000000 +0100 -@@ -181,6 +181,8 @@ - output. If there is no name specified after the B<-u>, the name text.des - will be embedded in the header. - -+=back -+ - =head1 SEE ALSO - - ps(1), -diff -uNr openssl-1.0.1c-orig/doc/crypto/X509_STORE_CTX_get_error.pod openssl-1.0.1c-new/doc/crypto/X509_STORE_CTX_get_error.pod ---- openssl-1.0.1c-orig/doc/crypto/X509_STORE_CTX_get_error.pod 2013-01-23 11:17:41.000000000 +0100 -+++ openssl-1.0.1c-new/doc/crypto/X509_STORE_CTX_get_error.pod 2013-01-23 11:17:45.000000000 +0100 -@@ -278,6 +278,8 @@ - an application specific error. This will never be returned unless explicitly - set by an application. - -+=back -+ - =head1 NOTES - - The above functions should be used instead of directly referencing the fields -diff -uNr openssl-1.0.1c-orig/doc/ssl/SSL_CTX_set_client_CA_list.pod openssl-1.0.1c-new/doc/ssl/SSL_CTX_set_client_CA_list.pod ---- openssl-1.0.1c-orig/doc/ssl/SSL_CTX_set_client_CA_list.pod 2001-04-12 18:02:34.000000000 +0200 -+++ openssl-1.0.1c-new/doc/ssl/SSL_CTX_set_client_CA_list.pod 2013-01-23 11:44:24.000000000 +0100 -@@ -70,7 +70,7 @@ - - The operation succeeded. - --=item 0 -+=item 2 - - A failure while manipulating the STACK_OF(X509_NAME) object occurred or - the X509_NAME could not be extracted from B. Check the error stack From 92a3fa3739fb069939c741cb310a0ba3cddad9fe Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sun, 8 Jun 2014 08:56:05 +0200 Subject: [PATCH 227/546] 16007: make doctest test intended case --- src/sage/calculus/calculus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index 7b9a81c514d..09e00675b91 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -1782,7 +1782,7 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): ODE solution constants are treated differently (:trac:`16007`):: sage: from sage.calculus.calculus import symbolic_expression_from_maxima_string as sefms - sage: sefms('%k1*%x + %k2*%y + %c') + sage: sefms('%k1*x + %k2*y + %c') _K1*x + _K2*y + _C """ From 764821553f9374fec7a2a30849a41f61d6d24598 Mon Sep 17 00:00:00 2001 From: vince Date: Sun, 8 Jun 2014 09:54:46 +0100 Subject: [PATCH 228/546] Made get_predecessors a hidden function --- src/sage/game_theory/cooperative_game.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 8c51dc1597a..105ea9c0d46 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -549,7 +549,7 @@ def marginal_of_pi(self, player, pi): sage: integer_game.marginal_of_pi(2, (2, 3, 1)) 12 """ - predecessors, player_and_pred = self.get_predecessors(player, pi) + predecessors, player_and_pred = self._get_predecessors(player, pi) if predecessors is None: predecessors = () else: @@ -558,11 +558,11 @@ def marginal_of_pi(self, player, pi): value = self.ch_f[player_and_pred] - self.ch_f[predecessors] return value - def get_predecessors(self, player, pi): + def _get_predecessors(self, player, pi): r""" Returns a list of all the predecessors of a player in a certain - permutation and the same list including the original player - (used elsewhere). + permutation and the same list including the original player. + This is a hidden function used elsewhere. INPUT: @@ -581,11 +581,11 @@ def get_predecessors(self, player, pi): ....: (2, 3,): 42, ....: (1, 2, 3,): 42} sage: integer_game = CooperativeGame(integer_function) - sage: integer_game.get_predecessors(1, (2, 3, 1)) + sage: integer_game._get_predecessors(1, (2, 3, 1)) ([2, 3], [1, 2, 3]) - sage: integer_game.get_predecessors(2, (2, 3, 1)) + sage: integer_game._get_predecessors(2, (2, 3, 1)) ([], [2]) - sage: integer_game.get_predecessors(3, (2, 3, 1)) + sage: integer_game._get_predecessors(3, (2, 3, 1)) ([2], [2, 3]) """ pred = list(pi[:pi.index(player)]) From bb612962b1ae806d83d75d12e8b381d8c1d7e169 Mon Sep 17 00:00:00 2001 From: vince Date: Sun, 8 Jun 2014 09:56:52 +0100 Subject: [PATCH 229/546] Used list comprehensions in marginal contributions --- src/sage/game_theory/cooperative_game.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 105ea9c0d46..5ebc5157920 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -520,10 +520,8 @@ def _marginal_contributions(self, player): sage: integer_game._marginal_contributions(1) [6, 6, 0, 0, 0, 0] """ - contributions = [] - for pi in permutations(self.player_list): - contributions.append(self.marginal_of_pi(player, pi)) - return contributions + return [self.marginal_of_pi(player, pi) for pi + in permutations(self.player_list)] def marginal_of_pi(self, player, pi): r""" From cdc792ca213b093e30665a0916f8f6083c30d348 Mon Sep 17 00:00:00 2001 From: vince Date: Sun, 8 Jun 2014 10:03:30 +0100 Subject: [PATCH 230/546] Made marginal_of_pi hidden --- src/sage/game_theory/cooperative_game.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 5ebc5157920..69cb75ed234 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -501,6 +501,7 @@ def is_superadditive(self): def _marginal_contributions(self, player): r""" Returns a list of contributions specific to one player. + This is a hidden function used in calculation of Shapley value. INPUT: @@ -520,12 +521,13 @@ def _marginal_contributions(self, player): sage: integer_game._marginal_contributions(1) [6, 6, 0, 0, 0, 0] """ - return [self.marginal_of_pi(player, pi) for pi + return [self._marginal_of_pi(player, pi) for pi in permutations(self.player_list)] - def marginal_of_pi(self, player, pi): + def _marginal_of_pi(self, player, pi): r""" - Returns a value for the players contribution in one permutation. + Returns a value for the contribution of a player in one permutation. + This is a hidden function used in calculation of Shapley value. INPUT: @@ -544,7 +546,7 @@ def marginal_of_pi(self, player, pi): ....: (2, 3,): 42, ....: (1, 2, 3,): 42} sage: integer_game = CooperativeGame(integer_function) - sage: integer_game.marginal_of_pi(2, (2, 3, 1)) + sage: integer_game._marginal_of_pi(2, (2, 3, 1)) 12 """ predecessors, player_and_pred = self._get_predecessors(player, pi) @@ -560,7 +562,7 @@ def _get_predecessors(self, player, pi): r""" Returns a list of all the predecessors of a player in a certain permutation and the same list including the original player. - This is a hidden function used elsewhere. + This is a hidden function used in calculation of Shapley value. INPUT: From 4c64de6fe911e925bdcb389308ab0920f7770d73 Mon Sep 17 00:00:00 2001 From: vince Date: Sun, 8 Jun 2014 10:13:14 +0100 Subject: [PATCH 231/546] Tidied some doc issues with regards to max cols --- src/sage/game_theory/cooperative_game.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 69cb75ed234..2ca06ce8994 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -111,8 +111,11 @@ class CooperativeGame(SageObject): approaches to obtaining the Shapley value of a game. Implementing these would be a worthwhile development. - A characteristic function game \(G=(N,v)\) is monotone if it satisfies \(v(C_2)\geq v(C_1) for all \(C_1\subseteq C_2\). - A characteristic function game \(G=(N,v)\) is super-additive if it satisfies \(v(C_2)\geq v(C_1) for all \(C_1\subseteq C_2\) such that \(C_1\cap\C_2=\emptyset\). + A characteristic function game \(G=(N,v)\) is monotone if it satisfies + \(v(C_2)\geq v(C_1) for all \(C_1\subseteq C_2\). + A characteristic function game \(G=(N,v)\) is super-additive if it satisfies + \(v(C_2)\geq v(C_1) for all \(C_1\subseteq C_2\) such that + \(C_1\cap\C_2=\emptyset\). We can test if a game is Monotonic or Superadditive. :: @@ -127,7 +130,8 @@ class CooperativeGame(SageObject): A 3 player Co-operative Game. - It can be shown that the 'fair' payoff vector, referred to as the Shapley value is given by the following formula: + It can be shown that the 'fair' payoff vector, referred to as the + Shapley value is given by the following formula: \[ \phi_i(G)=\frac{1}{N!}\sum_{\pi\in\Pi_n}\Delta_\pi^G(i) @@ -158,11 +162,14 @@ class CooperativeGame(SageObject): \[\lambda_i=0\] - In other words: if a player does not contribute to any coalition then that player should receive no payoff. + In other words: if a player does not contribute to any coalition + then that player should receive no payoff. * Does it possess the symmetry property? - A payoff vector possesses the symmetry property if \(v(C\cup i)=v(C\cup j)\) for all \(C\in 2^{\Omega}\setminus\{i,j\}\) then: + A payoff vector possesses the symmetry property if + \(v(C\cup i)=v(C\cup j)\) for all \(C\in 2^{\Omega}\setminus\{i,j\}\) + then: \[x_i=x_j\] @@ -178,7 +185,8 @@ class CooperativeGame(SageObject): sage: letter_game.symmetry(payoff_vector) True - Any Payoff Vector can be passed to the game and these properties can once again be tested: + Any Payoff Vector can be passed to the game and these properties + can once again be tested: sage: payoff_vector = {'A': 0, 'C': 35, 'B': 3} sage: letter_game.is_efficient(payoff_vector) @@ -214,7 +222,8 @@ class CooperativeGame(SageObject): sage: letter_game.symmetry({'A': 2, 'C': 35, 'B': 5}) True - Any Payoff Vector can be passed to the game and these properties can once again be tested: + Any Payoff Vector can be passed to the game and these properties can once + again be tested: sage: letter_game.is_efficient({'A': 0, 'C': 35, 'B': 3}) False From 8b91f7c98167ce5f8eec171e757d9e14bbd93c96 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Sun, 8 Jun 2014 10:38:45 +0100 Subject: [PATCH 232/546] tidies up docs --- src/sage/game_theory/cooperative_game.py | 70 +++++++++++++----------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 2ca06ce8994..e01dc324396 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -1,10 +1,10 @@ r""" Co-operative games with N players. -This module implements a **basic** implementation of a characteristic function cooperative game. -The main contribution is a class for a characteristic function game. -Methods to calculate the Shapley value (a fair way of sharing common -resources: https://www.youtube.com/watch?v=aThG4YAFErw) as well as +This module implements a **basic** implementation of a characteristic function +cooperative game. The main contribution is a class for a characteristic +function game. Methods to calculate the Shapley value (a fair way of sharing +common resources: https://www.youtube.com/watch?v=aThG4YAFErw) as well as test properties of the game (monotonicity, super additivity) are also included. AUTHOR: @@ -35,20 +35,19 @@ class CooperativeGame(SageObject): INPUT: - - characteristic_function - a dictionary containing all possible sets of players. - * Key - Each set must be entered as a tuple, not a string. A single element - tuple must end with a comma. + - characteristic_function - a dictionary containing all possible sets of + players. + * Key - Each set must be entered as a tuple, not a string. A single + element tuple must end with a comma. * Value - A real number representing each set of players contribution. - - payoff_vector - default = ``False``, a dictionary can be passed instead but - this will be overwritten if shapley_value is called. - EXAMPLES: - The type of game that is currently implemented is referred to as a Characteristic Function Game. - This is a game on a set $\omega$ of players that is defined by a value function $v:C\to \mathbb{R}$ - where $C=2^{\Omega}$ is set of all coalitions of players. - An example of such a game is shown below: + The type of game that is currently implemented is referred to as a + Characteristic Function Game. This is a game on a set $\omega$ of players + that is defined by a value function $v:C\to \mathbb{R}$ where + $C=2^{\Omega}$ is set of all coalitions of players. An example of such a + game is shown below: \[ v(c) = \begin{cases} @@ -63,10 +62,11 @@ class CooperativeGame(SageObject): \end{cases} \] - The function $v$ can be thought of as as a record of contribution of individuals - and coalitions of individuals. - Of interest, becomes how to fairly share the value of the grand coalition ($\omega$)? - This class allows for such an answer to be formulated by calculating the Shapley value of the game. + The function $v$ can be thought of as as a record of contribution of + individuals and coalitions of individuals. Of interest, becomes how to + fairly share the value of the grand coalition ($\omega$)? This class + allows for such an answer to be formulated by calculating the Shapley + value of the game. Basic example of how to implement a co-operative game. These functions will be used repeatedly in other examples. :: @@ -95,15 +95,18 @@ class CooperativeGame(SageObject): Characteristic function games can be of various types. - The following example implements a (trivial) 8 player characteristic function game: + The following example implements a (trivial) 8 player characteristic + function game: \[v(c)=|c|\text{ for all }c\in 2^{\omega}\] + :: + sage: def simple_characteristic_function(N): ....: return {tuple(coalition) : len(coalition) ....: for coalition in subsets(range(N))} sage: g = CooperativeGame(simple_characteristic_function(8)) - sage: g.shapley_value() + sage: g.shapley_value() # long time {0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1} The above is slow to run and this is due to the dimensionality @@ -113,8 +116,8 @@ class CooperativeGame(SageObject): A characteristic function game \(G=(N,v)\) is monotone if it satisfies \(v(C_2)\geq v(C_1) for all \(C_1\subseteq C_2\). - A characteristic function game \(G=(N,v)\) is super-additive if it satisfies - \(v(C_2)\geq v(C_1) for all \(C_1\subseteq C_2\) such that + A characteristic function game \(G=(N,v)\) is super-additive if it + satisfies \(v(C_2)\geq v(C_1) for all \(C_1\subseteq C_2\) such that \(C_1\cap\C_2=\emptyset\). We can test if a game is Monotonic or Superadditive. :: @@ -124,7 +127,8 @@ class CooperativeGame(SageObject): sage: letter_game.is_superadditive() False - Instances have a basic representation that will display basic information about the game. :: + Instances have a basic representation that will display basic information + about the game. :: sage: letter_game A 3 player Co-operative Game. @@ -137,14 +141,14 @@ class CooperativeGame(SageObject): \phi_i(G)=\frac{1}{N!}\sum_{\pi\in\Pi_n}\Delta_\pi^G(i) \] - where the summation is over the permutations of the players and the marginal - contributions of a player for a given permutation is given as: + where the summation is over the permutations of the players and the + marginal contributions of a player for a given permutation is given as: \[ \Delta_\pi^G(i)=v(S_{\pi}(i)\cup i)-v(S_{\pi}(i)) \] - To compute the Shapley value in Sage is simple: + To compute the Shapley value in Sage is simple. :: sage: letter_game.shapley_value() {'A': 2, 'C': 35, 'B': 5} @@ -158,7 +162,8 @@ class CooperativeGame(SageObject): * The nullplayer property: - If \(\exists\) \(i\) such that \(\(v(C\cup i)=v(C)\)\) for all \(C\in 2^{\Omega}\) then: + If \(\exists\) \(i\) such that \(\(v(C\cup i)=v(C)\)\) for all + \(C\in 2^{\Omega}\) then: \[\lambda_i=0\] @@ -173,7 +178,7 @@ class CooperativeGame(SageObject): \[x_i=x_j\] - If players contribute symmetrically then they should get the same payoff. + If players contribute symmetrically then they should get the same payoff. :: @@ -186,7 +191,7 @@ class CooperativeGame(SageObject): True Any Payoff Vector can be passed to the game and these properties - can once again be tested: + can once again be tested. :: sage: payoff_vector = {'A': 0, 'C': 35, 'B': 3} sage: letter_game.is_efficient(payoff_vector) @@ -223,7 +228,7 @@ class CooperativeGame(SageObject): True Any Payoff Vector can be passed to the game and these properties can once - again be tested: + again be tested. :: sage: letter_game.is_efficient({'A': 0, 'C': 35, 'B': 3}) False @@ -629,7 +634,8 @@ def _latex_(self): Returns the LaTeX code representing the characteristic function. - EXAMPLES:: + EXAMPLES: + Basic description of the game shown when calling the game instance. :: sage: letter_function = {(): 0, @@ -679,7 +685,7 @@ def is_efficient(self, payoff_vector): EXAMPLES: - An efficient payoff_vector.:: + An efficient payoff_vector. :: sage: letter_function = {(): 0, ....: ('A',): 6, From ed82c732c0114fad365d551a7f968c62ae1aad16 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Sun, 8 Jun 2014 11:27:45 +0100 Subject: [PATCH 233/546] minor changes to docs --- src/sage/game_theory/cooperative_game.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index e01dc324396..7049891b2c1 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -545,9 +545,9 @@ def _marginal_of_pi(self, player, pi): INPUT: - -player - A real number or string. + - player - A real number or string. - -pi - A tuple which is the permutation that should be used. + - pi - A tuple which is the permutation that should be used. EXAMPLES:: @@ -607,7 +607,6 @@ def _get_predecessors(self, player, pi): def _repr_(self): r""" - Returns a concise description of the Game. EXAMPLES: @@ -625,13 +624,13 @@ def _repr_(self): sage: letter_game = CooperativeGame(letter_function) sage: letter_game A 3 player Co-operative Game. + """ np = self.number_players return "A %s player Co-operative Game." % np def _latex_(self): r""" - Returns the LaTeX code representing the characteristic function. EXAMPLES: @@ -659,7 +658,6 @@ def _latex_(self): 42,&\text{ if }c=\{A, B, C\}\\ \end{cases} """ - np = self.number_players cf = self.ch_f output = "v(c) = \\begin{cases}\n" for key in sorted(cf.keys(), key=lambda key: len(key)) : @@ -711,7 +709,7 @@ def is_efficient(self, payoff_vector): sage: letter_game.is_efficient({'A': 10, 'B': 14, 'C': 14}) False - A longer example for is_efficient. :: + A longer example for is_efficient. :: sage: long_function = {(): 0, ....: (1,): 0, From e94888b8d549d6fa0799b07fd14c2c2bd96f3cae Mon Sep 17 00:00:00 2001 From: James Campbell Date: Sun, 8 Jun 2014 22:37:23 +0100 Subject: [PATCH 234/546] makes large changes to get docs to build --- src/sage/game_theory/cooperative_game.py | 114 ++++++++++------------- 1 file changed, 51 insertions(+), 63 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 7049891b2c1..c1d5583ae50 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -35,11 +35,11 @@ class CooperativeGame(SageObject): INPUT: - - characteristic_function - a dictionary containing all possible sets of - players. - * Key - Each set must be entered as a tuple, not a string. A single - element tuple must end with a comma. - * Value - A real number representing each set of players contribution. + - ``characteristic_function`` - a dictionary containing all possible sets of players. + * Key - each set must be entered as a tuple, not a string. A + single element tuple must end with a comma. + * Value - A real number + representing each set of players contribution. EXAMPLES: @@ -49,18 +49,18 @@ class CooperativeGame(SageObject): $C=2^{\Omega}$ is set of all coalitions of players. An example of such a game is shown below: - \[ - v(c) = \begin{cases} - 0,&\text{ if }c=\emptyset\\ - 6,&\text{ if }c=\{1\}\\ - 12,&\text{ if }c=\{2\}\\ - 42,&\text{ if }c=\{3\}\\ - 12,&\text{ if }c=\{1,2\}\\ - 42,&\text{ if }c=\{1,3\}\\ - 42,&\text{ if }c=\{2,3\}\\ - 42,&\text{ if }c=\{1, 2,3\}\\ - \end{cases} - \] + $ + v(c) = \begin{cases} + 0,&\text{ if }c=\emptyset\\ + 6,&\text{ if }c=\{1\}\\ + 12,&\text{ if }c=\{2\}\\ + 42,&\text{ if }c=\{3\}\\ + 12,&\text{ if }c=\{1,2\}\\ + 42,&\text{ if }c=\{1,3\}\\ + 42,&\text{ if }c=\{2,3\}\\ + 42,&\text{ if }c=\{1, 2,3\}\\ + \end{cases} + $ The function $v$ can be thought of as as a record of contribution of individuals and coalitions of individuals. Of interest, becomes how to @@ -98,7 +98,7 @@ class CooperativeGame(SageObject): The following example implements a (trivial) 8 player characteristic function game: - \[v(c)=|c|\text{ for all }c\in 2^{\omega}\] + $v(c)=|c|\text{ for all }c\in 2^{\omega}$ :: @@ -114,11 +114,11 @@ class CooperativeGame(SageObject): approaches to obtaining the Shapley value of a game. Implementing these would be a worthwhile development. - A characteristic function game \(G=(N,v)\) is monotone if it satisfies - \(v(C_2)\geq v(C_1) for all \(C_1\subseteq C_2\). - A characteristic function game \(G=(N,v)\) is super-additive if it - satisfies \(v(C_2)\geq v(C_1) for all \(C_1\subseteq C_2\) such that - \(C_1\cap\C_2=\emptyset\). + A characteristic function game $G=(N,v)$ is monotone if it satisfies + $v(C_2)\geq v(C_1)$ for all $C_1\subseteq C_2$. + A characteristic function game $G=(N,v)$ is super-additive if it + satisfies $v(C_2)\geq v(C_1)$ for all $C_1\subseteq C_2$ such that + \[C_1 \cap \C_2 = \emptyset\]. We can test if a game is Monotonic or Superadditive. :: @@ -133,20 +133,15 @@ class CooperativeGame(SageObject): sage: letter_game A 3 player Co-operative Game. - It can be shown that the 'fair' payoff vector, referred to as the Shapley value is given by the following formula: - \[ - \phi_i(G)=\frac{1}{N!}\sum_{\pi\in\Pi_n}\Delta_\pi^G(i) - \] + $\phi_i(G)=\frac{1}{N!}\sum_{\pi\in\Pi_n}\Delta_\pi^G(i)$ where the summation is over the permutations of the players and the marginal contributions of a player for a given permutation is given as: - \[ - \Delta_\pi^G(i)=v(S_{\pi}(i)\cup i)-v(S_{\pi}(i)) - \] + $\Delta_\pi^G(i)=v(S_{\pi}(i)\cup i)-v(S_{\pi}(i))$ To compute the Shapley value in Sage is simple. :: @@ -154,29 +149,20 @@ class CooperativeGame(SageObject): {'A': 2, 'C': 35, 'B': 5} We can test 3 basic properties of a Payoff Vector $lambda$. They are - * Efficiency: - - \[sum_{i=1}^N\lambda_i=v(\Omega)\] - - In other words: no value of the total coalition is lost. - - * The nullplayer property: - - If \(\exists\) \(i\) such that \(\(v(C\cup i)=v(C)\)\) for all - \(C\in 2^{\Omega}\) then: - - \[\lambda_i=0\] - - In other words: if a player does not contribute to any coalition - then that player should receive no payoff. + * Efficiency - $sum_{i=1}^N\lambda_i=v(\Omega)$ + In other words, no value of the total coalition is lost. - * Does it possess the symmetry property? + * The nullplayer property - If $\exists$ $i$ such that + $v(C\cup i)=v(C)$ for all + $C\in 2^{\Omega}$ then, $\lambda_i=0$. + In other words: if a player does not + contribute to any coalition then that + player should receive no payoff. - A payoff vector possesses the symmetry property if - \(v(C\cup i)=v(C\cup j)\) for all \(C\in 2^{\Omega}\setminus\{i,j\}\) - then: - - \[x_i=x_j\] + * Symmetry property - A payoff vector possesses the symmetry property + if $v(C\cup i)=v(C\cup j)$ for all + $C\in 2^{\Omega}\setminus{i,j}$, then + $x_i=x_j$ If players contribute symmetrically then they should get the same payoff. @@ -236,6 +222,7 @@ class CooperativeGame(SageObject): True sage: letter_game.symmetry({'A': 0, 'C': 35, 'B': 3}) True + """ def __init__(self, characteristic_function): @@ -460,7 +447,9 @@ def is_superadditive(self): sage: A_game.is_superadditive() True - An example for is_superadditive with a longer game that returns True. :: + An example for is_superadditive with a longer game that returns True. + + :: sage: long_function = {(): 0, ....: (1,): 0, @@ -482,7 +471,9 @@ def is_superadditive(self): sage: long_game.is_superadditive() True - An example for is_superadditive with a longer game that returns False. :: + An example for is_superadditive with a longer game that returns False. + + :: sage: long_function = {(): 0, ....: (1,): 0, @@ -519,7 +510,7 @@ def _marginal_contributions(self, player): INPUT: - -player - A real number or string. + - ``player`` - A real number or string. EXAMPLES:: @@ -536,7 +527,7 @@ def _marginal_contributions(self, player): [6, 6, 0, 0, 0, 0] """ return [self._marginal_of_pi(player, pi) for pi - in permutations(self.player_list)] + in permutations(self.player_list)] def _marginal_of_pi(self, player, pi): r""" @@ -580,9 +571,9 @@ def _get_predecessors(self, player, pi): INPUT: - - player - A real number or string. + - ``player`` - A real number or string. - - pi - A tuple which is the permutation that should be used. + - ``pi`` - A tuple which is the permutation that should be used. EXAMPLES:: @@ -678,8 +669,7 @@ def is_efficient(self, payoff_vector): INPUT: - - payoff_vector - a dictionary where the key is the player and the - value is their payoff. + - ``payoff_vector`` - a dictionary where the key is the player and the value is their payoff. EXAMPLES: @@ -741,8 +731,7 @@ def nullplayer(self, payoff_vector): INPUT: - - payoff_vector - a dictionary where the key is the player and the - value is their payoff. + - ``payoff_vector`` - a dictionary where the key is the player and the value is their payoff. EXAMPLES: @@ -829,8 +818,7 @@ def symmetry(self, payoff_vector): INPUT: - - payoff_vector - a dictionary where the key is the player and the - value is their payoff. + - ``payoff_vector`` - a dictionary where the key is the player and the value is their payoff. EXAMPLES: From c65e58e4aaa404ac4a40e196d7c296f34f208cd7 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Mon, 9 Jun 2014 02:15:49 +0200 Subject: [PATCH 235/546] trac #15390: roots and eigenvalues over finite fields --- src/sage/matrix/matrix2.pyx | 89 +++++++++++-------- .../rings/algebraic_closure_finite_field.py | 54 +++++++++++ 2 files changed, 108 insertions(+), 35 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index e657f6fb26b..44e42b8d821 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -5287,7 +5287,7 @@ cdef class Matrix(matrix1.Matrix): EXAMPLES:: - sage: a = matrix(QQ, 4, range(16)); a + sage: a = matrix(ZZ, 4, range(16)); a [ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11] @@ -5344,49 +5344,68 @@ cdef class Matrix(matrix1.Matrix): sage: M.eigenvalues(extend=False) [2] + The method also works for matrices over finite fields:: + + sage: M = matrix(GF(3), [[0,1,1],[1,2,0],[2,0,1]]) + sage: ev = M.eigenvalues(); ev + [2*z3 + 2, 2*z3 + 1, 2*z3] + + Similarly as in the case of QQbar, the eigenvalues belong to some + algebraic closure but they can be converted to elements of a finite + field:: + + sage: e = ev[0] + sage: e.parent() + Algebraic closure of Finite Field of size 3 + sage: e.as_finite_field_element() + (Finite Field in z3 of size 3^3, + 2*z3 + 2, + Ring morphism: + From: Finite Field in z3 of size 3^3 + To: Algebraic closure of Finite Field of size 3 + Defn: z3 |--> z3) """ x = self.fetch('eigenvalues') - if not x is None: + if x is not None: if not extend: - x1=Sequence([]) - for i in x: - if i in self.base_ring(): - x1.append(i) - x=x1 + x = Sequence(i for i in x if i in self.base_ring()) return x if not self.base_ring().is_exact(): from warnings import warn warn("Using generic algorithm for an inexact ring, which will probably give incorrect results due to numerical precision issues.") - from sage.rings.qqbar import QQbar - G = self.fcp() # factored charpoly of self. - V = [] - i=0 - for h, e in G: - if h.degree() == 1: - alpha = [-h[0]/h[1]] - V.extend(alpha*e) - else: - if extend: - F = h.root_field('%s%s'%('a',i)) - try: - alpha = F.gen(0).galois_conjugates(QQbar) - except AttributeError: - raise NotImplementedError, "eigenvalues() is not implemented for matrices with eigenvalues that are not in the fraction field of the base ring or in QQbar" - V.extend(alpha*e) - i+=1 - V = Sequence(V) - if extend: - self.cache('eigenvalues', V) if not extend: - V1=Sequence([]) - for i in V: - if i in self.base_ring(): - V1.append(i) - V=V1 - return V + return Sequence(r for r,m in self.charpoly().roots() for _ in xrange(m)) + + # now we need to find a natural algebraic closure for the base ring + K = self.base_ring() + try: + is_field = K.is_field() + except (ValueError,AttributeError): + is_field = False + + if not is_field: + if not K.is_integral_domain(): + raise NotImplementedError("eigenvalues() not implemented for non integral domains") + K = K.fraction_field() + + try: + A = K.algebraic_closure() + except (AttributeError,ValueError): + raise NotImplementedError("algebraic closure is not implemented for %s"%K) + + res = [] + for f,e in self.charpoly().change_ring(K).factor(): + if f.degree() == 1: + res.extend([-f.constant_coefficient()]*e) + else: + for r,ee in f.change_ring(A).roots(): + res.extend([r]*(e*ee)) + eigenvalues = Sequence(res) + self.cache('eigenvalues', eigenvalues) + return eigenvalues def eigenvectors_left(self,extend=True): @@ -13211,7 +13230,7 @@ cdef class Matrix(matrix1.Matrix): sage: A.eigenvalues() Traceback (most recent call last): ... - NotImplementedError: eigenvalues() is not implemented for matrices with eigenvalues that are not in the fraction field of the base ring or in QQbar + NotImplementedError: algebraic closures of finite fields are only implemented for prime fields Subdivisions are optional. :: @@ -13588,7 +13607,7 @@ cdef class Matrix(matrix1.Matrix): sage: A.eigenvalues() Traceback (most recent call last): ... - NotImplementedError: eigenvalues() is not implemented for matrices with eigenvalues that are not in the fraction field of the base ring or in QQbar + NotImplementedError: algebraic closures of finite fields are only implemented for prime fields Companion matrices may be selected as any one of four different types. See the documentation for the companion matrix constructor, diff --git a/src/sage/rings/algebraic_closure_finite_field.py b/src/sage/rings/algebraic_closure_finite_field.py index f2ba912bc92..91fbcaf6456 100644 --- a/src/sage/rings/algebraic_closure_finite_field.py +++ b/src/sage/rings/algebraic_closure_finite_field.py @@ -829,6 +829,60 @@ def some_elements(self): return (self(1), self.gen(2), 1+self.gen(3)) + def _roots_univariate_polynomial(self, p, ring=None, multiplicities=None, algorithm=None): + r""" + Return the roots of the polynomial ``p``. + + EXAMPLES:: + + sage: R. = PolynomialRing(GF(3),'x') + sage: P = x^7 + 2*x^6 + x^5 + x^4 + 2*x^3 + 2*x^2 + x + 1 + sage: K = GF(3).algebraic_closure('t') + sage: r = P.roots(K) # indirect doctest + sage: sorted(r) + [(t7^5 + t7 + 2, 1), + (t7^5 + 2*t7 + 2, 1), + (t7^5 + 2*t7^3 + t7 + 2, 1), + (t7^5 + 2*t7^4 + 2*t7^3 + t7^2 + t7 + 2, 1), + (t7^6 + t7^3 + t7 + 1, 1), + (t7^6 + 2*t7^4 + 2*t7^3 + 2*t7^2 + 2*t7 + 1, 1), + (t7^6 + 2*t7^5 + 2*t7^4 + 2*t7^3 + t7, 1)] + sage: all(P(c) == 0 for c,_ in r) + True + sage: prod((x - c) for c,_ in r) == P + True + """ + from sage.rings.arith import lcm + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + + # first build a polynomial over some finite field + coeffs = [v.as_finite_field_element() for v in p.list()] + levels = [c[0].degree() for c in coeffs] + l = lcm(levels) + F,phi = self.subfield(l) + new_coeffs = [F(c[1]) for c in coeffs] + + # then factor it and for each factor add some root by going to a larger + # finite field if needed + P = PolynomialRing(F, 'x') + + roots = {} # root -> multiplicity + polys = [(g,m,F,phi) for g,m in P(new_coeffs).factor()] + while polys: + g,m,F,phi = polys.pop() + if g.degree() == 1: # got a root ! + c = phi(-g.constant_coefficient()) + if c not in roots: + roots[c] = 0 + roots[c] += m + else: # build an extension where the polynomial splits + FF,pphi = self.subfield(F.degree() * g.degree()) + polys.extend([(gg,mm,FF,pphi) for gg,mm in g.change_ring(FF).factor()]) + if multiplicities: + return list(roots.iteritems()) + return roots.keys() + + class AlgebraicClosureFiniteField_pseudo_conway(AlgebraicClosureFiniteField_generic, WithEqualityById): """ Algebraic closure of a finite field, constructed using From 228cdac7b0062378ab8bdd43161c5d061a110e05 Mon Sep 17 00:00:00 2001 From: Jayant Date: Mon, 9 Jun 2014 00:01:20 -0400 Subject: [PATCH 236/546] Added examples to matroids_plot_helpers.py --- src/sage/matroids/matroid.pyx | 2 +- src/sage/matroids/matroids_plot_helpers.py | 117 +++++++++++++++++++-- 2 files changed, 109 insertions(+), 10 deletions(-) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index 9bffb378251..e106ac8d7f6 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -4751,7 +4751,7 @@ cdef class Matroid(SageObject): cpdef plot(self,B=None,lineorders=None): """ - Return geomrtric representation as a sage graphics object. + Return geometric representation as a sage graphics object. INPUT: diff --git a/src/sage/matroids/matroids_plot_helpers.py b/src/sage/matroids/matroids_plot_helpers.py index 01b86f04ad9..6268b61a683 100644 --- a/src/sage/matroids/matroids_plot_helpers.py +++ b/src/sage/matroids/matroids_plot_helpers.py @@ -51,6 +51,24 @@ def initial_triangle(M,B1,nB1,lps): 3. A list of elements of `M.simplify.groundset()`` that cane be placed inside the triangle in the geometric representation 4. A list of lists of elements of ``M.simplify.groundset()`` that correspond to lines in the geometric representation other than the sides of the triangle + EXAMPLES:: + + sage: from sage.matroids import matroids_plot_helpers + sage: M=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0],[0, 1, 0, 1, 0, 1, 1,0],[0, 0, 1, 1, 1, 0, 1,0]]) + sage: N=M.simplify() + sage: B1=list(N.basis()) + sage: nB1=list(set(M.simplify().groundset())-set(B1)) + sage: pts,trilines,nontripts,curvedlines=matroids_plot_helpers.initial_triangle(M,B1,nB1,M.loops()) + sage: print pts + {1: (1.0, 0.0), 2: (1.5, 1.0), 3: (0.5, 1.0), 4: (0, 0), 5: (1, 2), 6: (2, 0)} + sage: print trilines + [[3, 4, 5], [2, 5, 6], [1, 4, 6]] + sage: print nontripts + [0] + sage: print curvedlines + [[0, 1, 5], [0, 2, 4], [0, 3, 6], [1, 2, 3], [1, 4, 6], [2, 5, 6], [3, 4, 5]] + + .. NOTE:: This method does NOT do any checks. @@ -113,6 +131,13 @@ def trigrid(tripts): 1. Barycenter of 3 input points 2,3,4. Barycenters of 1. with 3 different 2-subsets of input points respectively + EXAMPLES:: + + sage: from sage.matroids import matroids_plot_helpers + sage: points=matroids_plot_helpers.trigrid([[2,1],[4,5],[5,2]]) + sage: print points + [[3.6666666666666665, 2.6666666666666665], [3.222222222222222, 2.888888888888889], [4.222222222222222, 3.222222222222222], [3.5555555555555554, 1.8888888888888886]] + .. NOTE:: This method does NOT do any checks. @@ -127,30 +152,42 @@ def trigrid(tripts): grid.append(pt) return grid -def addnontripts(M,tripts,ptsdict,nontripts): +def addnontripts(tripts_labels,nontripts_labels,ptsdict): """ - Return a grid of 4 points inside given 3 points as a list + Return modified ``ptsdict`` with additional keys and values corresponding to ``nontripts`` INPUT: - - ``M`` -- A matroid. - - ``tripts`` -- A list of labels in ``M.simplify().groundset()`` that can be placed on the sides of the triangle - - ``ptsdict`` -- A dictionary containing labels in ``tripts`` as keys and their (x,y) position as values - - ``nontripts``-- A list of labels in ``M.simplify().groundset()`` that can be placed inside the triangle + - ``tripts`` -- A list of 3 labels that are to be placed on vertices of the triangle + - ``ptsdict`` -- A dictionary (at least) containing labels in ``tripts`` as keys and their (x,y) position as values + - ``nontripts``-- A list of labels whose corresponding points are to be placed inside the triangle OUTPUT: A dictionary containing labels in ``tripts`` as keys and their (x,y) position as values allong with all keys and respective values in ``ptsdict`` + EXAMPLES:: + + sage: from sage.matroids import matroids_plot_helpers + sage: ptsdict={'a':(0,0),'b':(1,2),'c':(2,0)} + sage: ptsdict_1=matroids_plot_helpers.addnontripts(['a','b','c'],['d','e','f'],ptsdict) + sage: print ptsdict_1 + {'a': (0, 0), 'c': (2, 0), 'b': (1, 2), 'e': (0.6666666666666666, 0.8888888888888888), 'd': (1.0, 0.6666666666666666), 'f': (1.3333333333333333, 0.8888888888888888)} + + sage: ptsdict_2=matroids_plot_helpers.addnontripts(['a','b','c'],['d','e','f','g','h'],ptsdict) + sage: print ptsdict_2 + {'a': (0, 0), 'c': (2, 0), 'b': (1, 2), 'e': (0.6666666666666666, 0.8888888888888888), 'd': (1.0, 0.6666666666666666), 'g': (1.0, 0.2222222222222222), 'f': (1.3333333333333333, 0.8888888888888888), 'h': (0.5555555555555555, 0.5185185185185185)} + .. NOTE:: This method does NOT do any checks. """ + tripts=[list(ptsdict[p]) for p in tripts_labels] pairs = [[0,1],[1,2],[0,2]] q = [tripts] - num = len(nontripts) + num = len(nontripts_labels) gridpts = [[float((tripts[0][0]+tripts[1][0]+tripts[2][0])/3),float(tripts[0][1]+tripts[1][1]+tripts[2][1])/3]] n=0 while n that corresponds to the geometric representation of the matroid + EXAMPLES:: + + sage: from sage.matroids import matroids_plot_helpers + sage: M=matroids.named_matroids.P7() + sage: G=matroids_plot_helpers.geomrep(M) + sage: G.show(xmin=-2, xmax=3, ymin=-2, ymax=3) + + sage: M=matroids.named_matroids.P7() + sage: G=matroids_plot_helpers.geomrep(M,lineorders1=[['f','e','d']]) + sage: G.show(xmin=-2, xmax=3, ymin=-2, ymax=3) + + """ + if B1 == None: + B1 = list(M1.basis()) G = Graphics() # create lists of loops and parallel elements and simplify given matroid [M,L,P] = slp(M1) @@ -384,7 +483,7 @@ def geomrep(M1,B1=None,lineorders1=None): G += text(i,(float(pt[0]), float(pt[1])), color='white',fontsize=13) else: pts,trilines,nontripts,curvedlines = initial_triangle(M1,B1,list(set(M.groundset())-set(B1)), list(set(L)|set(P))) #[i for i in sorted(M.groundset()) if i not in B1]) - pts2= addnontripts(M,[pts[B1[0]],pts[B1[1]],pts[B1[2]]],pts,nontripts) + pts2= addnontripts([B1[0],B1[1],B1[2]],nontripts,pts) trilines.extend(curvedlines) j = 0 for ll in trilines: From 9fccb6f7ebda4ef6344d125bcdd9a6b67dfb2deb Mon Sep 17 00:00:00 2001 From: James Campbell Date: Mon, 9 Jun 2014 08:38:55 +0100 Subject: [PATCH 237/546] docs build, but latex not displaying properly --- src/doc/en/reference/game_theory/index.rst | 2 +- src/sage/game_theory/cooperative_game.py | 35 +++++++++++----------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/doc/en/reference/game_theory/index.rst b/src/doc/en/reference/game_theory/index.rst index 7abf79974fb..c027d8e8f5a 100644 --- a/src/doc/en/reference/game_theory/index.rst +++ b/src/doc/en/reference/game_theory/index.rst @@ -1,4 +1,4 @@ -Games +Game Theory ===== .. toctree:: diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index c1d5583ae50..1db8a5e06e6 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -49,18 +49,18 @@ class CooperativeGame(SageObject): $C=2^{\Omega}$ is set of all coalitions of players. An example of such a game is shown below: - $ - v(c) = \begin{cases} - 0,&\text{ if }c=\emptyset\\ - 6,&\text{ if }c=\{1\}\\ - 12,&\text{ if }c=\{2\}\\ - 42,&\text{ if }c=\{3\}\\ - 12,&\text{ if }c=\{1,2\}\\ - 42,&\text{ if }c=\{1,3\}\\ - 42,&\text{ if }c=\{2,3\}\\ - 42,&\text{ if }c=\{1, 2,3\}\\ - \end{cases} - $ + $ + v(c) = \begin{cases} + 0,&\text{ if }c=\emptyset\\ + 6,&\text{ if }c=\{1\}\\ + 12,&\text{ if }c=\{2\}\\ + 42,&\text{ if }c=\{3\}\\ + 12,&\text{ if }c=\{1,2\}\\ + 42,&\text{ if }c=\{1,3\}\\ + 42,&\text{ if }c=\{2,3\}\\ + 42,&\text{ if }c=\{1, 2,3\}\\ + \end{cases} + $ The function $v$ can be thought of as as a record of contribution of individuals and coalitions of individuals. Of interest, becomes how to @@ -98,7 +98,7 @@ class CooperativeGame(SageObject): The following example implements a (trivial) 8 player characteristic function game: - $v(c)=|c|\text{ for all }c\in 2^{\omega}$ + $v(c)=|c|\text{ for all }c\in 2^{\omega}$ :: @@ -115,10 +115,9 @@ class CooperativeGame(SageObject): these would be a worthwhile development. A characteristic function game $G=(N,v)$ is monotone if it satisfies - $v(C_2)\geq v(C_1)$ for all $C_1\subseteq C_2$. - A characteristic function game $G=(N,v)$ is super-additive if it - satisfies $v(C_2)\geq v(C_1)$ for all $C_1\subseteq C_2$ such that - \[C_1 \cap \C_2 = \emptyset\]. + $v(C_2)\geq v(C_1)$ for all $C_1\subseteq C_2$. A characteristic function + game $G=(N,v)$ is super-additive if it satisfies $v(C_2)\geq v(C_1)$ for + all $C_1\subseteq C_2$ such that \[C_1 \cap \C_2 = \emptyset\]. We can test if a game is Monotonic or Superadditive. :: @@ -148,7 +147,7 @@ class CooperativeGame(SageObject): sage: letter_game.shapley_value() {'A': 2, 'C': 35, 'B': 5} - We can test 3 basic properties of a Payoff Vector $lambda$. They are + We can test 3 basic properties of a Payoff Vector $\lambda$. They are * Efficiency - $sum_{i=1}^N\lambda_i=v(\Omega)$ In other words, no value of the total coalition is lost. From eb99aaf700241808b0d20c170c64354b0a04e3b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 9 Jun 2014 21:46:05 +0200 Subject: [PATCH 238/546] trac #16463 random parking function --- src/sage/combinat/parking_functions.py | 249 +++++++++++++++---------- 1 file changed, 154 insertions(+), 95 deletions(-) diff --git a/src/sage/combinat/parking_functions.py b/src/sage/combinat/parking_functions.py index 4f64c89c286..73dda208d1b 100644 --- a/src/sage/combinat/parking_functions.py +++ b/src/sage/combinat/parking_functions.py @@ -3,31 +3,36 @@ INFORMALLY (reference [Beck]_): -Imagine a one-way cul-de-sac with `n` parking spots. We'll give the first parking spot the -number 1, the next one number 2, etc., down to the last one, number `n`. Initially they're -all free, but there are `n` cars approaching the street, and they'd all like to park -there. To make life interesting, every car has a parking preference, and we record the -preferences in a sequence; For example, if `n = 3`, the sequence `(2, 1, 1)` means that -the first car would like to park at spot number 2, the second car prefers parking spot -number 1, and the last car would also like to part at number 1. The street is very narrow, -so there is no way to back up. Now each car enters the street and approaches its preferred -parking spot; if it is free, it parks there, and if not, it moves down the street to the -first available spot. We call a sequence a parking function (of length `n`) if all cars -end up finding a parking spot. For example, the sequence `(2, 1, 1)` is a parking sequence -(of length 3), whereas the sequence `(2, 3, 2)` is not. +Imagine a one-way cul-de-sac with `n` parking spots. We will give the +first parking spot the number 1, the next one number 2, etc., down to +the last one, number `n`. Initially they are all free, but there are +`n` cars approaching the street, and they would all like to park there. +To make life interesting, every car has a parking preference, and we +record the preferences in a sequence; For example, if `n = 3`, the +sequence `(2, 1, 1)` means that the first car would like to park at +spot number 2, the second car prefers parking spot number 1, and the +last car would also like to part at number 1. The street is very +narrow, so there is no way to back up. Now each car enters the street +and approaches its preferred parking spot; if it is free, it parks +there, and if not, it moves down the street to the first available +spot. We call a sequence a parking function (of length `n`) if all +cars end up finding a parking spot. For example, the sequence `(2, 1, +1)` is a parking sequence (of length 3), whereas the sequence `(2, 3, +2)` is not. FORMALLY: -A parking function of size `n` is a sequence `(a_1, \ldots, a_n)` of positive integers -such that if `b_1 \leq b_2 \leq \cdots \leq b_n` is the increasing rearrangement -of `a_1, \ldots, a_n`, then `b_i \leq i`. +A parking function of size `n` is a sequence `(a_1, \ldots, a_n)` of +positive integers such that if `b_1 \leq b_2 \leq \cdots \leq b_n` is +the increasing rearrangement of `a_1, \ldots, a_n`, then `b_i \leq i`. -A parking function of size `n` is a pair `(L, D)` of two sequences `L` and `D` -where `L` is a permutation and `D` is an area sequence of a Dyck path of size n such -that `D[i] \geq 0`, `D[i+1] \leq D[i]+1` and if `D[i+1] = D[i]+1` then `L[i+1] > L[i]`. +A parking function of size `n` is a pair `(L, D)` of two sequences `L` +and `D` where `L` is a permutation and `D` is an area sequence of a +Dyck path of size n such that `D[i] \geq 0`, `D[i+1] \leq D[i]+1` and +if `D[i+1] = D[i]+1` then `L[i+1] > L[i]`. -The number of parking functions of size `n` is equal to the number of rooted forest -on `n` vertices and is equal to `(n+1)^{n-1}`. +The number of parking functions of size `n` is equal to the number of +rooted forests on `n` vertices and is equal to `(n+1)^{n-1}`. REFERENCES: @@ -62,14 +67,17 @@ from sage.rings.all import QQ from copy import copy from sage.combinat.combinat import (CombinatorialClass, CombinatorialObject, - InfiniteAbstractCombinatorialClass) + InfiniteAbstractCombinatorialClass) from sage.combinat.permutation import Permutation, Permutations from sage.combinat.dyck_word import DyckWord from sage.combinat.combinatorial_map import combinatorial_map +from sage.misc.prandom import randint +from sage.rings.finite_rings.integer_mod_ring import Zmod + def ParkingFunctions(n=None): r""" - Returns the combinatorial class of Parking Functions. + Return the combinatorial class of Parking Functions. A *parking function* of size `n` is a sequence `(a_1, \ldots,a_n)` of positive integers such that if `b_1 \leq b_2 \leq \cdots \leq b_n` is @@ -149,15 +157,18 @@ def ParkingFunctions(n=None): """ if n is None: return ParkingFunctions_all() - else: - if not isinstance(n, (Integer, int)) or n<0: - raise ValueError("%s is not a non-negative integer." % n) - return ParkingFunctions_n(n) + + if not isinstance(n, (Integer, int)) or n < 0: + raise ValueError("%s is not a non-negative integer." % n) + return ParkingFunctions_n(n) + def is_a(x, n=None): - """ - Checks whether a list is a parking function. If a size `n` is specified, checks if a - list is a parking function of size `n`. + r""" + Checks whether a list is a parking function. + + If a size `n` is specified, checks if a list is a parking function + of size `n`. TESTS:: @@ -177,6 +188,7 @@ def is_a(x, n=None): from sage.combinat.non_decreasing_parking_function import is_a return is_a(A, n) + class ParkingFunctions_all(InfiniteAbstractCombinatorialClass): def __init__(self): """ @@ -231,6 +243,7 @@ def _infinite_cclass_slice(self, n): """ return ParkingFunctions_n(n) + class ParkingFunctions_n(CombinatorialClass): r""" The combinatorial class of parking functions of size `n`. @@ -280,7 +293,7 @@ def __repr__(self): sage: repr(ParkingFunctions(3)) 'Parking functions of size 3' """ - return "Parking functions of size %s"%(self.n) + return "Parking functions of size %s" % self.n def __contains__(self, x): """ @@ -307,24 +320,25 @@ def __contains__(self, x): def cardinality(self): r""" - Returns the number of parking functions of size ``n``. The cardinality is equal - to `(n+1)^{n-1}`. + Return the number of parking functions of size ``n``. + + The cardinality is equal to `(n+1)^{n-1}`. EXAMPLES:: sage: [ParkingFunctions(i).cardinality() for i in range(6)] [1, 1, 3, 16, 125, 1296] """ - return Integer((self.n+1)**(self.n-1)) + return Integer((self.n + 1) ** (self.n - 1)) def __iter__(self): """ - Returns an iterator for parking functions of size `n`. + Return an iterator for parking functions of size `n`. .. warning:: - The precise order in which the parking function are generated is not fixed, - and may change in the future. + The precise order in which the parking function are + generated is not fixed, and may change in the future. EXAMPLES:: @@ -360,12 +374,14 @@ def iterator_rec(n): sage: [e for e in PF] # indirect doctest [[1, 1], [1, 2], [2, 1]] """ - if n==0: - yield [ ]; return - if n==1: - yield [1]; return - for res1 in iterator_rec(n-1): - for i in range(res1[-1], n+1): + if n == 0: + yield [] + return + if n == 1: + yield [1] + return + for res1 in iterator_rec(n - 1): + for i in range(res1[-1], n + 1): res = copy(res1) res.append(i) yield res @@ -375,9 +391,36 @@ def iterator_rec(n): yield ParkingFunction(list(pi)) return -def ParkingFunction(pf=None, labelling=None, area_sequence=None, labelled_dyck_word = None): + def random_element(self): + """ + Return a random parking function of size `n` + + The algorithm uses a circular parking space with `n+1` + slots. Then all `n` cars can parks and there remains one empty + slot. Slots are then renumbered so that the empty slot is `0`. + + EXAMPLES:: + + sage: pf = ParkingFunctions(8) + sage: a = pf.random_element(); a # random + [5, 7, 2, 4, 2, 5, 1, 3] + """ + n = self.n + Zm = Zmod(n + 1) + fun = [Zm(randint(0, n)) for i in range(n)] + free = [Zm(j) for j in range(n + 1)] + for car in fun: + position = car + while not(position in free): + position += Zm.one() + free.remove(position) + return self([i - free[0] for i in fun]) + + +def ParkingFunction(pf=None, labelling=None, area_sequence=None, + labelled_dyck_word=None): r""" - Returns the combinatorial class of Parking Functions. + Return the combinatorial class of Parking Functions. A *parking function* of size `n` is a sequence `(a_1, \ldots,a_n)` of positive integers such that if `b_1 \leq b_2 \leq \cdots \leq b_n` is @@ -439,18 +482,20 @@ def ParkingFunction(pf=None, labelling=None, area_sequence=None, labelled_dyck_w elif labelling is not None: if (area_sequence is None): raise ValueError("must also provide area sequence along with labelling.") - if (len(area_sequence)!=len(labelling)): - raise ValueError("%s must be the same size as the labelling %s"%(area_sequence,labelling)) - if any(area_sequence[i]labelling[i+1] for i in range(len(labelling)-1)): - raise ValueError("%s is not a valid labeling of area sequence %s"%(labelling, area_sequence)) - return from_labelling_and_area_sequence( labelling, area_sequence ) + if (len(area_sequence) != len(labelling)): + raise ValueError("%s must be the same size as the labelling %s" % (area_sequence, labelling)) + if any(area_sequence[i] < area_sequence[i+1] and labelling[i] > labelling[i + 1] for i in range(len(labelling) - 1)): + raise ValueError("%s is not a valid labeling of area sequence %s" % (labelling, area_sequence)) + return from_labelling_and_area_sequence(labelling, area_sequence) elif labelled_dyck_word is not None: return from_labelled_dyck_word(labelled_dyck_word) elif area_sequence is not None: DW = DyckWord(area_sequence) - return ParkingFunction(labelling=range(1,DW.size()+1), area_sequence=DW) - else: - raise ValueError("did not manage to make this into a parking function") + return ParkingFunction(labelling=range(1, DW.size() + 1), + area_sequence=DW) + + raise ValueError("did not manage to make this into a parking function") + class ParkingFunction_class(CombinatorialObject): def __init__(self, lst): @@ -538,18 +583,20 @@ def diagonal_reading_word(self): L = self.to_labelling_permutation() D = self.to_area_sequence() m = max(D) - return Permutation([L[-j-1] for i in range(m+1) for j in range(len(L)) if D[-j-1]==m-i]) + return Permutation([L[-j - 1] for i in range(m + 1) + for j in range(len(L)) if D[-j - 1] == m - i]) diagonal_word = diagonal_reading_word def parking_permutation(self): # indices are cars, entries are parking spaces r""" - Returns the sequence of parking spots that are taken by cars 1 through `n` - and corresponding to the parking function. - For example, ``parking_permutation(PF) = [6, 1, 5, 2, 3, 4, 7]`` - means that spot 6 is taken by car 1, spot 1 by car 2, spot 5 by car 3, spot 2 is - taken by car 4, spot 3 is taken by car 5, spot 4 is taken by car 6 and spot 7 - is taken by car 7. + Returns the sequence of parking spots that are taken by cars 1 + through `n` and corresponding to the parking function. + + For example, ``parking_permutation(PF) = [6, 1, 5, 2, 3, 4, + 7]`` means that spot 6 is taken by car 1, spot 1 by car 2, + spot 5 by car 3, spot 2 is taken by car 4, spot 3 is taken by + car 5, spot 4 is taken by car 6 and spot 7 is taken by car 7. INPUT: @@ -557,8 +604,9 @@ def parking_permutation(self): # indices are cars, entries are parking space OUTPUT: - - returns the permutation of parking spots that corresponds to the parking - function and which is the same size as parking function + - returns the permutation of parking spots that corresponds to + the parking function and which is the same size as parking + function EXAMPLES:: @@ -612,11 +660,11 @@ def cars_permutation(self): # indices are parking spaces, entries are car la """ out = {} for i in range(len(self)): - j=0 - while self[i]+j in out.keys(): - j+=1 - out[self[i]+j] = i - return Permutation([out[i+1]+1 for i in range(len(self))]) + j = 0 + while self[i] + j in out.keys(): + j += 1 + out[self[i] + j] = i + return Permutation([out[i + 1] + 1 for i in range(len(self))]) def jump_list(self): # cars displacements r""" @@ -656,10 +704,12 @@ def jump_list(self): # cars displacements out.append(pi[i] - self[i]) return out - def jump(self): #sum of all jumps, sum of all dispalcements + def jump(self): # sum of all jumps, sum of all displacements r""" - Returns the sum of the differences between the parked and preferred parking spots - (see [Shin]_ p. 18). + Returns the sum of the differences between the parked and + preferred parking spots + + See [Shin]_ p. 18. INPUT: @@ -717,8 +767,7 @@ def lucky_cars(self): # the set of cars that can park in their preferred spo [1, 2, 3] """ w = self.jump_list() - return [i+1 for i in range(len(w)) if w[i]==0] - + return [i + 1 for i in range(len(w)) if w[i] == 0] def luck(self): # the number of lucky cars r""" @@ -781,7 +830,8 @@ def primary_dinversion_pairs(self): """ L = self.to_labelling_permutation() D = self.to_area_sequence() - return [(i,j) for j in range(len(D)) for i in range(j) if D[i] == D[j] and L[i] < L[j]] + return [(i, j) for j in range(len(D)) for i in range(j) + if D[i] == D[j] and L[i] < L[j]] def secondary_dinversion_pairs(self): r""" @@ -814,7 +864,8 @@ def secondary_dinversion_pairs(self): """ L = self.to_labelling_permutation() D = self.to_area_sequence() - return [(i,j) for j in range(len(D)) for i in range(j) if D[i] == D[j] + 1 and L[i] > L[j]] + return [(i, j) for j in range(len(D)) for i in range(j) + if D[i] == D[j] + 1 and L[i] > L[j]] def dinversion_pairs(self): r""" @@ -1010,7 +1061,7 @@ def touch_points(self): """ return self.to_dyck_word().touch_points() - @combinatorial_map(name = 'to touch composition') + @combinatorial_map(name='to touch composition') def touch_composition(self): r""" Returns the composition of the labelled Dyck path corresponding to the @@ -1046,7 +1097,7 @@ def touch_composition(self): diagonal_composition = touch_composition - @combinatorial_map(name = 'to labelling permutation') + @combinatorial_map(name='to labelling permutation') def to_labelling_permutation(self): r""" Returns the labelling of the support Dyck path of the parking function. @@ -1242,7 +1293,7 @@ def to_labelling_dyck_word_pair(self): """ return (self.to_labelling_permutation(), self.to_dyck_word()) - @combinatorial_map(name = 'to non-decreasing parking function') + @combinatorial_map(name='to non-decreasing parking function') def to_NonDecreasingParkingFunction(self): r""" Returns the non-decreasing parking function which underlies the parking @@ -1277,12 +1328,14 @@ def to_NonDecreasingParkingFunction(self): """ return ParkingFunction(sorted(self)) - def characteristic_quasisymmetric_function(self, q=None, R=QQ['q','t'].fraction_field()): + def characteristic_quasisymmetric_function(self, q=None, + R=QQ['q', 't'].fraction_field()): r""" - The characteristic function of the Parking Function is the sum over all permutation - labelling of the Dyck path `q^{dinv(PF)} F_{ides(PF)}` where `ides(PF)` is - :meth:`ides_composition` is the descent composition of diagonal reading word of - the parking function. + The characteristic function of the Parking Function is the sum + over all permutation labellings of the Dyck path `q^{dinv(PF)} + F_{ides(PF)}` where `ides(PF)` is :meth:`ides_composition` is + the descent composition of diagonal reading word of the + parking function. INPUT: @@ -1318,23 +1371,25 @@ def characteristic_quasisymmetric_function(self, q=None, R=QQ['q','t'].fraction_ """ from sage.combinat.ncsf_qsym.qsym import QuasiSymmetricFunctions if q is None: - q=R('q') + q = R('q') else: if not q in R: - raise ValueError("q=%s must be an element of the base ring %s"%(q,R)) + raise ValueError("q=%s must be an element of the base ring %s" % (q, R)) F = QuasiSymmetricFunctions(R).Fundamental() - return q**self.dinv()*F(self.ides_composition()) + return q ** self.dinv() * F(self.ides_composition()) def pretty_print(self, underpath=True): r""" - Displays a parking function as a lattice path consisting of a Dyck path - and a labelling with the labels displayed along the edges of the Dyck path. + Displays a parking function as a lattice path consisting of a + Dyck path and a labelling with the labels displayed along the + edges of the Dyck path. INPUT: - - ``underpath`` - if the length of the parking function is less than or - equal to 9 then display the labels under the path if ``underpath`` is True - otherwise display them to the right of the path (default: True) + - ``underpath`` -- if the length of the parking function is + less than or equal to 9 then display the labels under the + path if ``underpath`` is True otherwise display them to the + right of the path (default: ``True``) EXAMPLES:: @@ -1414,13 +1469,15 @@ def pretty_print(self, underpath=True): L = self.to_labelling_permutation() dw = self.to_dyck_word() if len(L) <= 9: - dw.pretty_print(labelling=L, underpath = underpath) + dw.pretty_print(labelling=L, underpath=underpath) else: - dw.pretty_print(labelling=L, underpath = False) + dw.pretty_print(labelling=L, underpath=False) #****************************************************************************** # CONSTRUCTIONS #****************************************************************************** + + def from_labelling_and_area_sequence(L, D): r""" Returns the parking function corresponding to the labelling area sequence pair. @@ -1453,7 +1510,9 @@ def from_labelling_and_area_sequence(L, D): sage: from_labelling_and_area_sequence([1, 2, 4, 3], [0, 1, 2, 1]) [1, 1, 3, 1] """ - return ParkingFunction_class([L.index(i)+1-D[L.index(i)] for i in range(1,len(L)+1)]) + return ParkingFunction_class([L.index(i) + 1 - D[L.index(i)] + for i in range(1, len(L) + 1)]) + def from_labelled_dyck_word(LDW): r""" @@ -1465,8 +1524,8 @@ def from_labelled_dyck_word(LDW): OUTPUT: - - returns the parking function corresponding to the labelled Dyck word that is - half the size of ``LDW`` + - returns the parking function corresponding to the labelled Dyck + word that is half the size of ``LDW`` EXAMPLES:: @@ -1484,6 +1543,6 @@ def from_labelled_dyck_word(LDW): sage: from_labelled_dyck_word([2, 4, 0, 1, 0, 0, 3, 0]) [2, 1, 4, 1] """ - L = [ell for ell in LDW if ell!=0] + L = [ell for ell in LDW if ell != 0] D = DyckWord(map(lambda x: Integer(not x.is_zero()), LDW)) return from_labelling_and_area_sequence(L, D.to_area_sequence()) From eb1a387e7d3760ed93bdb23bf132072248395b8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 9 Jun 2014 21:54:01 +0200 Subject: [PATCH 239/546] trac #16463 better, now really a parking function --- src/sage/combinat/parking_functions.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/parking_functions.py b/src/sage/combinat/parking_functions.py index 73dda208d1b..db26bfea2ef 100644 --- a/src/sage/combinat/parking_functions.py +++ b/src/sage/combinat/parking_functions.py @@ -273,8 +273,8 @@ class ParkingFunctions_n(CombinatorialClass): .. warning:: - The precise order in which the parking function are generated or - listed is not fixed, and may change in the future. + The precise order in which the parking function are generated or + listed is not fixed, and may change in the future. """ def __init__(self, n): """ @@ -404,6 +404,8 @@ def random_element(self): sage: pf = ParkingFunctions(8) sage: a = pf.random_element(); a # random [5, 7, 2, 4, 2, 5, 1, 3] + sage: a in pf + True """ n = self.n Zm = Zmod(n + 1) @@ -414,7 +416,7 @@ def random_element(self): while not(position in free): position += Zm.one() free.remove(position) - return self([i - free[0] for i in fun]) + return ParkingFunction([(i - free[0]).lift() for i in fun]) def ParkingFunction(pf=None, labelling=None, area_sequence=None, From 37fe5f952ee85470ea6671af5e38cf2e67a1d711 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 10 Jun 2014 09:33:19 +0200 Subject: [PATCH 240/546] trac #16463 reviewers' comments --- src/sage/combinat/parking_functions.py | 64 ++++++++++++++------------ 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/src/sage/combinat/parking_functions.py b/src/sage/combinat/parking_functions.py index db26bfea2ef..2f13f8cab4b 100644 --- a/src/sage/combinat/parking_functions.py +++ b/src/sage/combinat/parking_functions.py @@ -101,10 +101,8 @@ def ParkingFunctions(n=None): [1, 2, 2], [2, 1, 2], [2, 2, 1], [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] - If no size is specified, then ParkingFunctions returns the combinatorial class of - all parking functions. - - :: + If no size is specified, then ParkingFunctions returns the + combinatorial class of all parking functions. :: sage: PF = ParkingFunctions(); PF Parking functions @@ -396,8 +394,11 @@ def random_element(self): Return a random parking function of size `n` The algorithm uses a circular parking space with `n+1` - slots. Then all `n` cars can parks and there remains one empty - slot. Slots are then renumbered so that the empty slot is `0`. + spots. Then all `n` cars can park and there remains one empty + spot. Spots are then renumbered so that the empty spot is `0`. + + The probability distribution is uniform on the set of + `(n+1)^(n-1)` parking functions of size `n`. EXAMPLES:: @@ -517,8 +518,9 @@ def __getitem__(self, n): .. NOTE:: - Note that this is different than the image of ``n`` under function. It is - "off by one" in that it agrees with sage indexing starting at 0. + Note that this is different than the image of ``n`` under + function. It is "off by one" in that it agrees with sage + indexing starting at 0. EXAMPLES:: @@ -606,7 +608,7 @@ def parking_permutation(self): # indices are cars, entries are parking space OUTPUT: - - returns the permutation of parking spots that corresponds to + - the permutation of parking spots that corresponds to the parking function and which is the same size as parking function @@ -632,6 +634,7 @@ def cars_permutation(self): # indices are parking spaces, entries are car la r""" Returns the sequence of cars that take parking spots 1 through `n` and corresponding to the parking function. + For example, ``cars_permutation(PF) = [2, 4, 5, 6, 3, 1, 7]`` means that car 2 takes spots 1, car 4 takes spot 2, ..., car 1 takes spot 6 and car 7 takes spot 7. @@ -642,7 +645,7 @@ def cars_permutation(self): # indices are parking spaces, entries are car la OUTPUT: - - returns the permutation of cars corresponding to the parking function + - the permutation of cars corresponding to the parking function and which is the same size as parking function EXAMPLES:: @@ -671,6 +674,7 @@ def cars_permutation(self): # indices are parking spaces, entries are car la def jump_list(self): # cars displacements r""" Returns the displacements of cars that corresponds to the parking function. + For example, ``jump_list(PF) = [0, 0, 0, 0, 1, 3, 2]`` means that car 1 through 4 parked in their preferred spots, car 5 had to park one spot farther (jumped or was displaced by one spot), @@ -682,7 +686,7 @@ def jump_list(self): # cars displacements OUTPUT: - - returns the displacements sequence of parked cars which corresponds + - the displacements sequence of parked cars which corresponds to the parking function and which is the same size as parking function EXAMPLES:: @@ -719,7 +723,7 @@ def jump(self): # sum of all jumps, sum of all displacements OUTPUT: - - returns the sum of the differences between the parked and preferred parking + - the sum of the differences between the parked and preferred parking spots EXAMPLES:: @@ -751,7 +755,7 @@ def lucky_cars(self): # the set of cars that can park in their preferred spo OUTPUT: - - returns the cars that can park in their preferred spots + - the cars that can park in their preferred spots EXAMPLES:: @@ -782,7 +786,7 @@ def luck(self): # the number of lucky cars OUTPUT: - - returns the number of cars that parked in their preferred parking spots + - the number of cars that parked in their preferred parking spots EXAMPLES:: @@ -812,7 +816,7 @@ def primary_dinversion_pairs(self): OUTPUT: - - returns the pairs `(i, j)` such that `i < j`, and `i^{th}` area = `j^{th}` area, + - the pairs `(i, j)` such that `i < j`, and `i^{th}` area = `j^{th}` area, and `i^{th}` label < `j^{th}` label EXAMPLES:: @@ -846,7 +850,7 @@ def secondary_dinversion_pairs(self): OUTPUT: - - returns the pairs `(i, j)` such that `i < j`, and `i^{th}` area = `j^{th}` area +1, + - the pairs `(i, j)` such that `i < j`, and `i^{th}` area = `j^{th}` area +1, and `i^{th}` label > `j^{th}` label EXAMPLES:: @@ -880,7 +884,7 @@ def dinversion_pairs(self): OUTPUT: - - returns the primary and secondary diversion pairs + - the primary and secondary diversion pairs EXAMPLES:: @@ -911,7 +915,7 @@ def dinv(self): OUTPUT: - - returns the number of dinversion pairs + - the number of dinversion pairs EXAMPLES:: @@ -940,7 +944,7 @@ def area(self): OUTPUT: - - returns the sum of squares under and over the main diagonal the Dyck Path, + - the sum of squares under and over the main diagonal the Dyck Path, corresponding to the parking function EXAMPLES:: @@ -1043,7 +1047,7 @@ def touch_points(self): OUTPUT: - - returns the sequence of touch points after the initial step of the + - the sequence of touch points after the initial step of the labelled Dyck path that corresponds to the parking function EXAMPLES:: @@ -1077,7 +1081,7 @@ def touch_composition(self): OUTPUT: - - returns the length between the corresponding touch points which + - the length between the corresponding touch points which of the labelled Dyck path that corresponds to the parking function EXAMPLES:: @@ -1110,7 +1114,7 @@ def to_labelling_permutation(self): OUTPUT: - - returns the labelling of the Dyck path + - the labelling of the Dyck path EXAMPLES:: @@ -1140,7 +1144,7 @@ def to_area_sequence(self): OUTPUT: - - returns area sequence of the Dyck path + - the area sequence of the Dyck path EXAMPLES:: @@ -1204,7 +1208,7 @@ def to_dyck_word(self): OUTPUT: - - returns the Dyck word of the corresponding parking function + - the Dyck word of the corresponding parking function .. SEEALSO:: :meth:`DyckWord` @@ -1237,7 +1241,7 @@ def to_labelled_dyck_word(self): OUTPUT: - - returns the labelled Dyck word of the corresponding parking function + - the labelled Dyck word of the corresponding parking function which is twice the size of parking function word EXAMPLES:: @@ -1257,7 +1261,7 @@ def to_labelled_dyck_word(self): """ dw = self.to_dyck_word() out = list(copy(self.to_labelling_permutation())) - for i in range(2*len(out)): + for i in range(2 * len(out)): if dw[i] == 0: out.insert(i, 0) return out @@ -1273,7 +1277,7 @@ def to_labelling_dyck_word_pair(self): OUTPUT: - - returns the pair ``(L, D)``, where ``L`` is the labelling and ``D`` is + - the pair ``(L, D)``, where ``L`` is the labelling and ``D`` is the Dyck word of the parking function .. SEEALSO:: :meth:`DyckWord` @@ -1307,7 +1311,7 @@ def to_NonDecreasingParkingFunction(self): OUTPUT: - - returns a sorted parking function + - a sorted parking function .. SEEALSO:: :meth:`NonDecreasingParkingFunction` @@ -1492,7 +1496,7 @@ def from_labelling_and_area_sequence(L, D): OUTPUT: - - returns the parking function corresponding the labelling permutation ``L`` + - the parking function corresponding the labelling permutation ``L`` and ``D`` an area sequence of the corresponding Dyck path EXAMPLES:: @@ -1526,7 +1530,7 @@ def from_labelled_dyck_word(LDW): OUTPUT: - - returns the parking function corresponding to the labelled Dyck + - the parking function corresponding to the labelled Dyck word that is half the size of ``LDW`` EXAMPLES:: From 15f628de9a8f311b93da55f238579449b2c91d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 10 Jun 2014 10:05:26 +0200 Subject: [PATCH 241/546] trac #16463 doc of characteristic quasisymmetric function --- src/sage/combinat/parking_functions.py | 27 ++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/sage/combinat/parking_functions.py b/src/sage/combinat/parking_functions.py index 2f13f8cab4b..faa9ced1bb1 100644 --- a/src/sage/combinat/parking_functions.py +++ b/src/sage/combinat/parking_functions.py @@ -88,8 +88,8 @@ def ParkingFunctions(n=None): of a Dyck Path of size n such that `D[i] \geq 0`, `D[i+1] \leq D[i]+1` and if `D[i+1] = D[i]+1` then `L[i+1] > L[i]`. - The number of parking functions of size `n` is equal to the number of rooted forest - on `n` vertices and is equal to `(n+1)^{n-1}`. + The number of parking functions of size `n` is equal to the number + of rooted forests on `n` vertices and is equal to `(n+1)^{n-1}`. EXAMPLES: @@ -255,8 +255,8 @@ class ParkingFunctions_n(CombinatorialClass): of a Dyck Path of size `n` such that `D[i] \geq 0`, `D[i+1] \leq D[i]+1` and if `D[i+1] = D[i]+1` then `L[i+1] > L[i]`. - The number of parking functions of size `n` is equal to the number of rooted forest - on `n` vertices and is equal to `(n+1)^{n-1}`. + The number of parking functions of size `n` is equal to the number + of rooted forests on `n` vertices and is equal to `(n+1)^{n-1}`. EXAMPLES:: @@ -398,7 +398,7 @@ def random_element(self): spot. Spots are then renumbered so that the empty spot is `0`. The probability distribution is uniform on the set of - `(n+1)^(n-1)` parking functions of size `n`. + `(n+1)^{n-1}` parking functions of size `n`. EXAMPLES:: @@ -434,8 +434,8 @@ def ParkingFunction(pf=None, labelling=None, area_sequence=None, of a Dyck Path of size `n` such that `D[i] \geq 0`, `D[i+1] \leq D[i]+1` and if `D[i+1] = D[i]+1` then `L[i+1] > L[i]`. - The number of parking functions of size `n` is equal to the number of rooted forests - on `n` vertices and is equal to `(n+1)^{n-1}`. + The number of parking functions of size `n` is equal to the number + of rooted forests on `n` vertices and is equal to `(n+1)^{n-1}`. INPUT: @@ -1337,18 +1337,21 @@ def to_NonDecreasingParkingFunction(self): def characteristic_quasisymmetric_function(self, q=None, R=QQ['q', 't'].fraction_field()): r""" + Return the characteristic quasisymmetric function of ``self`` + The characteristic function of the Parking Function is the sum over all permutation labellings of the Dyck path `q^{dinv(PF)} - F_{ides(PF)}` where `ides(PF)` is :meth:`ides_composition` is + F_{ides(PF)}` where `ides(PF)` (:meth:`ides_composition`) is the descent composition of diagonal reading word of the parking function. INPUT: - - ``q`` -- (default: ``q = R('q')``) a parameter for the generating function power + - ``q`` -- (default: ``q = R('q')``) a parameter for the + generating function power - - ``R`` -- (default: ``R = QQ['q','t'].fraction_field()``) the base ring to do - the calculations over + - ``R`` -- (default: ``R = QQ['q','t'].fraction_field()``) the + base ring to do the calculations over OUTPUT: @@ -1356,7 +1359,7 @@ def characteristic_quasisymmetric_function(self, q=None, EXAMPLES:: - sage: R=QQ['q','t'].fraction_field() + sage: R = QQ['q','t'].fraction_field() sage: (q,t) = R.gens() sage: cqf = sum(t**PF.area()*PF.characteristic_quasisymmetric_function() for PF in ParkingFunctions(3)); cqf (q^3+q^2*t+q*t^2+t^3+q*t)*F[1, 1, 1] + (q^2+q*t+t^2+q+t)*F[1, 2] + (q^2+q*t+t^2+q+t)*F[2, 1] + F[3] From b79185930890333c38be73140e5a74481ef2f74f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 10 Jun 2014 10:33:38 +0200 Subject: [PATCH 242/546] trac #16463 taking care of "Returns" --- src/sage/combinat/parking_functions.py | 115 ++++++++++++++----------- 1 file changed, 65 insertions(+), 50 deletions(-) diff --git a/src/sage/combinat/parking_functions.py b/src/sage/combinat/parking_functions.py index faa9ced1bb1..d99efa8ed25 100644 --- a/src/sage/combinat/parking_functions.py +++ b/src/sage/combinat/parking_functions.py @@ -163,7 +163,7 @@ def ParkingFunctions(n=None): def is_a(x, n=None): r""" - Checks whether a list is a parking function. + Check whether a list is a parking function. If a size `n` is specified, checks if a list is a parking function of size `n`. @@ -390,8 +390,8 @@ def iterator_rec(n): return def random_element(self): - """ - Return a random parking function of size `n` + r""" + Return a random parking function of size `n`. The algorithm uses a circular parking space with `n+1` spots. Then all `n` cars can park and there remains one empty @@ -514,7 +514,7 @@ def __init__(self, lst): def __getitem__(self, n): """ - Returns the `n^{th}` item in the underlying list. + Return the `n^{th}` item in the underlying list. .. NOTE:: @@ -534,7 +534,7 @@ def __getitem__(self, n): def __call__(self, n): """ - Returns the image of ``n`` under the parking function. + Return the image of ``n`` under the parking function. EXAMPLES:: @@ -548,7 +548,7 @@ def __call__(self, n): def diagonal_reading_word(self): r""" - Returns a diagonal word of the labelled Dyck path corresponding to parking + Return a diagonal word of the labelled Dyck path corresponding to parking function (see [Hag08]_ p. 75). INPUT: @@ -594,7 +594,7 @@ def diagonal_reading_word(self): def parking_permutation(self): # indices are cars, entries are parking spaces r""" - Returns the sequence of parking spots that are taken by cars 1 + Return the sequence of parking spots that are taken by cars 1 through `n` and corresponding to the parking function. For example, ``parking_permutation(PF) = [6, 1, 5, 2, 3, 4, @@ -632,7 +632,7 @@ def parking_permutation(self): # indices are cars, entries are parking space @combinatorial_map(name='to car permutation') def cars_permutation(self): # indices are parking spaces, entries are car labels r""" - Returns the sequence of cars that take parking spots 1 through `n` + Return the sequence of cars that take parking spots 1 through `n` and corresponding to the parking function. For example, ``cars_permutation(PF) = [2, 4, 5, 6, 3, 1, 7]`` @@ -673,7 +673,7 @@ def cars_permutation(self): # indices are parking spaces, entries are car la def jump_list(self): # cars displacements r""" - Returns the displacements of cars that corresponds to the parking function. + Return the displacements of cars that corresponds to the parking function. For example, ``jump_list(PF) = [0, 0, 0, 0, 1, 3, 2]`` means that car 1 through 4 parked in their preferred spots, @@ -712,8 +712,8 @@ def jump_list(self): # cars displacements def jump(self): # sum of all jumps, sum of all displacements r""" - Returns the sum of the differences between the parked and - preferred parking spots + Return the sum of the differences between the parked and + preferred parking spots. See [Shin]_ p. 18. @@ -745,7 +745,7 @@ def jump(self): # sum of all jumps, sum of all displacements def lucky_cars(self): # the set of cars that can park in their preferred spots r""" - Returns the cars that can park in their preferred spots. For example, + Return the cars that can park in their preferred spots. For example, ``lucky_cars(PF) = [1, 2, 7]`` means that cars 1, 2 and 7 parked in their preferred spots and all the other cars did not. @@ -777,7 +777,7 @@ def lucky_cars(self): # the set of cars that can park in their preferred spo def luck(self): # the number of lucky cars r""" - Returns the number of cars that parked in their preferred parking spots + Return the number of cars that parked in their preferred parking spots (see [Shin]_ p. 33). INPUT: @@ -807,7 +807,7 @@ def luck(self): # the number of lucky cars def primary_dinversion_pairs(self): r""" - Returns the primary descent inversion pairs of a labelled Dyck path corresponding + Return the primary descent inversion pairs of a labelled Dyck path corresponding to the parking function. INPUT: @@ -841,7 +841,7 @@ def primary_dinversion_pairs(self): def secondary_dinversion_pairs(self): r""" - Returns the secondary descent inversion pairs of a labelled Dyck path + Return the secondary descent inversion pairs of a labelled Dyck path corresponding to the parking function. INPUT: @@ -875,8 +875,8 @@ def secondary_dinversion_pairs(self): def dinversion_pairs(self): r""" - Returns the descent inversion pairs of a labelled Dyck path corresponding - to the parking function. + Return the descent inversion pairs of a labelled Dyck path + corresponding to the parking function. INPUT: @@ -905,9 +905,10 @@ def dinversion_pairs(self): def dinv(self): r""" - Returns the number of inversions of a labelled Dyck path corresponding - to the parking function (see [Hag08]_ p. 74). Same as the cardinality of - :meth:`dinversion_pairs`. + Return the number of inversions of a labelled Dyck path corresponding + to the parking function (see [Hag08]_ p. 74). + + Same as the cardinality of :meth:`dinversion_pairs`. INPUT: @@ -936,7 +937,8 @@ def dinv(self): def area(self): r""" - Returns the area of the labelled Dyck path corresponding to the parking function. + Return the area of the labelled Dyck path corresponding to the + parking function. INPUT: @@ -967,12 +969,15 @@ def area(self): @combinatorial_map(name='to ides composition') def ides_composition(self): r""" - Return the :meth:`~sage.combinat.permutation.Permutation.descents_composition` - of the inverse of the :meth:`diagonal_reading_word` of corresponding parking - function. For example, ``ides_composition(PF) = [4, 2, 1]`` - means that the descents of the inverse of the permutation - :meth:`diagonal_reading_word` of the parking function with word ``PF`` are at - the 4th and 6th positions. + Return the + :meth:`~sage.combinat.permutation.Permutation.descents_composition` + of the inverse of the :meth:`diagonal_reading_word` of + corresponding parking function. + + For example, ``ides_composition(PF) = [4, 2, 1]`` means that + the descents of the inverse of the permutation + :meth:`diagonal_reading_word` of the parking function with + word ``PF`` are at the 4th and 6th positions. INPUT: @@ -1004,6 +1009,7 @@ def ides(self): r""" Return the :meth:`~sage.combinat.permutation.Permutation.descents` sequence of the inverse of the :meth:`diagonal_reading_word` of ``self``. + For example, ``ides(PF) = [1, 2, 3, 5]`` means that descents are at the 2nd, 3rd, 4th and 6th positions in the inverse of the :meth:`diagonal_reading_word` of the parking function (see [GXZ]_ p. 2). @@ -1036,10 +1042,12 @@ def ides(self): def touch_points(self): r""" - Returns the sequence of touch points which corresponds to the labelled Dyck path - after initial step. For example, ``touch_points(PF) = [4, 7]`` means that after - the initial step, the path touches the main diagonal at points `(4, 4)` and - `(7, 7)`. + Return the sequence of touch points which corresponds to the labelled Dyck path + after initial step. + + For example, ``touch_points(PF) = [4, 7]`` means that after + the initial step, the path touches the main diagonal at points + `(4, 4)` and `(7, 7)`. INPUT: @@ -1070,10 +1078,12 @@ def touch_points(self): @combinatorial_map(name='to touch composition') def touch_composition(self): r""" - Returns the composition of the labelled Dyck path corresponding to the - parking function. For example, ``touch_composition(PF) = [4, 3]`` - means that the first touch is four diagonal units from the starting point, and - the second is three units further (see [GXZ]_ p. 2). + Return the composition of the labelled Dyck path corresponding + to the parking function. + + For example, ``touch_composition(PF) = [4, 3]`` means that the + first touch is four diagonal units from the starting point, + and the second is three units further (see [GXZ]_ p. 2). INPUT: @@ -1106,7 +1116,7 @@ def touch_composition(self): @combinatorial_map(name='to labelling permutation') def to_labelling_permutation(self): r""" - Returns the labelling of the support Dyck path of the parking function. + Return the labelling of the support Dyck path of the parking function. INPUT: @@ -1136,7 +1146,8 @@ def to_labelling_permutation(self): def to_area_sequence(self): r""" - Returns the area sequence of the support Dyck path of the parking function. + Return the area sequence of the support Dyck path of the + parking function. INPUT: @@ -1166,8 +1177,9 @@ def to_area_sequence(self): def to_labelling_area_sequence_pair(self): r""" - Returns a pair consisting of a labelling and an area sequence of a Dyck path - which corresponds to the given parking function. + Return a pair consisting of a labelling and an area sequence + of a Dyck path which corresponds to the given parking + function. INPUT: @@ -1200,7 +1212,7 @@ def to_labelling_area_sequence_pair(self): @combinatorial_map(name='to dyck word') def to_dyck_word(self): r""" - Returns the support Dyck word of the parking function. + Return the support Dyck word of the parking function. INPUT: @@ -1231,9 +1243,11 @@ def to_dyck_word(self): def to_labelled_dyck_word(self): r""" - Returns the labelled Dyck word corresponding to the parking function. - This is a representation of the parking function as a list where the entries of - 1 in the Dyck word are replaced with the corresponding label. + Return the labelled Dyck word corresponding to the parking function. + + This is a representation of the parking function as a list + where the entries of 1 in the Dyck word are replaced with the + corresponding label. INPUT: @@ -1268,8 +1282,8 @@ def to_labelled_dyck_word(self): def to_labelling_dyck_word_pair(self): r""" - Returns the pair ``(L, D)`` where ``L`` is a labelling and ``D`` is the Dyck - word of the parking function. + Return the pair ``(L, D)`` where ``L`` is a labelling and + ``D`` is the Dyck word of the parking function. INPUT: @@ -1302,8 +1316,8 @@ def to_labelling_dyck_word_pair(self): @combinatorial_map(name='to non-decreasing parking function') def to_NonDecreasingParkingFunction(self): r""" - Returns the non-decreasing parking function which underlies the parking - function. + Return the non-decreasing parking function which underlies the + parking function. INPUT: @@ -1337,7 +1351,7 @@ def to_NonDecreasingParkingFunction(self): def characteristic_quasisymmetric_function(self, q=None, R=QQ['q', 't'].fraction_field()): r""" - Return the characteristic quasisymmetric function of ``self`` + Return the characteristic quasisymmetric function of ``self``. The characteristic function of the Parking Function is the sum over all permutation labellings of the Dyck path `q^{dinv(PF)} @@ -1489,7 +1503,8 @@ def pretty_print(self, underpath=True): def from_labelling_and_area_sequence(L, D): r""" - Returns the parking function corresponding to the labelling area sequence pair. + Return the parking function corresponding to the labelling area + sequence pair. INPUT: @@ -1525,7 +1540,7 @@ def from_labelling_and_area_sequence(L, D): def from_labelled_dyck_word(LDW): r""" - Returns the parking function corresponding to the labelled Dyck word. + Return the parking function corresponding to the labelled Dyck word. INPUT: From 51d676452a559595376aef69ccac2fe7ea0534ea Mon Sep 17 00:00:00 2001 From: Jayant Date: Tue, 10 Jun 2014 05:09:01 -0400 Subject: [PATCH 243/546] Added caching of coordinates. --- src/sage/matroids/matroid.pxd | 1 + src/sage/matroids/matroid.pyx | 10 +- src/sage/matroids/matroids_plot_helpers.py | 130 +++++++++++++++------ 3 files changed, 107 insertions(+), 34 deletions(-) diff --git a/src/sage/matroids/matroid.pxd b/src/sage/matroids/matroid.pxd index 98e9bbb3ae5..4dabdfee808 100644 --- a/src/sage/matroids/matroid.pxd +++ b/src/sage/matroids/matroid.pxd @@ -3,6 +3,7 @@ from sage.structure.sage_object cimport SageObject cdef class Matroid(SageObject): cdef public __custom_name cdef public _custom_name + cdef public _cached_info cdef int _stored_full_rank cdef int _stored_size diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index e106ac8d7f6..4bea27d6cf8 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -4775,6 +4775,13 @@ cdef class Matroid(SageObject): sage: G.show() """ + if self._cached_info == None: + self._cached_info={'positions':None,'lineorders': None} + if 'positions' not in self._cached_info.keys(): + self._cached_info['positions'] = None + if 'lineorders' not in self._cached_info.keys(): + self._cached_info['lineorders'] = None + if self.rank() > 3: return elif B == None: @@ -4782,7 +4789,8 @@ cdef class Matroid(SageObject): elif self.rank() != self.rank(B): return import matroids_plot_helpers - return matroids_plot_helpers.geomrep(self,B,lineorders) + lineorders2=matroids_plot_helpers.lineorders_union(self._cached_info['lineorders'],lineorders) + return matroids_plot_helpers.geomrep(self,B,lineorders2) cpdef show(self,B=None,lineorders=None): """ diff --git a/src/sage/matroids/matroids_plot_helpers.py b/src/sage/matroids/matroids_plot_helpers.py index 6268b61a683..8536cadba0b 100644 --- a/src/sage/matroids/matroids_plot_helpers.py +++ b/src/sage/matroids/matroids_plot_helpers.py @@ -37,8 +37,7 @@ def initial_triangle(M,B1,nB1,lps): INPUT: - ``M`` -- A matroid. - - ``B1``-- A list of labels of groundset elements of M that corresponds to a basis of matroid - returned by ``M.simplify()``. + - ``B1``-- A list of labels of groundset elements of M that corresponds to a basis of matroid M - ``nB1``-- A list of labes of elements in the ground set of M that corresponds to ``M.simplify.groundset() \ B1``. - ``lps``-- A list of labels of elements in the ground set of matroid M that are loops. @@ -74,6 +73,7 @@ def initial_triangle(M,B1,nB1,lps): This method does NOT do any checks. """ + tripts = [(0, 0),(1, 2),(2, 0)] pts = {} j=0 @@ -102,6 +102,7 @@ def initial_triangle(M,B1,nB1,lps): lines[i-1].extend(L[i-1]) lines[i-1].extend([B1[pairs[i-1][1]]]) # place triangle and L1,L2,L3 + for i in L:# loop over megalist interval = 1/float(len(i)+1) pt1 = list(tripts[pairs[L.index(i)][0]]) @@ -110,6 +111,8 @@ def initial_triangle(M,B1,nB1,lps): #Loop over L1,L2,L3 cc = interval*j pts[i[j-1]] = (cc*pt1[0]+(1-cc)*pt2[0],cc*pt1[1]+(1-cc)*pt2[1]) + + trilines = [list(set(x)) for x in lines if len(x)>=3] curvedlines = [list(set(list(x)).difference(set(lps))) for x in M.flats(2) if set(list(x)) not in trilines if len(list(x))>=3] nontripts = [i for i in nB1 if i not in pts.keys()] @@ -184,12 +187,12 @@ def addnontripts(tripts_labels,nontripts_labels,ptsdict): This method does NOT do any checks. """ - tripts=[list(ptsdict[p]) for p in tripts_labels] + tripts = [list(ptsdict[p]) for p in tripts_labels] pairs = [[0,1],[1,2],[0,2]] q = [tripts] num = len(nontripts_labels) gridpts = [[float((tripts[0][0]+tripts[1][0]+tripts[2][0])/3),float(tripts[0][1]+tripts[1][1]+tripts[2][1])/3]] - n=0 + n = 0 while n0: + if len(L) > 0: loops = L looptext = ", ".join([str(l) for l in loops]) rectx = -1 @@ -356,20 +359,20 @@ def addlp(M,L,P,ptsdict,G=None): G += text(looptext,(rectx+0.5,recty+0.3),color='black',fontsize=13) G += point((rectx+0.2, recty+0.3),color='black', size=300,zorder=2) G += text('Loop(s)',(rectx+0.5+0.4*len(loops)+0.1,recty+0.3),fontsize=13,color='black') - if len(P)>0: + if len(P) > 0: # create list of lists where inner lists are parallel classes - pcls=[] + pcls = [] gnd = sorted(M1.groundset_list()) for g in gnd: pcl = [g] for p in P: - if M.rank([g,p])==1: + if M.rank([g,p]) == 1: pcl.extend([p]) pcls.append(pcl) for pcl in pcls: - if len(pcl)>1: + if len(pcl) > 1: basept = list(ptsdict[pcl[0]]) - if len(pcl)<=2: + if len(pcl) <= 2: # add side by side ptsdict[pcl[1]] = (basept[0],basept[1]-0.13) G += points(zip([basept[0]], [basept[1]-0.13]),color='black', size=300,zorder=2) @@ -381,7 +384,7 @@ def addlp(M,L,P,ptsdict,G=None): def line_hasorder(l,lodrs=None): """ - Determine if and order is specified for a line + Determine if an order is specified for a line INPUT: @@ -390,7 +393,7 @@ def line_hasorder(l,lodrs=None): OUTPUT: - A tuple containing 3 elements in this order: + A tuple containing 2 elements in this order: 1. A boolean indicating whether there is any list in ``lordrs`` that is setwise equal to ``l`` 2. A list specifying an order on ``set(l)`` if 1. is True, otherwise an empty list @@ -407,12 +410,54 @@ def line_hasorder(l,lodrs=None): This method does NOT do any checks. """ - if lodrs!=None: + if lodrs != None: if len(lodrs) > 0: for i in lodrs: - if Set(i)==Set(l): + if Set(i) == Set(l): return True,i return False,[] + +def lineorders_union(lineorders1,lineorders2): + """ + Return a list of ordered lists labels that corresponds to union of two sets of ordered lists of labels in a sense + + INPUT: + + - ``lineorders1`` -- A list of ordered lists specifying orders on sets of labels + - ``lineorders2`` -- A list of ordered lists specifying orders on sets of labels + + OUTPUT: + + A list of ordered lists of labels that are (setwise) in only one of ``lineorders1`` or ``lineorders2`` + along with the ones in lineorder2 that are setwise equal to any list in lineorders1 + + EXAMPLES:: + + sage: from sage.matroids import matroids_plot_helpers + sage: matroids_plot_helpers.lineorders_union([['a','b','c'],['p','q','r'],['i','j','k','l']],[['r','p','q']]) + [['a', 'b', 'c'], ['p', 'q', 'r'], ['i', 'j', 'k', 'l']] + + """ + if lineorders1 != None and lineorders2 != None: + lineorders = lineorders1 + for order in lineorders2: + x,lo = line_hasorder(order,lineorders1) + if x == False: + lineorders.append(order) + lineorders.remove(lo) + return lineorders + elif lineorders1 == None and lineorders2 != None: + return lineorders2 + elif lineorders1!=None: + return lineorders1 + else: + return None + + + + + + def geomrep(M1,B1=None,lineorders1=None): """ @@ -450,27 +495,39 @@ def geomrep(M1,B1=None,lineorders1=None): G = Graphics() # create lists of loops and parallel elements and simplify given matroid [M,L,P] = slp(M1) + M._cached_info = M1._cached_info if M.rank()==1: - pts = {} - gnd = sorted(M.groundset()) - pts[gnd[0]]=(1,float(2)/3) + if M._cached_info != None and 'positions' in M._cached_info.keys() and M._cached_info['positions'] != None: + pts = M._cached_info['positions'] + else: + pts = {} + gnd = sorted(M.groundset()) + + pts[gnd[0]] = (1,float(2)/3) G += point((1, float(2)/3),size=300,zorder=2) pts2 = pts elif M.rank() == 2: - pts2 = {} - pts2[B1[0]] = (0,0) - pts2[B1[1]] = (2,0) - nB1=list(set(M.groundset_list())-set(B1)) + nB1=list(set(list(M.groundset())) - set(B1)) bline = [] for j in nB1: if M.is_dependent([j,B1[0],B1[1]]): bline.append(j) + interval = len(bline)+1 - lpt = list(pts2[B1[0]]) - rpt = list(pts2[B1[1]]) - for k in range(len(bline)): - cc = (float(1)/interval)*(k+1) - pts2[bline[k]] = (cc*lpt[0]+(1-cc)*rpt[0],cc*lpt[1]+(1-cc)*rpt[1]) + + if M._cached_info != None and 'positions' in M._cached_info.keys() and M._cached_info['positions'] != None: + pts2 = M._cached_info['positions'] + else: + pts2 = {} + pts2[B1[0]] = (0,0) + pts2[B1[1]] = (2,0) + lpt = list(pts2[B1[0]]) + rpt = list(pts2[B1[1]]) + for k in range(len(bline)): + cc = (float(1)/interval)*(k+1) + pts2[bline[k]] = (cc*lpt[0]+(1-cc)*rpt[0],cc*lpt[1]+(1-cc)*rpt[1]) + M._cached_info['positions']=pts2 + bline.extend(B1) ptsx,ptsy,x_i,y_i = createline(pts2,bline,lineorders1) G += line(zip(x_i, y_i),color='black',thickness=3,zorder=1) @@ -482,9 +539,14 @@ def geomrep(M1,B1=None,lineorders1=None): pt = list(pts2[i]) G += text(i,(float(pt[0]), float(pt[1])), color='white',fontsize=13) else: - pts,trilines,nontripts,curvedlines = initial_triangle(M1,B1,list(set(M.groundset())-set(B1)), list(set(L)|set(P))) #[i for i in sorted(M.groundset()) if i not in B1]) - pts2= addnontripts([B1[0],B1[1],B1[2]],nontripts,pts) - trilines.extend(curvedlines) + if M._cached_info == None or 'positions' not in M._cached_info.keys() or M._cached_info['positions'] == None: + pts,trilines,nontripts,curvedlines = initial_triangle(M1,B1,list(set(M.groundset())-set(B1)), list(set(L)|set(P))) + pts2= addnontripts([B1[0],B1[1],B1[2]],nontripts,pts) + trilines.extend(curvedlines) + else: + pts2=M._cached_info['positions'] + trilines=[list(set(list(x)).difference(L|P)) for x in M1.flats(2) if len(list(x))>=3] + j = 0 for ll in trilines: if len(ll)>=3: @@ -497,6 +559,8 @@ def geomrep(M1,B1=None,lineorders1=None): for i in pts2: pt = list(pts2[i]) G += text(i,(float(pt[0]), float(pt[1])),color='white',fontsize=13) + M1._cached_info['positions']=pts2 + M1._cached_info['lineorders']=lineorders1 #deal with loops and parallel elements G = addlp(M1,L,P,pts2,G) G.axes(False) From 2b6ae6caa8f84e150f9f89ac8faea382cca0781b Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 10 Jun 2014 15:30:47 +0100 Subject: [PATCH 244/546] improves title on docs --- src/doc/en/reference/game_theory/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/reference/game_theory/index.rst b/src/doc/en/reference/game_theory/index.rst index c027d8e8f5a..292ce80e82b 100644 --- a/src/doc/en/reference/game_theory/index.rst +++ b/src/doc/en/reference/game_theory/index.rst @@ -1,5 +1,5 @@ Game Theory -===== +=========== .. toctree:: :maxdepth: 2 From 73a4e48f890a31cafbf4e8d176ac69c3b3171cbb Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 10 Jun 2014 20:18:24 -0700 Subject: [PATCH 245/546] Implemented other changes/improvements Nicolas suggested. --- src/sage/categories/groups.py | 70 ++++++++++++++-------- src/sage/categories/monoids.py | 77 ++++++++++++++++++++----- src/sage/groups/indexed_free_group.py | 55 +++++++++--------- src/sage/monoids/free_monoid.py | 14 +++-- src/sage/monoids/indexed_free_monoid.py | 45 ++++++++++----- 5 files changed, 176 insertions(+), 85 deletions(-) diff --git a/src/sage/categories/groups.py b/src/sage/categories/groups.py index 3818c38891a..402dc91b73f 100644 --- a/src/sage/categories/groups.py +++ b/src/sage/categories/groups.py @@ -46,9 +46,9 @@ def example(self): return GL(4,QQ) @staticmethod - def free(index_set=None, names=None, commutative=False, **kwds): + def free(index_set=None, names=None, **kwds): r""" - Return the free (commutative) group. + Return the free group. INPUT: @@ -58,37 +58,19 @@ def free(index_set=None, names=None, commutative=False, **kwds): - ``names`` -- a string or list/tuple/iterable of strings (default: ``'x'``); the generator names or name prefix - - ``commutative`` -- (default: ``False``) whether to construct the - free commutative (abelian) group or the free group - EXAMPLES:: sage: Groups.free(index_set=ZZ) Free group indexed by Integer Ring - sage: Groups().free(index_set=ZZ) + sage: Groups().free(ZZ) Free group indexed by Integer Ring sage: F. = Groups().free(); F Free Group on generators {x, y, z} """ from sage.rings.all import ZZ if index_set in ZZ or (index_set is None and names is not None): - if not commutative: - from sage.groups.free_group import FreeGroup - return FreeGroup(index_set, names, **kwds) - - if names is None: - names = ['F' + repr(i) for i in range(index_set)] - elif isinstance(names, str): - if ',' not in names: - names = [names + repr(i) for i in range(index_set)] - else: - names = names.split(',') - from sage.groups.indexed_free_group import IndexedFreeAbelianGroup - return IndexedFreeAbelianGroup(names, **kwds) - - if commutative: - from sage.groups.indexed_free_group import IndexedFreeAbelianGroup - return IndexedFreeAbelianGroup(index_set, **kwds) + from sage.groups.free_group import FreeGroup + return FreeGroup(index_set, names, **kwds) from sage.groups.indexed_free_group import IndexedFreeGroup return IndexedFreeGroup(index_set, **kwds) @@ -412,6 +394,48 @@ class ElementMethods: Finite = LazyImport('sage.categories.finite_groups', 'FiniteGroups') #Algebras = LazyImport('sage.categories.group_algebras', 'GroupAlgebras') + class Commutative(CategoryWithAxiom): + """ + Category of commutative (abelian) groups. + + A group `G` is *commutative* if `xy = yx` for all `x,y \in G`. + """ + @staticmethod + def free(index_set=None, names=None, **kwds): + r""" + Return the free commutative group. + + INPUT: + + - ``index_set`` -- (optional) an index set for the generators; if + an integer, then this represents `\{0, 1, \ldots, n-1\}` + + - ``names`` -- a string or list/tuple/iterable of strings + (default: ``'x'``); the generator names or name prefix + + EXAMPLES:: + + sage: Groups.Commutative.free(index_set=ZZ) + Free abelian group indexed by Integer Ring + sage: Groups().Commutative().free(ZZ) + Free abelian group indexed by Integer Ring + sage: F. = Groups().Commutative().free(); F + Free abelian group indexed by {'x', 'y', 'z'} + """ + if names is not None: + if isinstance(names, str): + from sage.rings.all import ZZ + if ',' not in names and index_set in ZZ: + names = [names + repr(i) for i in range(index_set)] + else: + names = names.split(',') + names = tuple(names) + if index_set is None: + index_set = names + + from sage.groups.indexed_free_group import IndexedFreeAbelianGroup + return IndexedFreeAbelianGroup(index_set, **kwds) + class Algebras(AlgebrasCategory): r""" The category of group algebras over a given base ring. diff --git a/src/sage/categories/monoids.py b/src/sage/categories/monoids.py index 6c2362fa832..8b1c698c5c6 100644 --- a/src/sage/categories/monoids.py +++ b/src/sage/categories/monoids.py @@ -66,10 +66,10 @@ class Monoids(CategoryWithAxiom): Inverse = LazyImport('sage.categories.groups', 'Groups', at_startup=True) @staticmethod - def free(n=None, names=None, index_set=None, commutative=False, **kwds): + def free(index_set=None, names=None, **kwds): r""" - Return a free monoid on `n` generators or with the generators indexed by - a set `I`. + Return a free monoid on `n` generators or with the generators + indexed by a set `I`. A free monoid is constructed by specifing either: @@ -84,25 +84,26 @@ def free(n=None, names=None, index_set=None, commutative=False, **kwds): - ``names`` -- a string or list/tuple/iterable of strings (default: ``'x'``); the generator names or name prefix - - ``commutative`` -- (default: ``False``) whether to construct the - free commutative (abelian) group or the free group - EXAMPLES:: sage: Monoids.free(index_set=ZZ) Free monoid indexed by Integer Ring - sage: Monoids().free(index_set=ZZ) + sage: Monoids().free(ZZ) Free monoid indexed by Integer Ring sage: F. = Monoids().free(); F - Free monoid on 3 generators (x, y, z) + Free monoid indexed by {'x', 'y', 'z'} """ - from sage.rings.all import ZZ - if index_set in ZZ or (index_set is None and names is not None): - from sage.monoids.free_monoid import FreeMonoid - return FreeMonoid(index_set, names, **kwds) - if names is not None: - names = normalize_names(n, names) + if isinstance(names, str): + from sage.rings.all import ZZ + if ',' not in names and index_set in ZZ: + names = [names + repr(i) for i in range(index_set)] + else: + names = names.split(',') + names = tuple(names) + if index_set is None: + index_set = names + from sage.monoids.indexed_free_monoid import IndexedFreeMonoid return IndexedFreeMonoid(index_set, names=names, **kwds) @@ -235,6 +236,54 @@ def _pow_naive(self, n): result *= self return result + class Commutative(CategoryWithAxiom): + """ + Category of commutative (abelian) monoids. + + A monoid `M` is *commutative* if `xy = yx` for all `x,y \in M`. + """ + @staticmethod + def free(index_set=None, names=None, **kwds): + r""" + Return a free abelian monoid on `n` generators or with + the generators indexed by a set `I`. + + A free monoid is constructed by specifing either: + + - the number of generators and/or the names of the generators, or + - the indexing set for the generators. + + INPUT: + + - ``index_set`` -- (optional) an index set for the generators; if + an integer, then this represents `\{0, 1, \ldots, n-1\}` + + - ``names`` -- a string or list/tuple/iterable of strings + (default: ``'x'``); the generator names or name prefix + + EXAMPLES:: + + sage: Monoids.Commutative.free(index_set=ZZ) + Free abelian monoid indexed by Integer Ring + sage: Monoids().Commutative().free(ZZ) + Free abelian monoid indexed by Integer Ring + sage: F. = Monoids().Commutative().free(); F + Free abelian monoid indexed by {'x', 'y', 'z'} + """ + if names is not None: + if isinstance(names, str): + from sage.rings.all import ZZ + if ',' not in names and index_set in ZZ: + names = [names + repr(i) for i in range(index_set)] + else: + names = names.split(',') + names = tuple(names) + if index_set is None: + index_set = names + + from sage.monoids.indexed_free_monoid import IndexedFreeAbelianMonoid + return IndexedFreeAbelianMonoid(index_set, names=names, **kwds) + class WithRealizations(WithRealizationsCategory): class ParentMethods: diff --git a/src/sage/groups/indexed_free_group.py b/src/sage/groups/indexed_free_group.py index 79f0b48c893..e53bcab3c5c 100644 --- a/src/sage/groups/indexed_free_group.py +++ b/src/sage/groups/indexed_free_group.py @@ -27,6 +27,7 @@ IndexedMonoidElement, IndexedFreeMonoidElement, IndexedFreeAbelianMonoidElement) from sage.misc.cachefunc import cached_method +from sage.combinat.dict_addition import dict_addition from sage.rings.integer import Integer from sage.rings.infinity import infinity from sage.sets.family import Family @@ -46,10 +47,10 @@ def order(self): sage: G = Groups().free(index_set=ZZ) sage: G.order() +Infinity - sage: G = Groups().free(index_set='abc', commutative=True) + sage: G = Groups().Commutative().free(index_set='abc') sage: G.order() +Infinity - sage: G = Groups().free(index_set=[], commutative=True) + sage: G = Groups().Commutative().free(index_set=[]) sage: G.order() 1 """ @@ -76,13 +77,13 @@ def is_finite(self): :: - sage: G = Groups().free(index_set=ZZ, commutative=True) + sage: G = Groups().Commutative().free(index_set=ZZ) sage: G.is_finite() False - sage: G = Groups().free(index_set='abc', commutative=True) + sage: G = Groups().Commutative().free(index_set='abc') sage: G.is_finite() False - sage: G = Groups().free(index_set=[], commutative=True) + sage: G = Groups().Commutative().free(index_set=[]) sage: G.is_finite() True """ @@ -108,13 +109,13 @@ def rank(self): :: - sage: G = Groups().free(index_set=ZZ, commutative=True) + sage: G = Groups().Commutative().free(index_set=ZZ) sage: G.rank() +Infinity - sage: G = Groups().free(index_set='abc', commutative=True) + sage: G = Groups().Commutative().free(index_set='abc') sage: G.rank() 3 - sage: G = Groups().free(index_set=[], commutative=True) + sage: G = Groups().Commutative().free(index_set=[]) sage: G.rank() 0 """ @@ -335,10 +336,10 @@ class IndexedFreeAbelianGroup(IndexedGroup, AbelianGroup): EXAMPLES:: - sage: G = Groups().free(index_set=ZZ, commutative=True) + sage: G = Groups().Commutative().free(index_set=ZZ) sage: G Free abelian group indexed by Integer Ring - sage: G = Groups().free(index_set='abcde', commutative=True) + sage: G = Groups().Commutative().free(index_set='abcde') sage: G Free abelian group indexed by {'a', 'b', 'c', 'd', 'e'} """ @@ -348,9 +349,9 @@ def __init__(self, indices, prefix, category=None, **kwds): TESTS:: - sage: G = Groups().free(index_set=ZZ, commutative=True) + sage: G = Groups().Commutative().free(index_set=ZZ) sage: TestSuite(G).run() - sage: G = Groups().free(index_set='abc', commutative=True) + sage: G = Groups().Commutative().free(index_set='abc') sage: TestSuite(G).run() """ AbelianGroup.__init__(self) @@ -361,7 +362,7 @@ def _repr_(self): """ TESTS:: - sage: Groups.free(index_set=ZZ, commutative=True) + sage: Groups.Commutative().free(index_set=ZZ) Free abelian group indexed by Integer Ring """ return 'Free abelian group indexed by {}'.format(self._indices) @@ -375,14 +376,14 @@ def _element_constructor_(self, x=None): sage: G = FreeAbelianMonoid(index_set=ZZ) sage: G(G.gen(2)) F[2] - sage: G(-5) - F[-5] - sage: G(1) - F[1] sage: G([[1, 3], [-2, 12]]) F[-2]^12*F[1]^3 sage: G({1:3, -2: 12}) F[-2]^12*F[1]^3 + sage: G(-5) + Traceback (most recent call last): + ... + ValueError: unable to convert -5, use gen() instead """ if isinstance(x, (list, tuple, dict)): x = dict(x) @@ -395,7 +396,7 @@ def one(self): EXAMPLES:: - sage: G = Groups().free(index_set=ZZ, commutative=True) + sage: G = Groups().Commutative().free(index_set=ZZ) sage: G.one() 1 """ @@ -407,7 +408,7 @@ def gen(self, x): EXAMPLES:: - sage: G = Groups().free(index_set=ZZ, commutative=True) + sage: G = Groups().Commutative().free(index_set=ZZ) sage: G.gen(0) F[0] sage: G.gen(2) @@ -427,7 +428,7 @@ def _mul_(self, other): EXAMPLES:: - sage: G = Groups().free(index_set=ZZ, commutative=True) + sage: G = Groups().Commutative().free(index_set=ZZ) sage: a,b,c,d,e = [G.gen(i) for i in range(5)] sage: a*b^2*e^-1*d F[0]*F[1]^2*F[3]*F[4]^-1 @@ -436,12 +437,8 @@ def _mul_(self, other): sage: (a*b^-2*d^2) * (d^-2*b^2*a^-1) 1 """ - ret = copy(self._monomial) - for k,v in other._monomial.iteritems(): - ret[k] = ret.get(k, 0) + v - if ret[k] == 0: - del ret[k] - return self.__class__(self.parent(), ret) + return self.__class__(self.parent(), + dict_addition([self._monomial, other._monomial])) def __invert__(self): """ @@ -449,7 +446,7 @@ def __invert__(self): EXAMPLES:: - sage: G = Groups().free(index_set=ZZ, commutative=True) + sage: G = Groups().Commutative().free(index_set=ZZ) sage: a,b,c,d,e = [G.gen(i) for i in range(5)] sage: x = a*b^2*e^-1*d; ~x F[0]^-1*F[1]^-2*F[3]^-1*F[4] @@ -464,7 +461,7 @@ def __floordiv__(self, a): EXAMPLES:: - sage: G = Groups().free(index_set=ZZ, commutative=True) + sage: G = Groups().Commutative().free(index_set=ZZ) sage: a,b,c,d,e = [G.gen(i) for i in range(5)] sage: elt = a*b*c^3*d^2; elt F[0]*F[1]*F[2]^3*F[3]^2 @@ -485,7 +482,7 @@ def __pow__(self, n): EXAMPLES:: - sage: G = Groups().free(index_set=ZZ, commutative=True) + sage: G = Groups().Commutative().free(index_set=ZZ) sage: a,b,c,d,e = [G.gen(i) for i in range(5)] sage: x = a*b^2*e^-1*d; x F[0]*F[1]^2*F[3]*F[4]^-1 diff --git a/src/sage/monoids/free_monoid.py b/src/sage/monoids/free_monoid.py index 7581e87ea51..8257cba1f95 100644 --- a/src/sage/monoids/free_monoid.py +++ b/src/sage/monoids/free_monoid.py @@ -76,7 +76,7 @@ def create_object(self, version, key, **kwds): FreeMonoid_factory = FreeMonoidFactory("sage.monoids.free_monoid.FreeMonoid_factory") @rename_keyword(deprecation=15289, n="index_set") -def FreeMonoid(index_set=None, names=None, abelian=False, **kwds): +def FreeMonoid(index_set=None, names=None, commutative=False, **kwds): r""" Return a free monoid on `n` generators or with the generators indexed by a set `I`. @@ -93,8 +93,8 @@ def FreeMonoid(index_set=None, names=None, abelian=False, **kwds): - ``names`` -- names of generators - - ``abelian`` -- (default: ``False``) whether the free monoid is abelian - or not + - ``commutative`` -- (default: ``False``) whether the free monoid is + commutative or not OUTPUT: @@ -109,10 +109,14 @@ def FreeMonoid(index_set=None, names=None, abelian=False, **kwds): sage: F. = FreeMonoid(abelian=True); F Free abelian monoid on 3 generators (x, y, z) - sage: FreeMonoid(index_set=ZZ, abelian=True) + sage: FreeMonoid(index_set=ZZ, commutative=True) Free abelian monoid indexed by Integer Ring """ - if abelian: + if 'abelian' in kwds: + commutative = kwds['abelian'] + del kwds['abelian'] + + if commutative: from sage.monoids.free_abelian_monoid import FreeAbelianMonoid return FreeAbelianMonoid(index_set, names, **kwds) diff --git a/src/sage/monoids/indexed_free_monoid.py b/src/sage/monoids/indexed_free_monoid.py index bfafedff4cd..9cc4193e721 100644 --- a/src/sage/monoids/indexed_free_monoid.py +++ b/src/sage/monoids/indexed_free_monoid.py @@ -20,6 +20,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.element import MonoidElement from sage.structure.indexed_generators import IndexedGenerators +from sage.combinat.dict_addition import dict_addition from sage.categories.monoids import Monoids from sage.categories.poor_man_map import PoorManMap @@ -53,7 +54,7 @@ def __init__(self, F, x): EXAMPLES:: sage: F = FreeAbelianMonoid(index_set=ZZ) - sage: F(1) + sage: F.gen(1) F[1] sage: a,b,c,d,e = [F.gen(i) for i in range(5)] sage: x = a^2 * b^3 * a^2 * b^4; x @@ -552,10 +553,8 @@ def _mul_(self, other): sage: a*b^2*e*d F[0]*F[1]^2*F[3]*F[4] """ - ret = copy(self._monomial) - for k,v in other._monomial.iteritems(): - ret[k] = ret.get(k, 0) + v - return self.__class__(self.parent(), ret) + return self.__class__(self.parent(), + dict_addition([self._monomial, other._monomial])) def __pow__(self, n): """ @@ -672,11 +671,19 @@ def __classcall__(cls, indices, prefix="F", **kwds): ('LEFT', 'RIGHT') sage: F is G False + sage: Groups.Commutative.free() + Traceback (most recent call last): + ... + ValueError: no index set specified """ if isinstance(indices, str): indices = FiniteEnumeratedSet(list(indices)) elif isinstance(indices, (list, tuple)): indices = FiniteEnumeratedSet(indices) + elif indices is None: + if 'names' not in kwds: + raise ValueError("no index set specified") + indices = FiniteEnumeratedSet(kwds['names']) # bracket or latex_bracket might be lists, so convert # them to tuples so that they're hashable. @@ -712,6 +719,19 @@ def __init__(self, indices, prefix, category=None, names=None, **kwds): kwds.pop('key', None) IndexedGenerators.__init__(self, indices, prefix, **kwds) + def _first_ngens(self, n): + """ + Used by the preparser for ``F. = ...``. + + EXAMPLES:: + + sage: F. = FreeMonoid(index_set=ZZ) + sage: [x, y, z] + [F[0], F[1], F[-1]] + """ + it = iter(self._indices) + return tuple(self.gen(it.next()) for i in range(n)) + def _element_constructor_(self, x=None): """ Create an element of this abelian monoid from ``x``. @@ -721,17 +741,18 @@ def _element_constructor_(self, x=None): sage: F = FreeAbelianMonoid(index_set=ZZ) sage: F(F.gen(2)) F[2] - sage: F(-5) - F[-5] - sage: F(1) - F[1] sage: F([[1, 3], [-2, 12]]) F[-2]^12*F[1]^3 + sage: F(-5) + Traceback (most recent call last): + ... + ValueError: unable to convert -5, use gen() instead """ if x is None: return self.one() if x in self._indices: - return self.gens()[x] + raise ValueError("unable to convert {}, use gen() instead".format(x)) + # return self.gens()[x] return self.element_class(self, x) def _an_element_(self): @@ -922,10 +943,6 @@ def _element_constructor_(self, x=None): sage: F = FreeAbelianMonoid(index_set=ZZ) sage: F(F.gen(2)) F[2] - sage: F(-5) - F[-5] - sage: F(1) - F[1] sage: F([[1, 3], [-2, 12]]) F[-2]^12*F[1]^3 sage: F({1:3, -2: 12}) From f8701162940876cc82278d1802c09fb3ec3a2901 Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Wed, 11 Jun 2014 11:37:36 +0200 Subject: [PATCH 246/546] #16454 : remove Configure.patch --- build/pkgs/openssl/SPKG.txt | 2 -- build/pkgs/openssl/patches/Configure.patch | 20 -------------------- 2 files changed, 22 deletions(-) delete mode 100644 build/pkgs/openssl/patches/Configure.patch diff --git a/build/pkgs/openssl/SPKG.txt b/build/pkgs/openssl/SPKG.txt index 7a4f0eba994..c2cb3baf813 100644 --- a/build/pkgs/openssl/SPKG.txt +++ b/build/pkgs/openssl/SPKG.txt @@ -22,7 +22,5 @@ library in a variety of computer languages are available. === Patches === - * src/Configure: Sage's gcc doesn't recognize the '-arch' option, - while Apple's gcc does, so we remove this flag. * src/config: patched to fix a problem on Solaris. diff --git a/build/pkgs/openssl/patches/Configure.patch b/build/pkgs/openssl/patches/Configure.patch deleted file mode 100644 index 7a9a385f336..00000000000 --- a/build/pkgs/openssl/patches/Configure.patch +++ /dev/null @@ -1,20 +0,0 @@ -diff -ru src/Configure b/Configure ---- src/Configure 2012-03-14 15:20:40.000000000 -0700 -+++ b/Configure 2012-06-20 13:42:25.000000000 -0700 -@@ -575,11 +575,11 @@ - - ##### MacOS X (a.k.a. Rhapsody or Darwin) setup - "rhapsody-ppc-cc","cc:-O3 -DB_ENDIAN::(unknown):MACOSX_RHAPSODY::BN_LLONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${no_asm}::", --"darwin-ppc-cc","cc:-arch ppc -O3 -DB_ENDIAN -Wa,-force_cpusubtype_ALL::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${ppc32_asm}:osx32:dlfcn:darwin-shared:-fPIC -fno-common:-arch ppc -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", --"darwin64-ppc-cc","cc:-arch ppc64 -O3 -DB_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${ppc64_asm}:osx64:dlfcn:darwin-shared:-fPIC -fno-common:-arch ppc64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", --"darwin-i386-cc","cc:-arch i386 -O3 -fomit-frame-pointer -DL_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_INT RC4_CHUNK DES_UNROLL BF_PTR:".eval{my $asm=$x86_asm;$asm=~s/cast\-586\.o//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch i386 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", --"debug-darwin-i386-cc","cc:-arch i386 -g3 -DL_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_INT RC4_CHUNK DES_UNROLL BF_PTR:${x86_asm}:macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch i386 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", --"darwin64-x86_64-cc","cc:-arch x86_64 -O3 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch x86_64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", -+"darwin-ppc-cc","cc:-O3 -DB_ENDIAN -Wa,-force_cpusubtype_ALL::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${ppc32_asm}:osx32:dlfcn:darwin-shared:-fPIC -fno-common:-dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", -+"darwin64-ppc-cc","cc:-O3 -DB_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${ppc64_asm}:osx64:dlfcn:darwin-shared:-fPIC -fno-common:-dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", -+"darwin-i386-cc","cc:-O3 -fomit-frame-pointer -DL_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_INT RC4_CHUNK DES_UNROLL BF_PTR:".eval{my $asm=$x86_asm;$asm=~s/cast\-586\.o//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", -+"debug-darwin-i386-cc","cc:-g3 -DL_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_INT RC4_CHUNK DES_UNROLL BF_PTR:${x86_asm}:macosx:dlfcn:darwin-shared:-fPIC -fno-common:-dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", -+"darwin64-x86_64-cc","cc:-O3 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", - "debug-darwin-ppc-cc","cc:-DBN_DEBUG -DREF_CHECK -DCONF_DEBUG -DCRYPTO_MDEBUG -DB_ENDIAN -g -Wall -O::-D_REENTRANT:MACOSX::BN_LLONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${ppc32_asm}:osx32:dlfcn:darwin-shared:-fPIC:-dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", - # iPhoneOS/iOS - "iphoneos-cross","llvm-gcc:-O3 -isysroot \$(CROSS_TOP)/SDKs/\$(CROSS_SDK) -fomit-frame-pointer -fno-common::-D_REENTRANT:iOS:-Wl,-search_paths_first%:BN_LLONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${no_asm}:dlfcn:darwin-shared:-fPIC -fno-common:-dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", From c75fb7c8d6ec1f2f92d69155827e4f0ebfc0f8f2 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 11 Jun 2014 11:17:12 +0100 Subject: [PATCH 247/546] all latex works except one line --- src/sage/game_theory/cooperative_game.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 1db8a5e06e6..d61a5f4aa4c 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -49,8 +49,8 @@ class CooperativeGame(SageObject): $C=2^{\Omega}$ is set of all coalitions of players. An example of such a game is shown below: - $ - v(c) = \begin{cases} + + `v(c) = \begin{cases} 0,&\text{ if }c=\emptyset\\ 6,&\text{ if }c=\{1\}\\ 12,&\text{ if }c=\{2\}\\ @@ -59,8 +59,8 @@ class CooperativeGame(SageObject): 42,&\text{ if }c=\{1,3\}\\ 42,&\text{ if }c=\{2,3\}\\ 42,&\text{ if }c=\{1, 2,3\}\\ - \end{cases} - $ + \end{cases}` + The function $v$ can be thought of as as a record of contribution of individuals and coalitions of individuals. Of interest, becomes how to @@ -117,7 +117,7 @@ class CooperativeGame(SageObject): A characteristic function game $G=(N,v)$ is monotone if it satisfies $v(C_2)\geq v(C_1)$ for all $C_1\subseteq C_2$. A characteristic function game $G=(N,v)$ is super-additive if it satisfies $v(C_2)\geq v(C_1)$ for - all $C_1\subseteq C_2$ such that \[C_1 \cap \C_2 = \emptyset\]. + all $C_1\subseteq C_2$ such that C_1 \cap \C_2 = \emptyset. We can test if a game is Monotonic or Superadditive. :: @@ -148,20 +148,20 @@ class CooperativeGame(SageObject): {'A': 2, 'C': 35, 'B': 5} We can test 3 basic properties of a Payoff Vector $\lambda$. They are - * Efficiency - $sum_{i=1}^N\lambda_i=v(\Omega)$ + * Efficiency - `sum_{i=1}^N\lambda_i=v(\Omega)` In other words, no value of the total coalition is lost. - * The nullplayer property - If $\exists$ $i$ such that - $v(C\cup i)=v(C)$ for all - $C\in 2^{\Omega}$ then, $\lambda_i=0$. + * The nullplayer property - If `\exists` `i` such that + `v(C\cup i)=v(C)` for all + `C\in 2^{\Omega}` then, `\lambda_i=0`. In other words: if a player does not contribute to any coalition then that player should receive no payoff. * Symmetry property - A payoff vector possesses the symmetry property - if $v(C\cup i)=v(C\cup j)$ for all - $C\in 2^{\Omega}\setminus{i,j}$, then - $x_i=x_j$ + if `v(C\cup i)=v(C\cup j)` for all + `C\in 2^{\Omega}\setminus{i,j}`, then + `x_i=x_j` If players contribute symmetrically then they should get the same payoff. From ab07499fd91db16070b2018f2d8901a69c67f414 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 11 Jun 2014 17:08:51 +0100 Subject: [PATCH 248/546] minor change to docs --- src/sage/game_theory/cooperative_game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index d61a5f4aa4c..d403d9120e6 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -117,7 +117,7 @@ class CooperativeGame(SageObject): A characteristic function game $G=(N,v)$ is monotone if it satisfies $v(C_2)\geq v(C_1)$ for all $C_1\subseteq C_2$. A characteristic function game $G=(N,v)$ is super-additive if it satisfies $v(C_2)\geq v(C_1)$ for - all $C_1\subseteq C_2$ such that C_1 \cap \C_2 = \emptyset. + all $C_1\subseteq C_2$ such that `C_1 \cap \C_2` = \emptyset. We can test if a game is Monotonic or Superadditive. :: From dfec33e38599bf0c01416c0e483d6c7d99888b25 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 11 Jun 2014 17:11:43 +0100 Subject: [PATCH 249/546] removes space in docs --- src/sage/game_theory/cooperative_game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index d403d9120e6..3e0d54fdda3 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -117,7 +117,7 @@ class CooperativeGame(SageObject): A characteristic function game $G=(N,v)$ is monotone if it satisfies $v(C_2)\geq v(C_1)$ for all $C_1\subseteq C_2$. A characteristic function game $G=(N,v)$ is super-additive if it satisfies $v(C_2)\geq v(C_1)$ for - all $C_1\subseteq C_2$ such that `C_1 \cap \C_2` = \emptyset. + all $C_1\subseteq C_2$ such that `C_1 \cap\C_2` = \emptyset. We can test if a game is Monotonic or Superadditive. :: From d7553b01117ba6ed5da2f3430db64b4481bd0a02 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 11 Jun 2014 18:19:38 +0100 Subject: [PATCH 250/546] docs build as expected --- src/sage/game_theory/cooperative_game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 3e0d54fdda3..ba7906c509c 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -117,7 +117,7 @@ class CooperativeGame(SageObject): A characteristic function game $G=(N,v)$ is monotone if it satisfies $v(C_2)\geq v(C_1)$ for all $C_1\subseteq C_2$. A characteristic function game $G=(N,v)$ is super-additive if it satisfies $v(C_2)\geq v(C_1)$ for - all $C_1\subseteq C_2$ such that `C_1 \cap\C_2` = \emptyset. + all $C_1\subseteq C_2$ such that `C_1\cap C_2 = \emptyset`. We can test if a game is Monotonic or Superadditive. :: From 8d8b99b4180ecf59bd08e7cf6279971193978fe7 Mon Sep 17 00:00:00 2001 From: drvinceknight Date: Wed, 11 Jun 2014 22:07:11 +0100 Subject: [PATCH 251/546] Citing book and changed omega to capital --- src/sage/game_theory/cooperative_game.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 1db8a5e06e6..54ef949675a 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -4,7 +4,7 @@ This module implements a **basic** implementation of a characteristic function cooperative game. The main contribution is a class for a characteristic function game. Methods to calculate the Shapley value (a fair way of sharing -common resources: https://www.youtube.com/watch?v=aThG4YAFErw) as well as +common resources: (see 'Computational aspects of cooperative game theory' by Chalkiadakis et al.) as well as test properties of the game (monotonicity, super additivity) are also included. AUTHOR: @@ -44,7 +44,7 @@ class CooperativeGame(SageObject): EXAMPLES: The type of game that is currently implemented is referred to as a - Characteristic Function Game. This is a game on a set $\omega$ of players + Characteristic Function Game. This is a game on a set $\Omega$ of players that is defined by a value function $v:C\to \mathbb{R}$ where $C=2^{\Omega}$ is set of all coalitions of players. An example of such a game is shown below: @@ -64,7 +64,7 @@ class CooperativeGame(SageObject): The function $v$ can be thought of as as a record of contribution of individuals and coalitions of individuals. Of interest, becomes how to - fairly share the value of the grand coalition ($\omega$)? This class + fairly share the value of the grand coalition ($\Omega$)? This class allows for such an answer to be formulated by calculating the Shapley value of the game. @@ -98,7 +98,7 @@ class CooperativeGame(SageObject): The following example implements a (trivial) 8 player characteristic function game: - $v(c)=|c|\text{ for all }c\in 2^{\omega}$ + $v(c)=|c|\text{ for all }c\in 2^{\Omega}$ :: From 33df3a09c4bd89b88ce32363d83c714b467f49cf Mon Sep 17 00:00:00 2001 From: drvinceknight Date: Wed, 11 Jun 2014 22:11:03 +0100 Subject: [PATCH 252/546] Have reordered things --- src/sage/game_theory/cooperative_game.py | 46 ++++++++++++------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index d5850a203b2..87908c8ee60 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -49,7 +49,7 @@ class CooperativeGame(SageObject): $C=2^{\Omega}$ is set of all coalitions of players. An example of such a game is shown below: - + `v(c) = \begin{cases} 0,&\text{ if }c=\emptyset\\ 6,&\text{ if }c=\{1\}\\ @@ -60,7 +60,7 @@ class CooperativeGame(SageObject): 42,&\text{ if }c=\{2,3\}\\ 42,&\text{ if }c=\{1, 2,3\}\\ \end{cases}` - + The function $v$ can be thought of as as a record of contribution of individuals and coalitions of individuals. Of interest, becomes how to @@ -68,7 +68,7 @@ class CooperativeGame(SageObject): allows for such an answer to be formulated by calculating the Shapley value of the game. - Basic example of how to implement a co-operative game. These functions will + Basic examples of how to implement a co-operative game. These functions will be used repeatedly in other examples. :: sage: integer_function = {(): 0, @@ -95,25 +95,6 @@ class CooperativeGame(SageObject): Characteristic function games can be of various types. - The following example implements a (trivial) 8 player characteristic - function game: - - $v(c)=|c|\text{ for all }c\in 2^{\Omega}$ - - :: - - sage: def simple_characteristic_function(N): - ....: return {tuple(coalition) : len(coalition) - ....: for coalition in subsets(range(N))} - sage: g = CooperativeGame(simple_characteristic_function(8)) - sage: g.shapley_value() # long time - {0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1} - - The above is slow to run and this is due to the dimensionality - of the calculations involved. There are various approximation - approaches to obtaining the Shapley value of a game. Implementing - these would be a worthwhile development. - A characteristic function game $G=(N,v)$ is monotone if it satisfies $v(C_2)\geq v(C_1)$ for all $C_1\subseteq C_2$. A characteristic function game $G=(N,v)$ is super-additive if it satisfies $v(C_2)\geq v(C_1)$ for @@ -147,7 +128,26 @@ class CooperativeGame(SageObject): sage: letter_game.shapley_value() {'A': 2, 'C': 35, 'B': 5} - We can test 3 basic properties of a Payoff Vector $\lambda$. They are + The following example implements a (trivial) 8 player characteristic + function game: + + $v(c)=|c|\text{ for all }c\in 2^{\Omega}$ + + :: + + sage: def simple_characteristic_function(N): + ....: return {tuple(coalition) : len(coalition) + ....: for coalition in subsets(range(N))} + sage: g = CooperativeGame(simple_characteristic_function(8)) + sage: g.shapley_value() # long time + {0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1} + + The above is slow to run and this is due to the dimensionality + of the calculations involved. There are various approximation + approaches to obtaining the Shapley value of a game. Implementing + these would be a worthwhile development. + + We can test 3 basic properties of any Payoff Vector $\lambda$. They are * Efficiency - `sum_{i=1}^N\lambda_i=v(\Omega)` In other words, no value of the total coalition is lost. From a2acb8f97c90ba072ded262218e9f3fddd225343 Mon Sep 17 00:00:00 2001 From: drvinceknight Date: Wed, 11 Jun 2014 22:15:54 +0100 Subject: [PATCH 253/546] Added some text --- src/sage/game_theory/cooperative_game.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 87908c8ee60..ea182869940 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -147,7 +147,11 @@ class CooperativeGame(SageObject): approaches to obtaining the Shapley value of a game. Implementing these would be a worthwhile development. - We can test 3 basic properties of any Payoff Vector $\lambda$. They are + We can test 3 basic properties of any Payoff Vector $\lambda$. + The Shapley value (described above) is none to be the unique + payoff vector that satisfies these and 1 other property + not implemented here (additivity). + They are * Efficiency - `sum_{i=1}^N\lambda_i=v(\Omega)` In other words, no value of the total coalition is lost. From 09b2029d19c35c2960d2102e9bdffc4e10df4f9e Mon Sep 17 00:00:00 2001 From: Jayant Date: Thu, 12 Jun 2014 01:55:22 -0400 Subject: [PATCH 254/546] Added ability for user to specify point positions, added option/placeholder for point placement algorithm. Added option for user to specify whether point placement will be cached or not. --- src/sage/matroids/matroid.pxd | 4 +- src/sage/matroids/matroid.pyx | 52 ++++++-- src/sage/matroids/matroids_plot_helpers.py | 137 +++++++++++++++------ 3 files changed, 143 insertions(+), 50 deletions(-) diff --git a/src/sage/matroids/matroid.pxd b/src/sage/matroids/matroid.pxd index 4dabdfee808..8178f67af8b 100644 --- a/src/sage/matroids/matroid.pxd +++ b/src/sage/matroids/matroid.pxd @@ -145,5 +145,5 @@ cdef class Matroid(SageObject): cpdef flat_cover(self) # visualization - cpdef plot(self,B=*,lineorders=*) - cpdef show(self,B=*,lineorders=*) + cpdef plot(self,B=*,lineorders=*,pos_method=*,pos_dict=*,save_pos=*) + cpdef show(self,B=*,lineorders=*,pos_method=*,pos_dict=*,save_pos=*) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index 4bea27d6cf8..0ca7f8529f9 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -4749,17 +4749,26 @@ cdef class Matroid(SageObject): return [F for F in FF if fsol[F] > 1 - eps] - cpdef plot(self,B=None,lineorders=None): + cpdef plot(self, B=None, lineorders=None, pos_method=None,pos_dict=None,save_pos=False): """ Return geometric representation as a sage graphics object. INPUT: - - ``B`` -- (optional) a list containing elements of the groundset - not in any particular order. + - ``B`` -- (optional) a list containing elements of the groundset not in any particular order. + If internal point placement is used, these elements will be placed as vertices of a triangle. - ``lineorders`` -- (optional) A list of lists where each of the inner lists specify ground set elements in a certain order which will be used to draw the corresponding line in geometric representation (if it exists). + - ``pos_method`` -- An integer specifying positioning method + ``0``: default positioning + ``1``: use pos_dict if it is not ``None`` + ``2``: Force directed (Not yet implemented). + - ``pos_dict``: A dictionary mapping ground set elements to their (x,y) positions. + - ``save_pos``: A boolean indicating that point placements (either internal or user provided) and + line orders (if provided) will be cached in the matroid (``M._cached_info``) and can be used for + reproducing the geometric representation during the same session + OUTPUT: @@ -4775,6 +4784,15 @@ cdef class Matroid(SageObject): sage: G.show() """ + import matroids_plot_helpers + if pos_method == 1 and pos_dict!=None: + # check sanity of pos_dict and add it to cached info if sane + if matroids_plot_helpers.posdict_is_sane(self,pos_dict) ==True: + self._cached_info={'positions':pos_dict,'lineorders':lineorders} + # placeholder for aditional placement methods. Only need to compute positions and update self._cached_info + elif pos_method == 2: + raise NotImplementedError + if self._cached_info == None: self._cached_info={'positions':None,'lineorders': None} if 'positions' not in self._cached_info.keys(): @@ -4786,23 +4804,30 @@ cdef class Matroid(SageObject): return elif B == None: B = list(self.basis()) - elif self.rank() != self.rank(B): + elif self.is_basis(B)==False: return - import matroids_plot_helpers lineorders2=matroids_plot_helpers.lineorders_union(self._cached_info['lineorders'],lineorders) - return matroids_plot_helpers.geomrep(self,B,lineorders2) + return matroids_plot_helpers.geomrep(self,B,lineorders2,pd=pos_dict, sp=save_pos) - cpdef show(self,B=None,lineorders=None): + cpdef show(self,B=None,lineorders=None,pos_method=None,pos_dict=None,save_pos=False): """ Show the geometric representation of the matroid. INPUT: - - ``B`` -- (optional) a list containing elements of the groundset - not in any particular order. + - ``B`` -- (optional) a list containing elements of the groundset not in any particular order. + If internal point placement is used, these elements will be placed as vertices of a triangle. - ``lineorders`` -- (optional) A list of lists where each of the inner lists specify ground set elements in a certain order which will be used to draw the corresponding line in geometric representation (if it exists). + - ``pos_method`` -- An integer specifying positioning method + ``0``: default positioning + ``1``: use pos_dict if it is not ``None`` + ``2``: Force directed (Not yet implemented). + - ``pos_dict``: A dictionary mapping ground set elements to their (x,y) positions. + - ``save_pos``: A boolean indicating that point placements (either internal or user provided) and + line orders (if provided) will be cached in the matroid (``M._cached_info``) and can be used for + reproducing the geometric representation during the same session EXAMPLES:: @@ -4815,10 +4840,13 @@ cdef class Matroid(SageObject): return elif B == None: B = list(self.basis()) - elif self.rank() != self.rank(B): + elif self.is_basis(B)==False: return B1=B - lineorders1=lineorders - G=self.plot(B1,lineorders1) + lineorders1=lineorders + pm=pos_method + pd=pos_dict + sp=save_pos + G=self.plot(B1,lineorders1,pm,pd,sp) G.show(xmin=-2, xmax=3, ymin=-2, ymax=3) return diff --git a/src/sage/matroids/matroids_plot_helpers.py b/src/sage/matroids/matroids_plot_helpers.py index 8536cadba0b..22b4ce86e1a 100644 --- a/src/sage/matroids/matroids_plot_helpers.py +++ b/src/sage/matroids/matroids_plot_helpers.py @@ -37,9 +37,9 @@ def initial_triangle(M,B1,nB1,lps): INPUT: - ``M`` -- A matroid. - - ``B1``-- A list of labels of groundset elements of M that corresponds to a basis of matroid M - - ``nB1``-- A list of labes of elements in the ground set of M that corresponds to ``M.simplify.groundset() \ B1``. - - ``lps``-- A list of labels of elements in the ground set of matroid M that are loops. + - ``B1``-- A list of groundset elements of ``M`` that corresponds to a basis of matroid ``M`` + - ``nB1``-- A list of elements in the ground set of M that corresponds to ``M.simplify.groundset() \ B1``. + - ``lps``-- A list of elements in the ground set of matroid M that are loops. OUTPUT: @@ -162,13 +162,13 @@ def addnontripts(tripts_labels,nontripts_labels,ptsdict): INPUT: - - ``tripts`` -- A list of 3 labels that are to be placed on vertices of the triangle - - ``ptsdict`` -- A dictionary (at least) containing labels in ``tripts`` as keys and their (x,y) position as values - - ``nontripts``-- A list of labels whose corresponding points are to be placed inside the triangle + - ``tripts`` -- A list of 3 ground set elements that are to be placed on vertices of the triangle + - ``ptsdict`` -- A dictionary (at least) containing ground set elements in ``tripts`` as keys and their (x,y) position as values + - ``nontripts``-- A list of ground set elements whose corresponding points are to be placed inside the triangle OUTPUT: - A dictionary containing labels in ``tripts`` as keys and their (x,y) position as values allong with all keys and respective values in ``ptsdict`` + A dictionary containing ground set elements in ``tripts`` as keys and their (x,y) position as values allong with all keys and respective values in ``ptsdict`` EXAMPLES:: @@ -277,32 +277,41 @@ def createline(ptsdict,ll,lineorders2=None): x_i,y_i = scipy.interpolate.splev(np.linspace(0,1,100),tck) return sortedx,sortedy,x_i,y_i -def slp(M1): +def slp(M1,pos_dict=None): """ Return simple matroid, loops and parallel elements of given matroid INPUT: - ``M1`` -- A matroid. + - ``pos_dict`` -- (optional) A dictionary containing non loopy elements of ``M`` as keys and their (x,y) positions + as keys. While simplifying the matroid, all except one element in a parallel class that is also specified in ``pos_dict`` + will be retained. OUTPUT: A tuple containing 3 elements in this order: - 1. Simple matroid corresponding to M1 + 1. Simple matroid corresponding to ``M1`` 2. Loops of matroid ``M1`` 3. Elements that are in `M1.groundset()` but not in ground set of 1. or in 2. EXAMPLES:: sage: from sage.matroids import matroids_plot_helpers - sage: M=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1],[0, 1, 0, 1, 0, 1, 1,0,0],[0, 0, 1, 1, 1, 0, 1,0,0]]) - sage: [M1,L,P]=matroids_plot_helpers.slp(M) - sage: print L,P - set([7]) set([8]) - sage: M=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1],[0, 1, 0, 1, 0, 1, 1,0,0],[0, 0, 1, 1, 1, 0, 1,0,0]]) - sage: [M1,L,P]=matroids_plot_helpers.slp(M) - sage: M1.is_simple() + sage: M1=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1,0,1],[0, 1, 0, 1, 0, 1, 1,0,0,1,0],[0, 0, 1, 1, 1, 0, 1,0,0,0,0]]) + sage: [M,L,P]=matroids_plot_helpers.slp(M1) + sage: M.is_simple() True + sage: [L,P] + [{7}, {8, 9, 10}] + + sage: M1=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1,0,1],[0, 1, 0, 1, 0, 1, 1,0,0,1,0],[0, 0, 1, 1, 1, 0, 1,0,0,0,0]]) + sage: posdict= {8: (0, 0), 1: (2, 0), 2: (1, 2), 3: (1.5, 1.0), 4: (0.5, 1.0), 5: (1.0, 0.0), 6: (1.0, 0.6666666666666666)} + sage: [M,L,P]=matroids_plot_helpers.slp(M1,pos_dict=posdict) + sage: M.is_simple() + True + sage: [L,P] + [{7}, {0, 9, 10}] .. NOTE:: @@ -313,17 +322,25 @@ def slp(M1): sg = sorted(M1.simplify().groundset()) nP = L|set(M1.simplify().groundset()) P = set(M1.groundset())-nP - return [M1.delete(L|P),L,P] + if pos_dict != None: + pcls=list(set([frozenset(set(M1.closure([p]))-L) for p in list(P)])) + newP=[] + for pcl in pcls: + pcl_in_dict=[p for p in list(pcl) if p in pos_dict.keys()] + newP.extend(list(pcl-set([pcl_in_dict[0]]))) + return [M1.delete(L|set(newP)),L,set(newP)] + else: + return [M1.delete(L|P),L,P] -def addlp(M,L,P,ptsdict,G=None): +def addlp(M,M1,L,P,ptsdict,G=None): """ Return a graphics object containing loops (in inset) and parallel elements of matroid INPUT: - ``M`` -- A matroid. - - ``L`` -- List of labels of elements in ``M.groundset()`` that are loops of matroid ``M`` - - ``P`` -- List of labels of elements in ``M.groundset()`` not in ``M.simplify.groundset()`` or ``L`` + - ``L`` -- List of elements in ``M.groundset()`` that are loops of matroid ``M`` + - ``P`` -- List of elements in ``M.groundset()`` not in ``M.simplify.groundset()`` or ``L`` - ``ptsdict`` -- A dictionary containing elements in ``M.groundset()`` not necessarily containing elements of ``L`` - ``G`` -- (optional) A sage graphics object to which loops and parallel elements of matroid `M` added @@ -335,7 +352,7 @@ def addlp(M,L,P,ptsdict,G=None): sage: from sage.matroids import matroids_plot_helpers sage: M=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1],[0, 1, 0, 1, 0, 1, 1,0,0],[0, 0, 1, 1, 1, 0, 1,0,0]]) sage: [M1,L,P]=matroids_plot_helpers.slp(M) - sage: G=matroids_plot_helpers.addlp(M,L,P,{0:(0,0)}) + sage: G=matroids_plot_helpers.addlp(M,M1,L,P,{0:(0,0)}) sage: G.show(axes=False) sage: sage: G.show(axes=False) @@ -346,7 +363,7 @@ def addlp(M,L,P,ptsdict,G=None): """ if G == None: G = Graphics() - M1 = M.simplify() + #M1 = M.simplify() # deal with loops if len(L) > 0: loops = L @@ -388,8 +405,8 @@ def line_hasorder(l,lodrs=None): INPUT: - - ``l`` -- A line containing specified as a list of labels - - ``lordrs`` -- (optional) A list of lists each specifying an order on the subset of labels corresponding to that list + - ``l`` -- A line specified as a list of ground set elements + - ``lordrs`` -- (optional) A list of lists each specifying an order on the subset of ground set elements that may or may not correspond to a line in geometric representation OUTPUT: @@ -419,17 +436,17 @@ def line_hasorder(l,lodrs=None): def lineorders_union(lineorders1,lineorders2): """ - Return a list of ordered lists labels that corresponds to union of two sets of ordered lists of labels in a sense + Return a list of ordered lists of ground set elements that corresponds to union of two sets of ordered lists of ground set elements in a sense INPUT: - - ``lineorders1`` -- A list of ordered lists specifying orders on sets of labels - - ``lineorders2`` -- A list of ordered lists specifying orders on sets of labels + - ``lineorders1`` -- A list of ordered lists specifying orders on subsets of ground set. + - ``lineorders2`` -- A list of ordered lists specifying orders subsets of ground set. OUTPUT: - A list of ordered lists of labels that are (setwise) in only one of ``lineorders1`` or ``lineorders2`` - along with the ones in lineorder2 that are setwise equal to any list in lineorders1 + A list of ordered lists of ground set elements that are (setwise) in only one of ``lineorders1`` or ``lineorders2`` + along with the ones in lineorder2 that are setwise equal to any list in lineorders1. EXAMPLES:: @@ -453,24 +470,70 @@ def lineorders_union(lineorders1,lineorders2): else: return None +def posdict_is_sane(M1,pos_dict): + """ + Return a boolean establishing sanity of ``posdict`` wrt matroid ``M`` + + INPUT: + + - ``M1`` -- A matroid. + - ``posdict`` -- A dictionary mapping ground set elements to (x,y) positions + + OUTPUT: + A boolean that is ``True`` if posdict indeed has all the required elements to plot the geometric elements, otherwise ``False`` + + EXAMPLES: + + sage: from sage.matroids import matroids_plot_helpers + sage: M1=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1,0,1],[0, 1, 0, 1, 0, 1, 1,0,0,1,0],[0, 0, 1, 1, 1, 0, 1,0,0,0,0]]) + sage: pos_dict= {0: (0, 0), 1: (2, 0), 2: (1, 2), 3: (1.5, 1.0), 4: (0.5, 1.0), 5: (1.0, 0.0), 6: (1.0, 0.6666666666666666)} + sage: matroids_plot_helpers.posdict_is_sane(M1,pos_dict) + True + sage: pos_dict= {1: (2, 0), 2: (1, 2), 3: (1.5, 1.0), 4: (0.5, 1.0), 5: (1.0, 0.0), 6: (1.0, 0.6666666666666666)} + sage: matroids_plot_helpers.posdict_is_sane(M1,pos_dict) + False + + + .. NOTE:: + + This method does NOT do any checks. ``M1`` is assumed to be a matroid and ``posdict`` is assumed to be a dictionary + """ + L = set(M1.loops()) + sg = sorted(M1.simplify().groundset()) + nP = L|set(M1.simplify().groundset()) + P = set(M1.groundset())-nP + #pcls=list(set([set(M1.closure([p]))-L for p in list(P)])) + pcls=list(set([frozenset(set(M1.closure([p]))-L) for p in list(P)])) + for pcl in pcls: + pcl_list=list(pcl) + if not any([x in pos_dict.keys() for x in pcl_list]): + return False + allP=[] + for pcl in pcls: + allP.extend(list(pcl)) + if not all([x in pos_dict.keys() for x in list(set(M1.groundset())-(L|set(allP)))]): + return False + return True -def geomrep(M1,B1=None,lineorders1=None): +def geomrep(M1,B1=None,lineorders1=None,pd=None,sp=False): """ Return a sage graphics object containing geometric representation of matroid M1 INPUT: - ``M1`` -- A matroid. - - ``B1`` -- (optional) A list of labels in ``M1.groundset()`` that correspond to a basis + - ``B1`` -- (optional) A list of elements in ``M1.groundset()`` that correspond to a basis of ``M1`` and will be placed as vertices of the triangle in the geometric representation of ``M1`` - ``lineorders1`` -- (optional) A list of ordered lists of elements of ``M1.grondset()`` such that if a line in geometric representation is setwise same as any of these then points contained will be traversed in that order thus overriding internal order deciding heuristic. + - ``pd`` - (optional) A dictionary mapping ground set elements to their (x,y) positions. + - ``sp`` -- (optional) If True, a positioning dictionary and line orders will be placed in ``M._cached_info`` OUTPUT: @@ -494,7 +557,7 @@ def geomrep(M1,B1=None,lineorders1=None): B1 = list(M1.basis()) G = Graphics() # create lists of loops and parallel elements and simplify given matroid - [M,L,P] = slp(M1) + [M,L,P] = slp(M1,pos_dict=pd) M._cached_info = M1._cached_info if M.rank()==1: if M._cached_info != None and 'positions' in M._cached_info.keys() and M._cached_info['positions'] != None: @@ -526,7 +589,8 @@ def geomrep(M1,B1=None,lineorders1=None): for k in range(len(bline)): cc = (float(1)/interval)*(k+1) pts2[bline[k]] = (cc*lpt[0]+(1-cc)*rpt[0],cc*lpt[1]+(1-cc)*rpt[1]) - M._cached_info['positions']=pts2 + if sp == True: + M._cached_info['positions']=pts2 bline.extend(B1) ptsx,ptsy,x_i,y_i = createline(pts2,bline,lineorders1) @@ -559,9 +623,10 @@ def geomrep(M1,B1=None,lineorders1=None): for i in pts2: pt = list(pts2[i]) G += text(i,(float(pt[0]), float(pt[1])),color='white',fontsize=13) - M1._cached_info['positions']=pts2 - M1._cached_info['lineorders']=lineorders1 + if sp == True: + M1._cached_info['positions']=pts2 + M1._cached_info['lineorders']=lineorders1 #deal with loops and parallel elements - G = addlp(M1,L,P,pts2,G) + G = addlp(M1,M,L,P,pts2,G) G.axes(False) return G From a06b27564cbdabf0c47cd8629f256e823f48430b Mon Sep 17 00:00:00 2001 From: James Campbell Date: Thu, 12 Jun 2014 10:05:18 +0100 Subject: [PATCH 255/546] fixes indentation error --- src/sage/game_theory/cooperative_game.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index ea182869940..bf761e63737 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -151,7 +151,8 @@ class CooperativeGame(SageObject): The Shapley value (described above) is none to be the unique payoff vector that satisfies these and 1 other property not implemented here (additivity). - They are + They are: + * Efficiency - `sum_{i=1}^N\lambda_i=v(\Omega)` In other words, no value of the total coalition is lost. From 1611e8d06e5bb727068ec18be3d9e19a6d1c314b Mon Sep 17 00:00:00 2001 From: Jayant Date: Fri, 13 Jun 2014 23:56:25 -0400 Subject: [PATCH 256/546] 100% docstring coverage and minor documentation fixes. --- src/sage/matroids/matroids_plot_helpers.py | 142 +++++++++++++-------- 1 file changed, 91 insertions(+), 51 deletions(-) diff --git a/src/sage/matroids/matroids_plot_helpers.py b/src/sage/matroids/matroids_plot_helpers.py index 22b4ce86e1a..f1872fb2a82 100644 --- a/src/sage/matroids/matroids_plot_helpers.py +++ b/src/sage/matroids/matroids_plot_helpers.py @@ -6,9 +6,47 @@ - Jayant Apte (2014-06-06): initial version + .. NOTE:: + + This file provides functions that are called by ``show()`` and ``plot()`` + methods of abstract matroids class. The basic idea is to first decide the placement + of points in $\mathbb{R}^2$ and then draw lines in geometric representation through these points. + Point placement procedures such as ``addtripts``, ``addnontripts`` together + produce ``(x,y)`` tuples corresponding to ground set of the matroid in a dictionary. + These methods provide simple but rigid point placement algorithm. Alternatively, + one can build the point placement dictionary manually or via an optimization that + gives aesthetically pleasing point placement (in some sense. This is not yet implemented). + One can then use ``createline`` function to produce sequence of ``100`` points on a + smooth curve containing the points in the specified line which inturn uses ``scipy.interpolate.splprep``. + and ``scipy.interpolate.splprep`` Then one an use sage's + graphics primitives ``line``,``point``,``text`` and``points`` to produce graphics + object containg points (ground set elements) and lines (for a rank 3 matroid, these + are flats of rank 2 of size greater than equal to 3) of the geometric representation + of the matroid. Loops and parallel elements are added as per conventions in [Oxley] + using function ``addlp``. The priority order for point placement methods used inside + plot() and show() is as follows: + 1) User Specified points dictionary and lineorders + 2) cached point placement dictionary and line orders (alist of ordered lists) in + M._cached_info (a dictionary) + 3) Internal point placement and orders deciding heuristics + If a custom point placement and/or line orders is desired, then user can simply specify + the custom points dictionary as + ``M.cached info = {'positions':dictionary_of _points``,'lineorders') + + +REFERENCES +========== + +.. [Oxley] James Oxley, "Matroid Theory, Second Edition". Oxford University Press, 2011. EXAMPLES:: + sage: from sage.matroids import matroids_plot_helpers + sage: M1=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1,0,1],[0, 1, 0, 1, 0, 1, 1,0,0,1,0],[0, 0, 1, 1, 1, 0, 1,0,0,0,0]]) + sage: pos_dict= {0: (0, 0), 1: (2, 0), 2: (1, 2), 3: (1.5, 1.0), 4: (0.5, 1.0), 5: (1.0, 0.0), 6: (1.0, 0.6666666666666666)} + sage: M1._cached_info={'positions':pos_dict,'lineorders':None} + sage: matroids_plot_helpers.geomrep(M1,sp=True) + """ #***************************************************************************** @@ -30,14 +68,14 @@ from sage.plot.all import Graphics, line, text, polygon2d, point,points from sage.sets.set import Set -def initial_triangle(M,B1,nB1,lps): +def initial_triangle(M, B1, nB1, lps): """ - Return points on and off the triangle and lines to be drawn for a rank 3 matroid + Return points on and off the triangle and lines to be drawn for a rank 3 matroid. INPUT: - ``M`` -- A matroid. - - ``B1``-- A list of groundset elements of ``M`` that corresponds to a basis of matroid ``M`` + - ``B1``-- A list of groundset elements of ``M`` that corresponds to a basis of matroid ``M``. - ``nB1``-- A list of elements in the ground set of M that corresponds to ``M.simplify.groundset() \ B1``. - ``lps``-- A list of elements in the ground set of matroid M that are loops. @@ -45,10 +83,10 @@ def initial_triangle(M,B1,nB1,lps): A tuple containing 4 elements in this order: - 1. A dictionary containing 2-tuple (x,y) co-ordinates with ``M.simplify.groundset()`` elements that can be placed on the sides of the triangle as keys - 2. A list of 3 lists of elements of ``M.simplify.groundset()`` that can be placed on the 3 sides of the triangle - 3. A list of elements of `M.simplify.groundset()`` that cane be placed inside the triangle in the geometric representation - 4. A list of lists of elements of ``M.simplify.groundset()`` that correspond to lines in the geometric representation other than the sides of the triangle + 1. A dictionary containing 2-tuple (x,y) co-ordinates with ``M.simplify.groundset()`` elements that can be placed on the sides of the triangle as keys. + 2. A list of 3 lists of elements of ``M.simplify.groundset()`` that can be placed on the 3 sides of the triangle. + 3. A list of elements of `M.simplify.groundset()`` that cane be placed inside the triangle in the geometric representation. + 4. A list of lists of elements of ``M.simplify.groundset()`` that correspond to lines in the geometric representation other than the sides of the triangle. EXAMPLES:: @@ -120,19 +158,19 @@ def initial_triangle(M,B1,nB1,lps): def trigrid(tripts): """ - Return a grid of 4 points inside given 3 points as a list + Return a grid of 4 points inside given 3 points as a list. INPUT: - - ``tripts`` -- A list of 3 lists of the form [x,y] where x and y are the cartesian co-ordinates of a point + - ``tripts`` -- A list of 3 lists of the form [x,y] where x and y are the cartesian co-ordinates of a point. OUTPUT: A list of lists containing 4 points in following order: - 1. Barycenter of 3 input points - 2,3,4. Barycenters of 1. with 3 different 2-subsets of input points respectively + 1. Barycenter of 3 input points. + 2,3,4. Barycenters of 1. with 3 different 2-subsets of input points respectively. EXAMPLES:: @@ -157,18 +195,18 @@ def trigrid(tripts): def addnontripts(tripts_labels,nontripts_labels,ptsdict): """ - Return modified ``ptsdict`` with additional keys and values corresponding to ``nontripts`` + Return modified ``ptsdict`` with additional keys and values corresponding to ``nontripts``. INPUT: - - ``tripts`` -- A list of 3 ground set elements that are to be placed on vertices of the triangle - - ``ptsdict`` -- A dictionary (at least) containing ground set elements in ``tripts`` as keys and their (x,y) position as values - - ``nontripts``-- A list of ground set elements whose corresponding points are to be placed inside the triangle + - ``tripts`` -- A list of 3 ground set elements that are to be placed on vertices of the triangle. + - ``ptsdict`` -- A dictionary (at least) containing ground set elements in ``tripts`` as keys and their (x,y) position as values. + - ``nontripts``-- A list of ground set elements whose corresponding points are to be placed inside the triangle. OUTPUT: - A dictionary containing ground set elements in ``tripts`` as keys and their (x,y) position as values allong with all keys and respective values in ``ptsdict`` + A dictionary containing ground set elements in ``tripts`` as keys and their (x,y) position as values allong with all keys and respective values in ``ptsdict``. EXAMPLES:: @@ -206,9 +244,10 @@ def addnontripts(tripts_labels,nontripts_labels,ptsdict): j = j + 1 return ptsdict -def createline(ptsdict,ll,lineorders2=None): +def createline(ptsdict, ll, lineorders2=None): """ - Return ordered lists of co-ordinates of points to be traversed to draw a 2D line + Return ordered lists of co-ordinates of points to be traversed to draw a 2D line. + INPUT: - ``ptsdict`` -- A dictionary containing keys and their (x,y) position as values. @@ -221,10 +260,10 @@ def createline(ptsdict,ll,lineorders2=None): A tuple containing 4 elements in this order: - 1. Ordered list of x-coordinates of values of keys in ``ll`` specified in ptsdict - 2. Ordered list of y-coordinates of values of keys in ``ll`` specified in ptsdict - 3. Ordered list of interpolated x-coordinates of points through which a line can be drawn - 4. Ordered list of interpolated y-coordinates of points through which a line can be drawn + 1. Ordered list of x-coordinates of values of keys in ``ll`` specified in ptsdict. + 2. Ordered list of y-coordinates of values of keys in ``ll`` specified in ptsdict. + 3. Ordered list of interpolated x-coordinates of points through which a line can be drawn. + 4. Ordered list of interpolated y-coordinates of points through which a line can be drawn. Examples:: @@ -277,7 +316,7 @@ def createline(ptsdict,ll,lineorders2=None): x_i,y_i = scipy.interpolate.splev(np.linspace(0,1,100),tck) return sortedx,sortedy,x_i,y_i -def slp(M1,pos_dict=None): +def slp(M1, pos_dict=None): """ Return simple matroid, loops and parallel elements of given matroid @@ -291,8 +330,8 @@ def slp(M1,pos_dict=None): OUTPUT: A tuple containing 3 elements in this order: - 1. Simple matroid corresponding to ``M1`` - 2. Loops of matroid ``M1`` + 1. Simple matroid corresponding to ``M1``. + 2. Loops of matroid ``M1``. 3. Elements that are in `M1.groundset()` but not in ground set of 1. or in 2. EXAMPLES:: @@ -332,17 +371,17 @@ def slp(M1,pos_dict=None): else: return [M1.delete(L|P),L,P] -def addlp(M,M1,L,P,ptsdict,G=None): +def addlp(M, M1, L, P, ptsdict, G=None): """ - Return a graphics object containing loops (in inset) and parallel elements of matroid + Return a graphics object containing loops (in inset) and parallel elements of matroid. INPUT: - ``M`` -- A matroid. - - ``L`` -- List of elements in ``M.groundset()`` that are loops of matroid ``M`` - - ``P`` -- List of elements in ``M.groundset()`` not in ``M.simplify.groundset()`` or ``L`` - - ``ptsdict`` -- A dictionary containing elements in ``M.groundset()`` not necessarily containing elements of ``L`` - - ``G`` -- (optional) A sage graphics object to which loops and parallel elements of matroid `M` added + - ``L`` -- List of elements in ``M.groundset()`` that are loops of matroid ``M``. + - ``P`` -- List of elements in ``M.groundset()`` not in ``M.simplify.groundset()`` or ``L``. + - ``ptsdict`` -- A dictionary containing elements in ``M.groundset()`` not necessarily containing elements of ``L``. + - ``G`` -- (optional) A sage graphics object to which loops and parallel elements of matroid `M` added . OUTPUT: A sage graphics object containing loops and parallel elements of matroid ``M`` @@ -399,20 +438,19 @@ def addlp(M,M1,L,P,ptsdict,G=None): G += text('{ '+", ".join(sorted([str(kk) for kk in pcl[1:]]))+' }',(float(basept[0]), float(basept[1]-0.2)-0.034),color='black',fontsize=13) return G -def line_hasorder(l,lodrs=None): +def line_hasorder(l, lodrs=None): """ Determine if an order is specified for a line INPUT: - - ``l`` -- A line specified as a list of ground set elements - - ``lordrs`` -- (optional) A list of lists each specifying an order on the subset of ground set elements that may or may not correspond to a line in geometric representation - + - ``l`` -- A line specified as a list of ground set elements. + - ``lordrs`` -- (optional) A list of lists each specifying an order on the subset of ground set elements that may or may not correspond to a line in geometric representation. OUTPUT: A tuple containing 2 elements in this order: - 1. A boolean indicating whether there is any list in ``lordrs`` that is setwise equal to ``l`` - 2. A list specifying an order on ``set(l)`` if 1. is True, otherwise an empty list + 1. A boolean indicating whether there is any list in ``lordrs`` that is setwise equal to ``l``. + 2. A list specifying an order on ``set(l)`` if 1. is True, otherwise an empty list. EXAMPLES:: @@ -434,9 +472,9 @@ def line_hasorder(l,lodrs=None): return True,i return False,[] -def lineorders_union(lineorders1,lineorders2): +def lineorders_union(lineorders1, lineorders2): """ - Return a list of ordered lists of ground set elements that corresponds to union of two sets of ordered lists of ground set elements in a sense + Return a list of ordered lists of ground set elements that corresponds to union of two sets of ordered lists of ground set elements in a sense. INPUT: @@ -470,18 +508,18 @@ def lineorders_union(lineorders1,lineorders2): else: return None -def posdict_is_sane(M1,pos_dict): +def posdict_is_sane(M1, pos_dict): """ - Return a boolean establishing sanity of ``posdict`` wrt matroid ``M`` + Return a boolean establishing sanity of ``posdict`` wrt matroid ``M``. INPUT: - ``M1`` -- A matroid. - - ``posdict`` -- A dictionary mapping ground set elements to (x,y) positions + - ``posdict`` -- A dictionary mapping ground set elements to (x,y) positions. OUTPUT: - A boolean that is ``True`` if posdict indeed has all the required elements to plot the geometric elements, otherwise ``False`` + A boolean that is ``True`` if posdict indeed has all the required elements to plot the geometric elements, otherwise ``False``. EXAMPLES: @@ -497,7 +535,7 @@ def posdict_is_sane(M1,pos_dict): .. NOTE:: - This method does NOT do any checks. ``M1`` is assumed to be a matroid and ``posdict`` is assumed to be a dictionary + This method does NOT do any checks. ``M1`` is assumed to be a matroid and ``posdict`` is assumed to be a dictionary. """ L = set(M1.loops()) sg = sorted(M1.simplify().groundset()) @@ -520,25 +558,25 @@ def posdict_is_sane(M1,pos_dict): -def geomrep(M1,B1=None,lineorders1=None,pd=None,sp=False): +def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): """ - Return a sage graphics object containing geometric representation of matroid M1 + Return a sage graphics object containing geometric representation of matroid M1. INPUT: - ``M1`` -- A matroid. - - ``B1`` -- (optional) A list of elements in ``M1.groundset()`` that correspond to a basis - of ``M1`` and will be placed as vertices of the triangle in the geometric representation of ``M1`` + - ``B1`` -- (optional) A list of elements in ``M1.groundset()`` that correspond to a basis. + of ``M1`` and will be placed as vertices of the triangle in the geometric representation of ``M1``. - ``lineorders1`` -- (optional) A list of ordered lists of elements of ``M1.grondset()`` such that if a line in geometric representation is setwise same as any of these then points contained will be traversed in that order thus overriding internal order deciding heuristic. - ``pd`` - (optional) A dictionary mapping ground set elements to their (x,y) positions. - - ``sp`` -- (optional) If True, a positioning dictionary and line orders will be placed in ``M._cached_info`` + - ``sp`` -- (optional) If True, a positioning dictionary and line orders will be placed in ``M._cached_info``. OUTPUT: A sage graphics object of type that - corresponds to the geometric representation of the matroid + corresponds to the geometric representation of the matroid. EXAMPLES:: @@ -551,7 +589,9 @@ def geomrep(M1,B1=None,lineorders1=None,pd=None,sp=False): sage: G=matroids_plot_helpers.geomrep(M,lineorders1=[['f','e','d']]) sage: G.show(xmin=-2, xmax=3, ymin=-2, ymax=3) - + .. NOTE:: + + This method does NOT do any checks. """ if B1 == None: B1 = list(M1.basis()) From 884bc6841811077d55a9048d616cb973e313ef24 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sat, 14 Jun 2014 09:50:27 +0200 Subject: [PATCH 257/546] 16007: give advice how to use constants --- src/sage/calculus/desolvers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/calculus/desolvers.py b/src/sage/calculus/desolvers.py index a78a0a7e787..75a92e98daf 100644 --- a/src/sage/calculus/desolvers.py +++ b/src/sage/calculus/desolvers.py @@ -8,7 +8,9 @@ Solutions from the Maxima package can contain the three constants ``_C``, ``_K1``, and ``_K2`` where the underscore is used to distinguish -them from symbolic variables that the user might have used. +them from symbolic variables that the user might have used. You can +substitute values for them, and make them into accessible usable +symbolic variables, for example with ``var("_C")``. Commands: From 1a26f595814e3b7ef5b01c350858d78fd3dcf56b Mon Sep 17 00:00:00 2001 From: Jayant Date: Sat, 14 Jun 2014 15:30:02 -0400 Subject: [PATCH 258/546] Fixed some spelling mistakes in documentation. --- src/sage/matroids/matroids_plot_helpers.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/matroids/matroids_plot_helpers.py b/src/sage/matroids/matroids_plot_helpers.py index f1872fb2a82..f30d0c3eb7e 100644 --- a/src/sage/matroids/matroids_plot_helpers.py +++ b/src/sage/matroids/matroids_plot_helpers.py @@ -18,20 +18,20 @@ gives aesthetically pleasing point placement (in some sense. This is not yet implemented). One can then use ``createline`` function to produce sequence of ``100`` points on a smooth curve containing the points in the specified line which inturn uses ``scipy.interpolate.splprep``. - and ``scipy.interpolate.splprep`` Then one an use sage's - graphics primitives ``line``,``point``,``text`` and``points`` to produce graphics - object containg points (ground set elements) and lines (for a rank 3 matroid, these - are flats of rank 2 of size greater than equal to 3) of the geometric representation - of the matroid. Loops and parallel elements are added as per conventions in [Oxley] - using function ``addlp``. The priority order for point placement methods used inside - plot() and show() is as follows: + and ``scipy.interpolate.splev``. Then one can use sage's graphics primitives ``line``,``point``, + ``text`` and``points`` to produce graphics object containg points (ground set elements) and + lines (for a rank 3 matroid, these are flats of rank 2 of size greater than equal to 3) of + the geometric representation of the matroid. Loops and parallel elements are added as per + conventions in [Oxley] using function ``addlp``. The priority order for point placement methods + used inside plot() and show() is as follows: 1) User Specified points dictionary and lineorders - 2) cached point placement dictionary and line orders (alist of ordered lists) in + 2) cached point placement dictionary and line orders (a list of ordered lists) in M._cached_info (a dictionary) 3) Internal point placement and orders deciding heuristics If a custom point placement and/or line orders is desired, then user can simply specify the custom points dictionary as - ``M.cached info = {'positions':dictionary_of _points``,'lineorders') + ``M.cached info = {'positions':,'lineorders':)`` + REFERENCES @@ -318,7 +318,7 @@ def createline(ptsdict, ll, lineorders2=None): def slp(M1, pos_dict=None): """ - Return simple matroid, loops and parallel elements of given matroid + Return simple matroid, loops and parallel elements of given matroid. INPUT: From bf2bdebab30fe3dfb04376d77ec60203250bd5fa Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 16 Jun 2014 17:16:36 +0100 Subject: [PATCH 259/546] Have new implementation, going to get rid of redundant code --- src/sage/game_theory/cooperative_game.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index bf761e63737..d76050de33f 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -25,6 +25,7 @@ #***************************************************************************** from itertools import permutations, combinations from sage.misc.misc import powerset +from sage.rings.arith import factorial from sage.structure.sage_object import SageObject @@ -152,7 +153,7 @@ class CooperativeGame(SageObject): payoff vector that satisfies these and 1 other property not implemented here (additivity). They are: - + * Efficiency - `sum_{i=1}^N\lambda_i=v(\Omega)` In other words, no value of the total coalition is lost. @@ -351,9 +352,14 @@ def shapley_value(self): """ payoff_vector = {} for player in self.player_list: - player_contribution = self._marginal_contributions(player) - average = sum(player_contribution) / len(player_contribution) - payoff_vector[player] = average + r = 0 + for coalition in [coalition for coalition in powerset(self.player_list) if len(coalition) != 0]: + r += factorial(len(coalition) - 1) * factorial(len(self.player_list) - len(coalition)) / factorial(len(self.player_list)) * (self.ch_f[tuple(coalition)] - self.ch_f[tuple([p for p in coalition if p != player])]) + payoff_vector[player] = r + +# player_contribution = self._marginal_contributions(player) +# average = sum(player_contribution) / len(player_contribution) +# payoff_vector[player] = average self.payoff_vector = payoff_vector return payoff_vector From a11d1a3a853c2c9e40e90a6ce777de6eccf6694a Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 16 Jun 2014 17:19:53 +0100 Subject: [PATCH 260/546] Have removed now redundant functions --- src/sage/game_theory/cooperative_game.py | 95 ------------------------ 1 file changed, 95 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index d76050de33f..18a5a6a4469 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -357,9 +357,6 @@ def shapley_value(self): r += factorial(len(coalition) - 1) * factorial(len(self.player_list) - len(coalition)) / factorial(len(self.player_list)) * (self.ch_f[tuple(coalition)] - self.ch_f[tuple([p for p in coalition if p != player])]) payoff_vector[player] = r -# player_contribution = self._marginal_contributions(player) -# average = sum(player_contribution) / len(player_contribution) -# payoff_vector[player] = average self.payoff_vector = payoff_vector return payoff_vector @@ -513,98 +510,6 @@ def is_superadditive(self): return False return True - def _marginal_contributions(self, player): - r""" - Returns a list of contributions specific to one player. - This is a hidden function used in calculation of Shapley value. - - INPUT: - - - ``player`` - A real number or string. - - EXAMPLES:: - - sage: integer_function = {(): 0, - ....: (1,): 6, - ....: (2,): 12, - ....: (3,): 42, - ....: (1, 2,): 12, - ....: (1, 3,): 42, - ....: (2, 3,): 42, - ....: (1, 2, 3,): 42} - sage: integer_game = CooperativeGame(integer_function) - sage: integer_game._marginal_contributions(1) - [6, 6, 0, 0, 0, 0] - """ - return [self._marginal_of_pi(player, pi) for pi - in permutations(self.player_list)] - - def _marginal_of_pi(self, player, pi): - r""" - Returns a value for the contribution of a player in one permutation. - This is a hidden function used in calculation of Shapley value. - - INPUT: - - - player - A real number or string. - - - pi - A tuple which is the permutation that should be used. - - EXAMPLES:: - - sage: integer_function = {(): 0, - ....: (1,): 6, - ....: (2,): 12, - ....: (3,): 42, - ....: (1, 2,): 12, - ....: (1, 3,): 42, - ....: (2, 3,): 42, - ....: (1, 2, 3,): 42} - sage: integer_game = CooperativeGame(integer_function) - sage: integer_game._marginal_of_pi(2, (2, 3, 1)) - 12 - """ - predecessors, player_and_pred = self._get_predecessors(player, pi) - if predecessors is None: - predecessors = () - else: - predecessors = tuple(predecessors) - player_and_pred = tuple(player_and_pred) - value = self.ch_f[player_and_pred] - self.ch_f[predecessors] - return value - - def _get_predecessors(self, player, pi): - r""" - Returns a list of all the predecessors of a player in a certain - permutation and the same list including the original player. - This is a hidden function used in calculation of Shapley value. - - INPUT: - - - ``player`` - A real number or string. - - - ``pi`` - A tuple which is the permutation that should be used. - - EXAMPLES:: - - sage: integer_function = {(): 0, - ....: (1,): 6, - ....: (2,): 12, - ....: (3,): 42, - ....: (1, 2,): 12, - ....: (1, 3,): 42, - ....: (2, 3,): 42, - ....: (1, 2, 3,): 42} - sage: integer_game = CooperativeGame(integer_function) - sage: integer_game._get_predecessors(1, (2, 3, 1)) - ([2, 3], [1, 2, 3]) - sage: integer_game._get_predecessors(2, (2, 3, 1)) - ([], [2]) - sage: integer_game._get_predecessors(3, (2, 3, 1)) - ([2], [2, 3]) - """ - pred = list(pi[:pi.index(player)]) - return sorted(pred), sorted(pred + [player]) def _repr_(self): r""" From 8f97f9c5d65c3e412585f30d3222b4297569fe1c Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 16 Jun 2014 17:27:13 +0100 Subject: [PATCH 261/546] Tidied slightly --- src/sage/game_theory/cooperative_game.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 18a5a6a4469..9ae9b11ac94 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -352,10 +352,16 @@ def shapley_value(self): """ payoff_vector = {} for player in self.player_list: - r = 0 + weighted_contribution = 0 for coalition in [coalition for coalition in powerset(self.player_list) if len(coalition) != 0]: - r += factorial(len(coalition) - 1) * factorial(len(self.player_list) - len(coalition)) / factorial(len(self.player_list)) * (self.ch_f[tuple(coalition)] - self.ch_f[tuple([p for p in coalition if p != player])]) - payoff_vector[player] = r + weight = factorial(len(coalition) - 1) + weight *= factorial(len(self.player_list) - len(coalition)) + weight /= factorial(len(self.player_list)) + contribution = (self.ch_f[tuple(coalition)] - self.ch_f[tuple([p for p + in coalition + if p != player])]) + weighted_contribution += weight * contribution + payoff_vector[player] = weighted_contribution self.payoff_vector = payoff_vector return payoff_vector From 5d5066658a6e846f68fff8670a570cb65ef26ad4 Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 16 Jun 2014 17:49:28 +0100 Subject: [PATCH 262/546] Updated with new formulation --- src/sage/game_theory/cooperative_game.py | 25 +++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 9ae9b11ac94..9996ae8ae83 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -124,12 +124,19 @@ class CooperativeGame(SageObject): $\Delta_\pi^G(i)=v(S_{\pi}(i)\cup i)-v(S_{\pi}(i))$ + Note that an equivalent formula for the Shapley value is given by: + + $\phi_i(G)=\sum_{s\subseteq\Omega}\sum_{p\in S}\frac{(|s|-1)!(N-|S|)!}{N!}v(s)-v(s\setminus \{i\})$ + + This later formulation is implemented in Sage and + requires $2^N-1$ calculations instead of $N!$. + To compute the Shapley value in Sage is simple. :: sage: letter_game.shapley_value() {'A': 2, 'C': 35, 'B': 5} - The following example implements a (trivial) 8 player characteristic + The following example implements a (trivial) 10 player characteristic function game: $v(c)=|c|\text{ for all }c\in 2^{\Omega}$ @@ -139,14 +146,14 @@ class CooperativeGame(SageObject): sage: def simple_characteristic_function(N): ....: return {tuple(coalition) : len(coalition) ....: for coalition in subsets(range(N))} - sage: g = CooperativeGame(simple_characteristic_function(8)) - sage: g.shapley_value() # long time - {0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1} + sage: g = CooperativeGame(simple_characteristic_function(10)) + sage: g.shapley_value() + {0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 1, 9: 1} - The above is slow to run and this is due to the dimensionality - of the calculations involved. There are various approximation - approaches to obtaining the Shapley value of a game. Implementing - these would be a worthwhile development. + For very large game it might be worth taking advantage of the particular + problem structure to calculate the Shapley value and there are also + various approximation approaches to obtaining the Shapley value of a game. + Implementing these would be a worthwhile development. We can test 3 basic properties of any Payoff Vector $\lambda$. The Shapley value (described above) is none to be the unique @@ -360,7 +367,7 @@ def shapley_value(self): contribution = (self.ch_f[tuple(coalition)] - self.ch_f[tuple([p for p in coalition if p != player])]) - weighted_contribution += weight * contribution + weighted_contribution += weight * contribution payoff_vector[player] = weighted_contribution self.payoff_vector = payoff_vector From 9d0cc2370d1a2e32d29c5e6bbf1ab878549c1043 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Mon, 16 Jun 2014 20:22:30 +0100 Subject: [PATCH 263/546] removes redundant lines --- src/sage/game_theory/cooperative_game.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 9996ae8ae83..f24949d8b33 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -1,4 +1,4 @@ -r""" +""" Co-operative games with N players. This module implements a **basic** implementation of a characteristic function @@ -161,7 +161,7 @@ class CooperativeGame(SageObject): not implemented here (additivity). They are: - * Efficiency - `sum_{i=1}^N\lambda_i=v(\Omega)` + * Efficiency - `\sum_{i=1}^N\lambda_i=v(\Omega)` In other words, no value of the total coalition is lost. * The nullplayer property - If `\exists` `i` such that @@ -332,8 +332,6 @@ def shapley_value(self): (1, 2, 3) sage: integer_game.shapley_value() {1: 2, 2: 5, 3: 35} - sage: integer_game.payoff_vector - {1: 2, 2: 5, 3: 35} A longer example of the shapley_value. :: @@ -365,12 +363,10 @@ def shapley_value(self): weight *= factorial(len(self.player_list) - len(coalition)) weight /= factorial(len(self.player_list)) contribution = (self.ch_f[tuple(coalition)] - self.ch_f[tuple([p for p - in coalition - if p != player])]) + in coalition if p != player])]) weighted_contribution += weight * contribution payoff_vector[player] = weighted_contribution - self.payoff_vector = payoff_vector return payoff_vector def is_monotone(self): @@ -523,7 +519,6 @@ def is_superadditive(self): return False return True - def _repr_(self): r""" Returns a concise description of the Game. From 8a41cc83d825f508d12d1dc3b1b6a28c0bd51005 Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Mon, 16 Jun 2014 23:38:59 +0200 Subject: [PATCH 264/546] Expose bug #16485 in doctest. At the moment this bug causes an exception from sage/rings/polynomial/multi_polynomial_libsingular.pyx: TypeError: keys do not match self's parent --- src/sage/rings/polynomial/multi_polynomial_ideal.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 536dd8520d2..9e0be883400 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -2593,6 +2593,13 @@ def variety(self, ring=None): sage: I.variety() [] + Check that the issue at :trac:`16485` is fixed:: + + sage: R. = PolynomialRing(QQ, order='lex') + sage: I = R.ideal(c^2-2, b-c, a) + sage: I.variety(QQbar) + [...a: 0...] + ALGORITHM: Uses triangular decomposition. From d3059da97dd52c5c26eec08ba5cf74e927e1293e Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Mon, 16 Jun 2014 23:45:42 +0200 Subject: [PATCH 265/546] Fix type mismatch in variety computation. Switch all polynomials of triangular decomposition to target ring before enumerating the variety. This avoids mismatchs between rings. --- src/sage/rings/polynomial/multi_polynomial_ideal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 9e0be883400..3c5acfa2438 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -2653,7 +2653,7 @@ def _variety(T, V, v=None): V = [] for t in T: - Vbar = _variety(list(t),[]) + Vbar = _variety([P(f) for f in t], []) #Vbar = _variety(list(t.gens()),[]) for v in Vbar: From 96fefd52852a8559f9af0942fe32312ad6609c9d Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Mon, 16 Jun 2014 23:49:06 +0200 Subject: [PATCH 266/546] Drive-by improvement: Omit multiplicities. Instead of ignoring the multiplicities during iteration, it might be cleaner to not ask for them in the first place. --- src/sage/rings/polynomial/multi_polynomial_ideal.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 3c5acfa2438..330cc23a6a1 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -2620,9 +2620,9 @@ def _variety(T, V, v=None): return V variable = f.variable(0) - roots = f.univariate_polynomial().roots(ring=ring) + roots = f.univariate_polynomial().roots(ring=ring, multiplicities=False) - for root,_ in roots: + for root in roots: vbar = v.copy() vbar[variable] = root Tbar = [ f.subs({variable:root}) for f in T ] From 95ac8b6d68b7e291983e4df137d35c601110eb27 Mon Sep 17 00:00:00 2001 From: Jayant Date: Mon, 16 Jun 2014 21:02:33 -0400 Subject: [PATCH 267/546] Added x-y limits tracking. Added _fix_positions() method to abstract matroids class. --- src/sage/matroids/matroid.pxd | 3 +- src/sage/matroids/matroid.pyx | 38 ++++++-- src/sage/matroids/matroids_plot_helpers.py | 103 ++++++++++++++++----- 3 files changed, 115 insertions(+), 29 deletions(-) diff --git a/src/sage/matroids/matroid.pxd b/src/sage/matroids/matroid.pxd index 8178f67af8b..cbebec524f8 100644 --- a/src/sage/matroids/matroid.pxd +++ b/src/sage/matroids/matroid.pxd @@ -146,4 +146,5 @@ cdef class Matroid(SageObject): # visualization cpdef plot(self,B=*,lineorders=*,pos_method=*,pos_dict=*,save_pos=*) - cpdef show(self,B=*,lineorders=*,pos_method=*,pos_dict=*,save_pos=*) + cpdef show(self,B=*,lineorders=*,pos_method=*,pos_dict=*,save_pos=*,lims=*) + cpdef _fix_positions(self,pos_dict=*,lineorders=*) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index 0ca7f8529f9..50e14fdb7aa 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -4804,12 +4804,12 @@ cdef class Matroid(SageObject): return elif B == None: B = list(self.basis()) - elif self.is_basis(B)==False: + elif B != None and self.is_basis(B)==False: return lineorders2=matroids_plot_helpers.lineorders_union(self._cached_info['lineorders'],lineorders) return matroids_plot_helpers.geomrep(self,B,lineorders2,pd=pos_dict, sp=save_pos) - cpdef show(self,B=None,lineorders=None,pos_method=None,pos_dict=None,save_pos=False): + cpdef show(self,B=None,lineorders=None,pos_method=None,pos_dict=None,save_pos=False,lims=None): """ Show the geometric representation of the matroid. @@ -4824,23 +4824,25 @@ cdef class Matroid(SageObject): ``0``: default positioning ``1``: use pos_dict if it is not ``None`` ``2``: Force directed (Not yet implemented). - - ``pos_dict``: A dictionary mapping ground set elements to their (x,y) positions. - - ``save_pos``: A boolean indicating that point placements (either internal or user provided) and + - ``pos_dict`` -- A dictionary mapping ground set elements to their (x,y) positions. + - ``save_pos`` -- A boolean indicating that point placements (either internal or user provided) and line orders (if provided) will be cached in the matroid (``M._cached_info``) and can be used for reproducing the geometric representation during the same session + - ``lims`` -- A list of 4 elements ``[xmin,xmax,ymin,ymax]`` EXAMPLES:: sage: M=matroids.named_matroids.TernaryDowling3() sage: M.show(B=['a','b','c']) - sage: M.show(B=['a','b','c'],lineorders=[['f','e','i']]) + sage: M.show(B=['a','b','c'],lineorders=[['f','e','i']]) + sage: M.show(pos_method=1, pos_dict=pos,lims=[-3,3,-3,3]) """ if self.rank() > 3: return elif B == None: B = list(self.basis()) - elif self.is_basis(B)==False: + elif B != None and self.is_basis(B)==False: return B1=B lineorders1=lineorders @@ -4848,5 +4850,27 @@ cdef class Matroid(SageObject): pd=pos_dict sp=save_pos G=self.plot(B1,lineorders1,pm,pd,sp) - G.show(xmin=-2, xmax=3, ymin=-2, ymax=3) + if lims == None: + G.show() + else: + G.show(xmin=lims[0], xmax=lims[1], ymin=lims[2], ymax=lims[3]) return + + cpdef _fix_positions(self,pos_dict=None,lineorders=None): + """ + Cache point positions and line orders without actually plotting + + INPUT: + + - ``pos_dict`` -- (optional) A dictionary mapping ground set elements to their (x,y) positions. + - ``lineorders`` -- (optional) A list of lists where each of the inner lists + specify ground set elements in a certain order which will be used to draw the + corresponding line in geometric representation (if it exists). + """ + # check sanity of pos_dict and add it to cached info if sane + if(pos_dict!=None): + import matroids_plot_helpers + if matroids_plot_helpers.posdict_is_sane(self,pos_dict) ==True: + self._cached_info={'positions':pos_dict,'lineorders':lineorders} + return + diff --git a/src/sage/matroids/matroids_plot_helpers.py b/src/sage/matroids/matroids_plot_helpers.py index f30d0c3eb7e..80563940b2f 100644 --- a/src/sage/matroids/matroids_plot_helpers.py +++ b/src/sage/matroids/matroids_plot_helpers.py @@ -30,7 +30,7 @@ 3) Internal point placement and orders deciding heuristics If a custom point placement and/or line orders is desired, then user can simply specify the custom points dictionary as - ``M.cached info = {'positions':,'lineorders':)`` + ``M.cached info = {'positions':,'lineorders':}`` @@ -316,7 +316,7 @@ def createline(ptsdict, ll, lineorders2=None): x_i,y_i = scipy.interpolate.splev(np.linspace(0,1,100),tck) return sortedx,sortedy,x_i,y_i -def slp(M1, pos_dict=None): +def slp(M1, pos_dict=None,B=None): """ Return simple matroid, loops and parallel elements of given matroid. @@ -326,6 +326,7 @@ def slp(M1, pos_dict=None): - ``pos_dict`` -- (optional) A dictionary containing non loopy elements of ``M`` as keys and their (x,y) positions as keys. While simplifying the matroid, all except one element in a parallel class that is also specified in ``pos_dict`` will be retained. + - ``B`` -- (optional) A basis of M1 that has been chosen for placement on vertics of triangle OUTPUT: @@ -361,17 +362,30 @@ def slp(M1, pos_dict=None): sg = sorted(M1.simplify().groundset()) nP = L|set(M1.simplify().groundset()) P = set(M1.groundset())-nP - if pos_dict != None: - pcls=list(set([frozenset(set(M1.closure([p]))-L) for p in list(P)])) - newP=[] - for pcl in pcls: - pcl_in_dict=[p for p in list(pcl) if p in pos_dict.keys()] - newP.extend(list(pcl-set([pcl_in_dict[0]]))) - return [M1.delete(L|set(newP)),L,set(newP)] + if len(P) > 0: + if pos_dict != None: + pcls=list(set([frozenset(set(M1.closure([p]))-L) for p in list(P)])) + newP=[] + for pcl in pcls: + pcl_in_dict=[p for p in list(pcl) if p in pos_dict.keys()] + newP.extend(list(pcl-set([pcl_in_dict[0]]))) + return [M1.delete(L|set(newP)),L,set(newP)] + elif B != None: + pcls=list(set([frozenset(set(M1.closure([p]))-L) for p in list(P)])) + newP=[] + for pcl in pcls: + pcl_in_basis=[p for p in list(pcl) if p in B] + if len(pcl_in_basis)>0: + newP.extend(list(pcl-set([pcl_in_basis[0]]))) + else: + newP.extend(list(pcl-set([pcl[0]]))) + return [M1.delete(L|set(newP)),L,set(newP)] + else: + return [M1.delete(L|P),L,P] else: return [M1.delete(L|P),L,P] -def addlp(M, M1, L, P, ptsdict, G=None): +def addlp(M, M1, L, P, ptsdict, G=None,limits=None): """ Return a graphics object containing loops (in inset) and parallel elements of matroid. @@ -382,9 +396,13 @@ def addlp(M, M1, L, P, ptsdict, G=None): - ``P`` -- List of elements in ``M.groundset()`` not in ``M.simplify.groundset()`` or ``L``. - ``ptsdict`` -- A dictionary containing elements in ``M.groundset()`` not necessarily containing elements of ``L``. - ``G`` -- (optional) A sage graphics object to which loops and parallel elements of matroid `M` added . + - ``limits``-- (optional) Current axes limits [xmin,xmax,ymin,ymax]. OUTPUT: - A sage graphics object containing loops and parallel elements of matroid ``M`` + A 2-tuple containing: + + 1. A sage graphics object containing loops and parallel elements of matroid ``M`` + 2. axes limits array EXAMPLES: @@ -407,14 +425,19 @@ def addlp(M, M1, L, P, ptsdict, G=None): if len(L) > 0: loops = L looptext = ", ".join([str(l) for l in loops]) - rectx = -1 - recty = -1 + if(limits==None): + rectx = -1 + recty = -1 + else: + rectx = limits[0] + recty = limits[2]-1 rectw = 0.5 + 0.4*len(loops) + 0.5 # control this based on len(loops) recth = 0.6 G += polygon2d([[rectx,recty], [rectx,recty+recth], [rectx+rectw,recty+recth], [rectx+rectw,recty]], color='black',fill=False,thickness=4) G += text(looptext,(rectx+0.5,recty+0.3),color='black',fontsize=13) G += point((rectx+0.2, recty+0.3),color='black', size=300,zorder=2) G += text('Loop(s)',(rectx+0.5+0.4*len(loops)+0.1,recty+0.3),fontsize=13,color='black') + limits=tracklims(limits,[rectx,rectx+rectw],[recty,recty+recth]) if len(P) > 0: # create list of lists where inner lists are parallel classes pcls = [] @@ -433,10 +456,12 @@ def addlp(M, M1, L, P, ptsdict, G=None): ptsdict[pcl[1]] = (basept[0],basept[1]-0.13) G += points(zip([basept[0]], [basept[1]-0.13]),color='black', size=300,zorder=2) G += text(pcl[1],(float(basept[0]), float(basept[1])-0.13),color='white',fontsize=13) + limits=tracklims(limits,[basept[0]],[basept[1]-0.13]) else: # add in a bracket G += text('{ '+", ".join(sorted([str(kk) for kk in pcl[1:]]))+' }',(float(basept[0]), float(basept[1]-0.2)-0.034),color='black',fontsize=13) - return G + limits=tracklims(limits,[basept[0]],[(basept[1]-0.2)-0.034]) + return G,limits def line_hasorder(l, lodrs=None): """ @@ -554,9 +579,35 @@ def posdict_is_sane(M1, pos_dict): return False return True +def tracklims(lims,x_i=[],y_i=[]): + """ + Return modified limits list + + INPUT: + + - ``lims`` -- A list with 4 elements ``[xmin,xmax,ymin,ymax]`` + - ``x_i`` -- New x values to track + - ``y_i`` -- New y values to track + + OUTPUT: + + A list with 4 elements ``[xmin,xmax,ymin,ymax]`` + EXAMPLES:: + sage: from sage.matroids import matroids_plot_helpers + sage: matroids_plot_helpers.tracklims([0,5,-1,7],[1,2,3,6,-1],[-1,2,3,6]) + [-1, 6, -1, 7] + .. NOTE:: + + This method does NOT do any checks. + """ + if lims!=None and lims[0]!=None and lims[1]!=None and lims[2]!=None and lims[3]!=None: + lims=[min(min(x_i),lims[0]),max(max(x_i),lims[1]),min(min(y_i),lims[2]),max(max(y_i),lims[3])] + else: + lims=[min(x_i),max(x_i),min(y_i),max(y_i)] + return lims def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): """ @@ -593,12 +644,13 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): This method does NOT do any checks. """ - if B1 == None: - B1 = list(M1.basis()) G = Graphics() # create lists of loops and parallel elements and simplify given matroid - [M,L,P] = slp(M1,pos_dict=pd) + if B1 == None: + B1 = list(M.basis()) + [M,L,P] = slp(M1,pos_dict=pd,B=B1) M._cached_info = M1._cached_info + if M.rank()==1: if M._cached_info != None and 'positions' in M._cached_info.keys() and M._cached_info['positions'] != None: pts = M._cached_info['positions'] @@ -609,6 +661,9 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): pts[gnd[0]] = (1,float(2)/3) G += point((1, float(2)/3),size=300,zorder=2) pts2 = pts + # track limits [xmin,xmax,ymin,ymax] + pl=[list(x) for x in pts2.values()] + lims=tracklims([None,None,None,None],[pt[0] for pt in pl],[pt[1] for pt in pl]) elif M.rank() == 2: nB1=list(set(list(M.groundset())) - set(B1)) bline = [] @@ -631,9 +686,12 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): pts2[bline[k]] = (cc*lpt[0]+(1-cc)*rpt[0],cc*lpt[1]+(1-cc)*rpt[1]) if sp == True: M._cached_info['positions']=pts2 - + # track limits [xmin,xmax,ymin,ymax] + pl=[list(x) for x in pts2.values()] + lims=tracklims([None,None,None,None],[pt[0] for pt in pl],[pt[1] for pt in pl]) bline.extend(B1) ptsx,ptsy,x_i,y_i = createline(pts2,bline,lineorders1) + lims=tracklims(lims,x_i,y_i) G += line(zip(x_i, y_i),color='black',thickness=3,zorder=1) allpts = [list(pts2[i]) for i in M.groundset()] xpts = [float(k[0]) for k in allpts] @@ -650,11 +708,13 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): else: pts2=M._cached_info['positions'] trilines=[list(set(list(x)).difference(L|P)) for x in M1.flats(2) if len(list(x))>=3] - + pl=[list(x) for x in pts2.values()] + lims=tracklims([None,None,None,None],[pt[0] for pt in pl],[pt[1] for pt in pl]) j = 0 for ll in trilines: if len(ll)>=3: ptsx,ptsy,x_i,y_i=createline(pts2,ll,lineorders1) + lims=tracklims(lims,x_i,y_i) G += line(zip(x_i,y_i),color='black',thickness=3,zorder=1) allpts = [list(pts2[i]) for i in M.groundset()] xpts = [float(k[0]) for k in allpts] @@ -666,7 +726,8 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): if sp == True: M1._cached_info['positions']=pts2 M1._cached_info['lineorders']=lineorders1 - #deal with loops and parallel elements - G = addlp(M1,M,L,P,pts2,G) + #deal with loops and parallel elements + G,lims = addlp(M1,M,L,P,pts2,G,lims) G.axes(False) + G.axes_range(xmin=lims[0]-0.5,xmax=lims[1]+0.5,ymin=lims[2]-0.5,ymax=lims[3]+0.5) return G From 3d92382b4f7e2a6131503f77bfee41c5a6107c69 Mon Sep 17 00:00:00 2001 From: Jayant Date: Mon, 16 Jun 2014 21:10:15 -0400 Subject: [PATCH 268/546] Added x-y limits tracking. Added _fix_positions() method to abstract matroids class. --- src/sage/matroids/matroid.pyx | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index 50e14fdb7aa..0db0a3974f2 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -4866,6 +4866,35 @@ cdef class Matroid(SageObject): - ``lineorders`` -- (optional) A list of lists where each of the inner lists specify ground set elements in a certain order which will be used to draw the corresponding line in geometric representation (if it exists). + + EXAMPLES:: + + sage: M=matroids.named_matroids.BetsyRoss() + sage: pos={} + sage: s="abcde" + sage: t="fghij" + sage: x=1.61 + sage: y=1/1.61 + sage: for i in range(5): + ....: pos[s[i]]=(RR(x*sin(2*pi*i/5)), RR(x*cos(2*pi*i/5))) + ....: pos[t[i]]=(RR(y*sin(2*pi*(i+1/2)/5)), RR(y*cos(2*pi*(i+1/2)/5))) + ....: + sage: pos['k']=(0,0) + sage: M._fix_positions(pos_dict=pos) + sage: M._cached_info + {'lineorders': None, + 'positions': {'a': (0.000000000000000, 1.61000000000000), + 'b': (1.53120099123520, 0.497517360943665), + 'c': (0.946334256190882, -1.30251736094367), + 'd': (-0.946334256190882, -1.30251736094367), + 'e': (-1.53120099123520, 0.497517360943665), + 'f': (0.365084007635076, 0.502495027562079), + 'g': (0.590718333102580, -0.191936021350899), + 'h': (0.000000000000000, -0.621118012422360), + 'i': (-0.590718333102580, -0.191936021350899), + 'j': (-0.365084007635077, 0.502495027562079), + 'k': (0, 0)}} + """ # check sanity of pos_dict and add it to cached info if sane if(pos_dict!=None): From f2e59340dabe0d7baa73e08a107acaa58494948f Mon Sep 17 00:00:00 2001 From: Jayant Date: Tue, 17 Jun 2014 01:41:26 -0400 Subject: [PATCH 269/546] Added NotImplementedError for rank > 3 matroids in show() method of the abstract matroids class. --- src/sage/matroids/matroid.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index 0db0a3974f2..e7dc0a98e34 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -4839,7 +4839,7 @@ cdef class Matroid(SageObject): """ if self.rank() > 3: - return + raise NotImplementedError elif B == None: B = list(self.basis()) elif B != None and self.is_basis(B)==False: From a01095ea161f0adae68692c67174771232c80675 Mon Sep 17 00:00:00 2001 From: Punarbasu Purkayastha Date: Tue, 17 Jun 2014 20:55:36 +0800 Subject: [PATCH 270/546] fix latex according to reviewer comments --- src/sage/plot/plot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index 8484cfb00d4..9d89915005e 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -758,7 +758,7 @@ def plot(funcs, *args, **kwds): - this function does NOT simply sample equally spaced points between xmin and xmax. Instead it computes equally spaced points and adds small perturbations to them. This reduces the possibility - of, e.g., sampling sin only at multiples of `2\pi`, which would + of, e.g., sampling `\sin` only at multiples of `2\pi`, which would yield a very misleading graph. - if there is a range of consecutive points where the function has @@ -768,7 +768,7 @@ def plot(funcs, *args, **kwds): EXAMPLES: - We plot the sin function:: + We plot the `\sin` function:: sage: P = plot(sin, (0,10)); print P Graphics object consisting of 1 graphics primitive From 2c68026320ba3c516cd94fa1721e886ecd732804 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Tue, 17 Jun 2014 17:37:03 +0200 Subject: [PATCH 271/546] 8734: adapt doctest to recent change of output --- src/sage/tests/french_book/recequadiff.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/tests/french_book/recequadiff.py b/src/sage/tests/french_book/recequadiff.py index 76fd47fbb89..692f1fc012e 100755 --- a/src/sage/tests/french_book/recequadiff.py +++ b/src/sage/tests/french_book/recequadiff.py @@ -247,7 +247,7 @@ sage: desolve(eq1,[f,x]) Traceback (most recent call last): ... - TypeError: ECL says: Maxima asks: + TypeError: Computation failed ... Is k positive, negative or zero? Sage example in ./recequadiff.tex, line 728:: From c5b2f5d92936577e517f3a871e8f673727f33b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 17 Jun 2014 21:41:56 +0200 Subject: [PATCH 272/546] trac #11980 minor doc changes + a little code formatting and pyflakes check --- .../hyperelliptic_finite_field.py | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py index 51b20921e71..98a295d1797 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py @@ -415,7 +415,7 @@ def frobenius_polynomial(self): sage: H.frobenius_polynomial() x^6 - 14*x^5 + 1512*x^4 - 66290*x^3 + 3028536*x^2 - 56168126*x + 8036054027 - Curves defined over a non-prime field are only supported as well, + Curves defined over a non-prime field are supported as well, but a naive algorithm is used; especially when ``g = 1``, fast point counting on elliptic curves should be used:: @@ -542,7 +542,6 @@ def _points_fast_sqrt(self): for x in K: points.append(self.point([x, f(x).sqrt(), one], check=True)) else: - R = f.base_ring() a_sqrts = { } # Artin-Schreier 2-roots for x in K: a_sqrts[x**2 + x] = x # char 2 => x^2 - x == x^2 + x @@ -816,13 +815,12 @@ def count_points_frobenius_polynomial(self, n=1, f=None): if f is None: f = self.frobenius_polynomial() - g = self.genus() q = self.base_ring().cardinality() S = PowerSeriesRing(QQ, default_prec=n+1, names='t') frev = f.reverse() - # the coefficients() method of power series only returns non-zero coefficients - # so let's use the list() method - # but this does not work for zero which gives the empty list + # the coefficients() method of power series only returns + # non-zero coefficients so let us use the list() method but + # this does not work for zero which gives the empty list flog = S(frev).log() return [q**(i+1) + 1 + ZZ((i+1)*flog[i+1]) for i in xrange(n)] @@ -1249,9 +1247,7 @@ def zeta_function(self): sage: H.zeta_function() (1369*x^4 + 37*x^3 - 52*x^2 + x + 1)/(37*x^2 - 38*x + 1) - A quadratic twist: - - :: + A quadratic twist:: sage: R. = PolynomialRing(GF(37)) sage: H = HyperellipticCurve(2*t^5 + 2*t + 4) @@ -1555,29 +1551,29 @@ def _Hasse_Witt_cached(self): #Since Trac Ticket #11115, there is a different cache for methods #that don't accept arguments. Anyway, the easiest is to call #the cached method and simply see whether the data belong to self. - M, Coeffs,g, Fq, p, E= self._Cartier_matrix_cached() - if E!=self: + M, Coeffs, g, Fq, p, E = self._Cartier_matrix_cached() + if E != self: self._Cartier_matrix_cached.clear_cache() - M, Coeffs,g, Fq, p, E= self._Cartier_matrix_cached() + M, Coeffs, g, Fq, p, E = self._Cartier_matrix_cached() #This compute the action of p^kth Frobenius on list of coefficients def frob_mat(Coeffs, k): - a = p**k + a = p ** k mat = [] - Coeffs_pow = [c**a for c in Coeffs] - for i in range(1,g+1): - H=[(Coeffs[j]) for j in range((p*i-1), (p*i - g-1), -1)] - mat.append(H); - return matrix(Fq,mat) + Coeffs_pow = [c ** a for c in Coeffs] # not used ?? + for i in range(1, g + 1): + H = [(Coeffs[j]) for j in range((p*i-1), (p*i - g-1), -1)] + mat.append(H) + return matrix(Fq, mat) #Computes all the different possible action of frobenius on matrix M and stores in list Mall - Mall = [M] + [frob_mat(Coeffs,k) for k in range(1,g)] + Mall = [M] + [frob_mat(Coeffs, k) for k in range(1, g)] #initial N=I, so we can go through Mall and multiply all matrices with I and #get the Hasse-Witt matrix. - N = identity_matrix(Fq,g) + N = identity_matrix(Fq, g) for l in Mall: - N = N*l; + N = N * l return N, E #This is the function which is actually called by command line From 5c7fb75e184b2e034d53acf1807c58e2165ac7d0 Mon Sep 17 00:00:00 2001 From: Punarbasu Purkayastha Date: Wed, 18 Jun 2014 14:39:01 +0800 Subject: [PATCH 273/546] do not use exclude both as list and as None --- src/sage/plot/plot.py | 60 +++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index 9d89915005e..5ec17a4a1b9 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -1269,6 +1269,7 @@ def _plot(funcs, xrange, parametric=False, from sage.plot.misc import setup_for_eval_on_grid if funcs == []: return Graphics() + excluded_points = [] funcs, ranges = setup_for_eval_on_grid(funcs, [xrange], options['plot_points']) xmin, xmax, delta = ranges[0] xrange=ranges[0][:2] @@ -1327,38 +1328,40 @@ def _plot(funcs, xrange, parametric=False, v = exclude.variables()[0] points = [e.right() for e in exclude.solve(v) if e.left() == v and (v not in e.right().variables())] # We are only interested in real solutions - exclude = [] for x in points: try: - exclude.append(float(x)) + excluded_points.append(float(x)) except TypeError: pass + excluded_points.sort() - if isinstance(exclude, (list, tuple)): - exclude = sorted(exclude) - # We make sure that points plot points close to the excluded points are computed - epsilon = 0.001*(xmax - xmin) - initial_points = reduce(lambda a,b: a+b, [[x - epsilon, x + epsilon] for x in exclude], []) - data = generate_plot_points(f, xrange, plot_points, adaptive_tolerance, adaptive_recursion, randomize, initial_points) + # We should either have a list in excluded points or exclude + # itself must be a list + elif isinstance(exclude, (list, tuple)): + excluded_points = sorted(exclude) else: raise ValueError('exclude needs to be a list of numbers or an equation') - if exclude == []: - exclude = None + # We make sure that points plot points close to the excluded points are computed + epsilon = 0.001*(xmax - xmin) + initial_points = reduce(lambda a,b: a+b, + [[x - epsilon, x + epsilon] + for x in excluded_points], []) + data = generate_plot_points(f, xrange, plot_points, + adaptive_tolerance, adaptive_recursion, + randomize, initial_points) else: - data = generate_plot_points(f, xrange, plot_points, adaptive_tolerance, adaptive_recursion, randomize) + data = generate_plot_points(f, xrange, plot_points, + adaptive_tolerance, adaptive_recursion, + randomize) - # Need exclude to be a list to be able to do automatic exclusion of - # plot regions. - if exclude is None: - exclude = [] for i in range(len(data)-1): # If the difference between consecutive x-values is more than # 2 times the difference between two consecutive plot points, then # add an exclusion point. if abs(data[i+1][0] - data[i][0]) > 2*abs(xmax - xmin)/plot_points: - exclude.append((data[i][0] + data[i+1][0])/2) + excluded_points.append((data[i][0] + data[i+1][0])/2) if parametric: # We need the original x-values to be able to exclude points in parametric plots @@ -1369,15 +1372,10 @@ def _plot(funcs, xrange, parametric=False, newdata.append((fdata, g(x))) except ValueError: newdata.append((fdata, 0)) # append a dummy value 0 - exclude.append(x) + excluded_points.append(x) data = newdata - # We set exclude back to None if there are no points to be excluded - if not exclude: - exclude = None - else: - exclude.sort() - + excluded_points.sort() G = Graphics() fillcolor = options.pop('fillcolor', 'automatic') @@ -1445,7 +1443,7 @@ def _plot(funcs, xrange, parametric=False, detect_poles = options.pop('detect_poles', False) legend_label = options.pop('legend_label', None) - if exclude is not None or detect_poles != False: + if excluded_points or detect_poles != False: start_index = 0 # setup for pole detection from sage.rings.all import RDF @@ -1457,13 +1455,15 @@ def _plot(funcs, xrange, parametric=False, # setup for exclusion points exclusion_point = 0 - if exclude is not None: - exclude.reverse() - exclusion_point = exclude.pop() + if excluded_points: + excluded_points.reverse() + exclusion_point = excluded_points.pop() + flag = True for i in range(len(data)-1): x0, y0 = exclude_data[i] x1, y1 = exclude_data[i+1] + # detect poles if (not (polar or parametric)) and detect_poles != False \ and ((y1 > 0 and y0 < 0) or (y1 < 0 and y0 > 0)): @@ -1479,15 +1479,15 @@ def _plot(funcs, xrange, parametric=False, start_index = i+2 # exclude points - if exclude is not None and (x0 <= exclusion_point <= x1): + if flag and (x0 <= exclusion_point <= x1): G += line(data[start_index:i], **options) start_index = i + 2 while exclusion_point <= x1: try: - exclusion_point = exclude.pop() + exclusion_point = excluded_points.pop() except IndexError: # all excluded points were considered - exclude = None + flag = False break G += line(data[start_index:], legend_label=legend_label, **options) From 2fba3777e463d0ea5d37ba7b436d4be5745c4b38 Mon Sep 17 00:00:00 2001 From: vince Date: Wed, 18 Jun 2014 09:21:57 +0100 Subject: [PATCH 274/546] Added reference to cooperative game for different calculation of shapley code --- src/sage/game_theory/cooperative_game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index f24949d8b33..17fa71433b1 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -114,7 +114,7 @@ class CooperativeGame(SageObject): sage: letter_game A 3 player Co-operative Game. - It can be shown that the 'fair' payoff vector, referred to as the + It can be shown (see: [this](http://www.math.ucla.edu/~tom/Game_Theory/coal.pdf)) that the 'fair' payoff vector, referred to as the Shapley value is given by the following formula: $\phi_i(G)=\frac{1}{N!}\sum_{\pi\in\Pi_n}\Delta_\pi^G(i)$ From fe2a0f0c4c536e2ad4f5fb940ce6ee7e3a1f0c22 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Wed, 18 Jun 2014 11:13:41 +0200 Subject: [PATCH 275/546] 16007: handle latexification of converted special maxima variable names --- src/sage/misc/latex.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py index ab83c4c4c04..59671abb54e 100644 --- a/src/sage/misc/latex.py +++ b/src/sage/misc/latex.py @@ -2679,6 +2679,11 @@ def latex_variable_name(x, is_fname=False): else: prefix = x[:underscore] suffix = x[underscore+1:] + if prefix == '': + from sage.calculus.calculus import symtable + for sym in symtable.values(): + if sym[0] == '_' and sym[1:] == suffix: + return latex_variable_name(suffix) if suffix and len(suffix) > 0: # handle the suffix specially because it very well might be numeric # I use strip to avoid using regex's -- It makes it a bit faster (and the code is more comprehensible to non-regex'ed people) From 4f20c9542bac14a6e4e193e5195c5e8b6142bc14 Mon Sep 17 00:00:00 2001 From: Jayant Date: Wed, 18 Jun 2014 07:03:00 -0400 Subject: [PATCH 276/546] pep8 compliance for matroids_plot_helpers.py. --- src/sage/matroids/matroids_plot_helpers.py | 870 ++++++++++++--------- 1 file changed, 497 insertions(+), 373 deletions(-) diff --git a/src/sage/matroids/matroids_plot_helpers.py b/src/sage/matroids/matroids_plot_helpers.py index 80563940b2f..9041c697c81 100644 --- a/src/sage/matroids/matroids_plot_helpers.py +++ b/src/sage/matroids/matroids_plot_helpers.py @@ -7,49 +7,55 @@ - Jayant Apte (2014-06-06): initial version .. NOTE:: - - This file provides functions that are called by ``show()`` and ``plot()`` - methods of abstract matroids class. The basic idea is to first decide the placement - of points in $\mathbb{R}^2$ and then draw lines in geometric representation through these points. - Point placement procedures such as ``addtripts``, ``addnontripts`` together - produce ``(x,y)`` tuples corresponding to ground set of the matroid in a dictionary. - These methods provide simple but rigid point placement algorithm. Alternatively, - one can build the point placement dictionary manually or via an optimization that - gives aesthetically pleasing point placement (in some sense. This is not yet implemented). - One can then use ``createline`` function to produce sequence of ``100`` points on a - smooth curve containing the points in the specified line which inturn uses ``scipy.interpolate.splprep``. - and ``scipy.interpolate.splev``. Then one can use sage's graphics primitives ``line``,``point``, - ``text`` and``points`` to produce graphics object containg points (ground set elements) and - lines (for a rank 3 matroid, these are flats of rank 2 of size greater than equal to 3) of - the geometric representation of the matroid. Loops and parallel elements are added as per - conventions in [Oxley] using function ``addlp``. The priority order for point placement methods - used inside plot() and show() is as follows: + + This file provides functions that are called by ``show()`` and ``plot()`` + methods of abstract matroids class. The basic idea is to first decide + the placement of points in $\mathbb{R}^2$ and then draw lines in geometric + representation through these points. Point placement procedures such as + ``addtripts``, ``addnontripts`` together produce ``(x,y)`` tuples + corresponding to ground set of the matroid in a dictionary. + These methods provide simple but rigid point placement algorithm. + Alternatively, one can build the point placement dictionary manually or + via an optimization that gives aesthetically pleasing point placement (in + some sense. This is not yet implemented). One can then use + ``createline`` function to produce sequence of ``100`` points on a smooth + curve containing the points in the specified line which inturn uses + ``scipy.interpolate.splprep`` and ``scipy.interpolate.splev``. Then one + can use sage's graphics primitives ``line``,``point``, ``text`` and + ``points`` to produce graphics object containg points (ground set + elements) and lines (for a rank 3 matroid, these are flats of rank 2 of + size greater than equal to 3) of the geometric representation of the + matroid. Loops and parallel elements are added as per conventions in + [Oxley] using function ``addlp``. The priority order for point placement + methods used inside plot() and show() is as follows: 1) User Specified points dictionary and lineorders - 2) cached point placement dictionary and line orders (a list of ordered lists) in - M._cached_info (a dictionary) + 2) cached point placement dictionary and line orders (a list of ordered + lists) in M._cached_info (a dictionary) 3) Internal point placement and orders deciding heuristics - If a custom point placement and/or line orders is desired, then user can simply specify - the custom points dictionary as - ``M.cached info = {'positions':,'lineorders':}`` + If a custom point placement and/or line orders is desired, then user can + simply specify the custom points dictionary as ``M.cached info = + {'positions':,'lineorders':}`` REFERENCES ========== -.. [Oxley] James Oxley, "Matroid Theory, Second Edition". Oxford University Press, 2011. +.. [Oxley] James Oxley, "Matroid Theory, Second Edition". Oxford University + Press, 2011. EXAMPLES:: sage: from sage.matroids import matroids_plot_helpers - sage: M1=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1,0,1],[0, 1, 0, 1, 0, 1, 1,0,0,1,0],[0, 0, 1, 1, 1, 0, 1,0,0,0,0]]) - sage: pos_dict= {0: (0, 0), 1: (2, 0), 2: (1, 2), 3: (1.5, 1.0), 4: (0.5, 1.0), 5: (1.0, 0.0), 6: (1.0, 0.6666666666666666)} + sage: M1=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1,0,1], + [0, 1, 0, 1, 0, 1, 1,0,0,1,0],[0, 0, 1, 1, 1, 0, 1,0,0,0,0]]) + sage: pos_dict= {0: (0, 0), 1: (2, 0), 2: (1, 2), 3: (1.5, 1.0), + 4: (0.5, 1.0), 5: (1.0, 0.0), 6: (1.0, 0.6666666666666666)} sage: M1._cached_info={'positions':pos_dict,'lineorders':None} sage: matroids_plot_helpers.geomrep(M1,sp=True) - -""" -#***************************************************************************** +""" +# ***************************************************************************** # Copyright (C) 2013 Jayant Apte # # This program is free software: you can redistribute it and/or modify @@ -57,245 +63,293 @@ # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ -#***************************************************************************** +# ***************************************************************************** import scipy import scipy.interpolate import numpy as np -import operator -from sage.structure.sage_object import SageObject -from sage.plot.all import Graphics, line, text, polygon2d, point,points +from sage.plot.all import Graphics, line, text, polygon2d, point, points from sage.sets.set import Set -def initial_triangle(M, B1, nB1, lps): + +def it(M, B1, nB1, lps): """ - Return points on and off the triangle and lines to be drawn for a rank 3 matroid. - + Return points on and off the triangle and lines to be drawn for a rank 3 + matroid. + INPUT: - + - ``M`` -- A matroid. - - ``B1``-- A list of groundset elements of ``M`` that corresponds to a basis of matroid ``M``. - - ``nB1``-- A list of elements in the ground set of M that corresponds to ``M.simplify.groundset() \ B1``. - - ``lps``-- A list of elements in the ground set of matroid M that are loops. - + - ``B1``-- A list of groundset elements of ``M`` that corresponds to a + basis of matroid ``M``. + - ``nB1``-- A list of elements in the ground set of M that corresponds to + ``M.simplify.groundset() \ B1``. + - ``lps``-- A list of elements in the ground set of matroid M that are + loops. + OUTPUT: - + A tuple containing 4 elements in this order: - - 1. A dictionary containing 2-tuple (x,y) co-ordinates with ``M.simplify.groundset()`` elements that can be placed on the sides of the triangle as keys. - 2. A list of 3 lists of elements of ``M.simplify.groundset()`` that can be placed on the 3 sides of the triangle. - 3. A list of elements of `M.simplify.groundset()`` that cane be placed inside the triangle in the geometric representation. - 4. A list of lists of elements of ``M.simplify.groundset()`` that correspond to lines in the geometric representation other than the sides of the triangle. - + + 1. A dictionary containing 2-tuple (x,y) co-ordinates with + ``M.simplify.groundset()`` elements that can be placed on the sides of + the triangle as keys. + 2. A list of 3 lists of elements of ``M.simplify.groundset()`` that can + be placed on the 3 sides of the triangle. + 3. A list of elements of `M.simplify.groundset()`` that cane be placed + inside the triangle in the geometric representation. + 4. A list of lists of elements of ``M.simplify.groundset()`` that + correspond to lines in the geometric representation other than the sides + of the triangle. + EXAMPLES:: - - sage: from sage.matroids import matroids_plot_helpers - sage: M=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0],[0, 1, 0, 1, 0, 1, 1,0],[0, 0, 1, 1, 1, 0, 1,0]]) + + sage: from sage.matroids import matroids_plot_helpers as mph + sage: M=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0], + ....: [0, 1, 0, 1, 0, 1, 1,0],[0, 0, 1, 1, 1, 0, 1,0]]) sage: N=M.simplify() sage: B1=list(N.basis()) sage: nB1=list(set(M.simplify().groundset())-set(B1)) - sage: pts,trilines,nontripts,curvedlines=matroids_plot_helpers.initial_triangle(M,B1,nB1,M.loops()) + sage: pts,trilines,nontripts,curvedlines=mph.it(M, + ....: B1,nB1,M.loops()) sage: print pts - {1: (1.0, 0.0), 2: (1.5, 1.0), 3: (0.5, 1.0), 4: (0, 0), 5: (1, 2), 6: (2, 0)} + {1: (1.0, 0.0), 2: (1.5, 1.0), 3: (0.5, 1.0), 4: (0, 0), 5: (1, 2), + 6: (2, 0)} sage: print trilines [[3, 4, 5], [2, 5, 6], [1, 4, 6]] sage: print nontripts [0] sage: print curvedlines - [[0, 1, 5], [0, 2, 4], [0, 3, 6], [1, 2, 3], [1, 4, 6], [2, 5, 6], [3, 4, 5]] + [[0, 1, 5], [0, 2, 4], [0, 3, 6], [1, 2, 3], [1, 4, 6], [2, 5, 6], + [3, 4, 5]] - .. NOTE:: - This method does NOT do any checks. - - """ - - tripts = [(0, 0),(1, 2),(2, 0)] + This method does NOT do any checks. + + """ + + tripts = [(0, 0), (1, 2), (2, 0)] pts = {} - j=0 + j = 0 for i in B1: pts[i] = tripts[j] j = j + 1 - #nB1=[j for j in gnd if j not in B1] - pairs=[[0, 1],[1,2],[0,2]] + pairs = [[0, 1], [1, 2], [0, 2]] L1 = [] L2 = [] L3 = [] for i in nB1: - if M.is_dependent([i,B1[pairs[0][0]],B1[pairs[0][1]]]): - # Add to L1 + if M.is_dependent([i, B1[pairs[0][0]], B1[pairs[0][1]]]): + # Add to L1 L1.append(i) - elif M.is_dependent([i,B1[pairs[1][0]],B1[pairs[1][1]]]): - # Add to L2 + elif M.is_dependent([i, B1[pairs[1][0]], B1[pairs[1][1]]]): + # Add to L2 L2.append(i) - elif M.is_dependent([i,B1[pairs[2][0]],B1[pairs[2][1]]]): - # Add to L3 + elif M.is_dependent([i, B1[pairs[2][0]], B1[pairs[2][1]]]): + # Add to L3 L3.append(i) - L = [L1,L2,L3] - lines = [] #the list of lines - for i in range(1,len(L)+1): + L = [L1, L2, L3] # megalist + lines = [] # the list of lines + for i in range(1, len(L)+1): lines.append([B1[pairs[i-1][0]]]) lines[i-1].extend(L[i-1]) lines[i-1].extend([B1[pairs[i-1][1]]]) # place triangle and L1,L2,L3 - - for i in L:# loop over megalist + for i in L: # loop over megalist interval = 1/float(len(i)+1) pt1 = list(tripts[pairs[L.index(i)][0]]) pt2 = list(tripts[pairs[L.index(i)][1]]) - for j in range(1,len(i)+1): - #Loop over L1,L2,L3 + for j in range(1, len(i)+1): + # loop over L1,L2,L3 cc = interval*j - pts[i[j-1]] = (cc*pt1[0]+(1-cc)*pt2[0],cc*pt1[1]+(1-cc)*pt2[1]) - - - trilines = [list(set(x)) for x in lines if len(x)>=3] - curvedlines = [list(set(list(x)).difference(set(lps))) for x in M.flats(2) if set(list(x)) not in trilines if len(list(x))>=3] - nontripts = [i for i in nB1 if i not in pts.keys()] - return pts,trilines,nontripts,curvedlines + pts[i[j-1]] = (cc*pt1[0]+(1-cc)*pt2[0], cc*pt1[1]+(1-cc)*pt2[1]) + trilines = [list(set(x)) for x in lines if len(x) >= 3] + curvedlines = [list(set(list(x)).difference(set(lps))) + for x in M.flats(2) if set(list(x)) not in trilines if + len(list(x)) >= 3] + nontripts = [i for i in nB1 if i not in pts.keys()] + return pts, trilines, nontripts, curvedlines + def trigrid(tripts): """ Return a grid of 4 points inside given 3 points as a list. - - + INPUT: - - - ``tripts`` -- A list of 3 lists of the form [x,y] where x and y are the cartesian co-ordinates of a point. - + + - ``tripts`` -- A list of 3 lists of the form [x,y] where x and y are the + cartesian co-ordinates of a point. + OUTPUT: - + A list of lists containing 4 points in following order: - + 1. Barycenter of 3 input points. - 2,3,4. Barycenters of 1. with 3 different 2-subsets of input points respectively. - + 2,3,4. Barycenters of 1. with 3 different 2-subsets of input points + respectively. + EXAMPLES:: - + sage: from sage.matroids import matroids_plot_helpers sage: points=matroids_plot_helpers.trigrid([[2,1],[4,5],[5,2]]) sage: print points - [[3.6666666666666665, 2.6666666666666665], [3.222222222222222, 2.888888888888889], [4.222222222222222, 3.222222222222222], [3.5555555555555554, 1.8888888888888886]] - + [[3.6666666666666665, 2.6666666666666665], + [3.222222222222222, 2.888888888888889], + [4.222222222222222, 3.222222222222222], + [3.5555555555555554, 1.8888888888888886]] + .. NOTE:: This method does NOT do any checks. - - """ + + """ n = 0 - pairs = [[0,1],[1,2],[0,2]] - cpt = list((float(tripts[0][0]+tripts[1][0]+tripts[2][0])/3,float(tripts[0][1]+tripts[1][1]+tripts[2][1])/3)) + pairs = [[0, 1], [1, 2], [0, 2]] + cpt = list((float(tripts[0][0]+tripts[1][0]+tripts[2][0])/3, + float(tripts[0][1]+tripts[1][1]+tripts[2][1])/3)) grid = [cpt] for p in pairs: - pt = list((float(tripts[p[0]][0]+tripts[p[1]][0]+cpt[0])/3,float(tripts[p[0]][1]+tripts[p[1]][1]+cpt[1])/3)) + pt = list((float(tripts[p[0]][0]+tripts[p[1]][0]+cpt[0])/3, + float(tripts[p[0]][1]+tripts[p[1]][1]+cpt[1])/3)) grid.append(pt) return grid - -def addnontripts(tripts_labels,nontripts_labels,ptsdict): + + +def addnontripts(tripts_labels, nontripts_labels, ptsdict): """ - Return modified ``ptsdict`` with additional keys and values corresponding to ``nontripts``. - - + Return modified ``ptsdict`` with additional keys and values corresponding + to ``nontripts``. + INPUT: - - - ``tripts`` -- A list of 3 ground set elements that are to be placed on vertices of the triangle. - - ``ptsdict`` -- A dictionary (at least) containing ground set elements in ``tripts`` as keys and their (x,y) position as values. - - ``nontripts``-- A list of ground set elements whose corresponding points are to be placed inside the triangle. - + + - ``tripts`` -- A list of 3 ground set elements that are to be placed on + vertices of the triangle. + - ``ptsdict`` -- A dictionary (at least) containing ground set elements in + ``tripts`` as keys and their (x,y) position as values. + - ``nontripts``-- A list of ground set elements whose corresponding points + are to be placed inside the triangle. + OUTPUT: - - A dictionary containing ground set elements in ``tripts`` as keys and their (x,y) position as values allong with all keys and respective values in ``ptsdict``. - + + A dictionary containing ground set elements in ``tripts`` as keys and + their (x,y) position as values allong with all keys and respective values + in ``ptsdict``. + EXAMPLES:: - + sage: from sage.matroids import matroids_plot_helpers sage: ptsdict={'a':(0,0),'b':(1,2),'c':(2,0)} - sage: ptsdict_1=matroids_plot_helpers.addnontripts(['a','b','c'],['d','e','f'],ptsdict) + sage: ptsdict_1=matroids_plot_helpers.addnontripts(['a','b','c'], + ....: ['d','e','f'],ptsdict) sage: print ptsdict_1 - {'a': (0, 0), 'c': (2, 0), 'b': (1, 2), 'e': (0.6666666666666666, 0.8888888888888888), 'd': (1.0, 0.6666666666666666), 'f': (1.3333333333333333, 0.8888888888888888)} - - sage: ptsdict_2=matroids_plot_helpers.addnontripts(['a','b','c'],['d','e','f','g','h'],ptsdict) + sage: sage: print ptsdict_1 + {'a': (0, 0), 'c': (2, 0), 'b': (1, 2), + 'e': (0.6666666666666666, 0.8888888888888888), + 'd': (1.0, 0.6666666666666666), + 'f': (1.3333333333333333, 0.8888888888888888)} + sage: ptsdict_2=matroids_plot_helpers.addnontripts(['a','b','c'], + ....: ['d','e','f','g','h'],ptsdict) sage: print ptsdict_2 - {'a': (0, 0), 'c': (2, 0), 'b': (1, 2), 'e': (0.6666666666666666, 0.8888888888888888), 'd': (1.0, 0.6666666666666666), 'g': (1.0, 0.2222222222222222), 'f': (1.3333333333333333, 0.8888888888888888), 'h': (0.5555555555555555, 0.5185185185185185)} - + {'a': (0, 0), 'c': (2, 0), 'b': (1, 2), + 'e': (0.6666666666666666, 0.8888888888888888), + 'd': (1.0, 0.6666666666666666), 'g': (1.0, 0.2222222222222222), + 'f': (1.3333333333333333, 0.8888888888888888), + 'h': (0.5555555555555555, 0.5185185185185185)} + .. NOTE:: This method does NOT do any checks. - + """ tripts = [list(ptsdict[p]) for p in tripts_labels] - pairs = [[0,1],[1,2],[0,2]] + pairs = [[0, 1], [1, 2], [0, 2]] q = [tripts] num = len(nontripts_labels) - gridpts = [[float((tripts[0][0]+tripts[1][0]+tripts[2][0])/3),float(tripts[0][1]+tripts[1][1]+tripts[2][1])/3]] + gridpts = [[float((tripts[0][0]+tripts[1][0]+tripts[2][0])/3), + float(tripts[0][1]+tripts[1][1]+tripts[2][1])/3]] n = 0 - while n (float(max(ypts))-float(min(ypts))): + xdim = (float(max(xpts))-float(min(xpts))) + ydim = (float(max(ypts))-float(min(ypts))) + if xdim > ydim: sortedind = sorted(range(len(xpts)), key=lambda k: float(xpts[k])) else: sortedind = sorted(range(len(ypts)), key=lambda k: float(ypts[k])) @@ -307,145 +361,166 @@ def createline(ptsdict, ll, lineorders2=None): linepts = [list(ptsdict[i]) for i in lo] sortedx = [k[0] for k in linepts] sortedy = [k[1] for k in linepts] - - if flip == True: - tck,u = scipy.interpolate.splprep([sortedy,sortedx],s=0.0,k=2) - y_i,x_i = scipy.interpolate.splev(np.linspace(0,1,100),tck) + + if flip is True: + tck, u = scipy.interpolate.splprep([sortedy, sortedx], s=0.0, k=2) + y_i, x_i = scipy.interpolate.splev(np.linspace(0, 1, 100), tck) else: - tck,u = scipy.interpolate.splprep([sortedx,sortedy],s=0.0,k=2) - x_i,y_i = scipy.interpolate.splev(np.linspace(0,1,100),tck) - return sortedx,sortedy,x_i,y_i + tck, u = scipy.interpolate.splprep([sortedx, sortedy], s=0.0, k=2) + x_i, y_i = scipy.interpolate.splev(np.linspace(0, 1, 100), tck) + return sortedx, sortedy, x_i, y_i + -def slp(M1, pos_dict=None,B=None): +def slp(M1, pos_dict=None, B=None): """ Return simple matroid, loops and parallel elements of given matroid. - + INPUT: - + - ``M1`` -- A matroid. - - ``pos_dict`` -- (optional) A dictionary containing non loopy elements of ``M`` as keys and their (x,y) positions - as keys. While simplifying the matroid, all except one element in a parallel class that is also specified in ``pos_dict`` - will be retained. - - ``B`` -- (optional) A basis of M1 that has been chosen for placement on vertics of triangle - + - ``pos_dict`` -- (optional) A dictionary containing non loopy elements of + ``M`` as keys and their (x,y) positions. + as keys. While simplifying the matroid, all except one element in a + parallel class that is also specified in ``pos_dict`` will be retained. + - ``B`` -- (optional) A basis of M1 that has been chosen for placement on + vertics of triangle. + OUTPUT: - + A tuple containing 3 elements in this order: + 1. Simple matroid corresponding to ``M1``. 2. Loops of matroid ``M1``. - 3. Elements that are in `M1.groundset()` but not in ground set of 1. or in 2. - + 3. Elements that are in `M1.groundset()` but not in ground set of 1 or + in 2 + EXAMPLES:: - + sage: from sage.matroids import matroids_plot_helpers - sage: M1=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1,0,1],[0, 1, 0, 1, 0, 1, 1,0,0,1,0],[0, 0, 1, 1, 1, 0, 1,0,0,0,0]]) + sage: M1=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1,0,1], + ....: [0, 1, 0, 1, 0, 1, 1,0,0,1,0],[0, 0, 1, 1, 1, 0, 1,0,0,0,0]]) sage: [M,L,P]=matroids_plot_helpers.slp(M1) sage: M.is_simple() True sage: [L,P] [{7}, {8, 9, 10}] - - sage: M1=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1,0,1],[0, 1, 0, 1, 0, 1, 1,0,0,1,0],[0, 0, 1, 1, 1, 0, 1,0,0,0,0]]) - sage: posdict= {8: (0, 0), 1: (2, 0), 2: (1, 2), 3: (1.5, 1.0), 4: (0.5, 1.0), 5: (1.0, 0.0), 6: (1.0, 0.6666666666666666)} + sage: M1=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1,0,1], + ....: [0, 1, 0, 1, 0, 1, 1,0,0,1,0],[0, 0, 1, 1, 1, 0, 1,0,0,0,0]]) + sage: posdict= {8: (0, 0), 1: (2, 0), 2: (1, 2), 3: (1.5, 1.0), + ....: 4: (0.5, 1.0), 5: (1.0, 0.0), 6: (1.0, 0.6666666666666666)} sage: [M,L,P]=matroids_plot_helpers.slp(M1,pos_dict=posdict) sage: M.is_simple() True sage: [L,P] [{7}, {0, 9, 10}] - + .. NOTE:: This method does NOT do any checks. - + """ L = set(M1.loops()) sg = sorted(M1.simplify().groundset()) - nP = L|set(M1.simplify().groundset()) + nP = L | set(M1.simplify().groundset()) P = set(M1.groundset())-nP if len(P) > 0: - if pos_dict != None: - pcls=list(set([frozenset(set(M1.closure([p]))-L) for p in list(P)])) - newP=[] + if pos_dict is not None: + pcls = list(set([frozenset(set(M1.closure([p])) - L) + for p in list(P)])) + newP = [] for pcl in pcls: - pcl_in_dict=[p for p in list(pcl) if p in pos_dict.keys()] + pcl_in_dict = [p for p in list(pcl) if p in pos_dict.keys()] newP.extend(list(pcl-set([pcl_in_dict[0]]))) - return [M1.delete(L|set(newP)),L,set(newP)] - elif B != None: - pcls=list(set([frozenset(set(M1.closure([p]))-L) for p in list(P)])) - newP=[] + return [M1.delete(L | set(newP)), L, set(newP)] + elif B is not None: + pcls = list(set([frozenset(set(M1.closure([p])) - L) + for p in list(P)])) + newP = [] for pcl in pcls: - pcl_in_basis=[p for p in list(pcl) if p in B] - if len(pcl_in_basis)>0: - newP.extend(list(pcl-set([pcl_in_basis[0]]))) + pcl_in_basis = [p for p in list(pcl) if p in B] + if len(pcl_in_basis) > 0: + newP.extend(list(pcl - set([pcl_in_basis[0]]))) else: - newP.extend(list(pcl-set([pcl[0]]))) - return [M1.delete(L|set(newP)),L,set(newP)] + newP.extend(list(pcl - set([pcl[0]]))) + return [M1.delete(L | set(newP)), L, set(newP)] else: - return [M1.delete(L|P),L,P] + return [M1.delete(L | P), L, P] else: - return [M1.delete(L|P),L,P] - -def addlp(M, M1, L, P, ptsdict, G=None,limits=None): + return [M1.delete(L | P), L, P] + + +def addlp(M, M1, L, P, ptsdict, G=None, limits=None): """ - Return a graphics object containing loops (in inset) and parallel elements of matroid. - + Return a graphics object containing loops (in inset) and parallel elements + of matroid. + INPUT: - + - ``M`` -- A matroid. - - ``L`` -- List of elements in ``M.groundset()`` that are loops of matroid ``M``. - - ``P`` -- List of elements in ``M.groundset()`` not in ``M.simplify.groundset()`` or ``L``. - - ``ptsdict`` -- A dictionary containing elements in ``M.groundset()`` not necessarily containing elements of ``L``. - - ``G`` -- (optional) A sage graphics object to which loops and parallel elements of matroid `M` added . + - ``L`` -- List of elements in ``M.groundset()`` that are loops of matroid + ``M``. + - ``P`` -- List of elements in ``M.groundset()`` not in + ``M.simplify.groundset()`` or ``L``. + - ``ptsdict`` -- A dictionary containing elements in ``M.groundset()`` not + necessarily containing elements of ``L``. + - ``G`` -- (optional) A sage graphics object to which loops and parallel + elements of matroid `M` added . - ``limits``-- (optional) Current axes limits [xmin,xmax,ymin,ymax]. - + OUTPUT: + A 2-tuple containing: - - 1. A sage graphics object containing loops and parallel elements of matroid ``M`` + + 1. A sage graphics object containing loops and parallel elements of + matroid ``M`` 2. axes limits array - + EXAMPLES: - + sage: from sage.matroids import matroids_plot_helpers - sage: M=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1],[0, 1, 0, 1, 0, 1, 1,0,0],[0, 0, 1, 1, 1, 0, 1,0,0]]) + sage: M=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1], + ....: [0, 1, 0, 1, 0, 1, 1,0,0],[0, 0, 1, 1, 1, 0, 1,0,0]]) sage: [M1,L,P]=matroids_plot_helpers.slp(M) - sage: G=matroids_plot_helpers.addlp(M,M1,L,P,{0:(0,0)}) + sage: G,lims=matroids_plot_helpers.addlp(M,M1,L,P,{0:(0,0)}) sage: G.show(axes=False) - sage: sage: G.show(axes=False) - + .. NOTE:: This method does NOT do any checks. - + """ - if G == None: + if G is None: G = Graphics() - #M1 = M.simplify() # deal with loops if len(L) > 0: loops = L looptext = ", ".join([str(l) for l in loops]) - if(limits==None): + if(limits is None): rectx = -1 recty = -1 else: rectx = limits[0] recty = limits[2]-1 - rectw = 0.5 + 0.4*len(loops) + 0.5 # control this based on len(loops) + rectw = 0.5 + 0.4*len(loops) + 0.5 # controlled based on len(loops) recth = 0.6 - G += polygon2d([[rectx,recty], [rectx,recty+recth], [rectx+rectw,recty+recth], [rectx+rectw,recty]], color='black',fill=False,thickness=4) - G += text(looptext,(rectx+0.5,recty+0.3),color='black',fontsize=13) - G += point((rectx+0.2, recty+0.3),color='black', size=300,zorder=2) - G += text('Loop(s)',(rectx+0.5+0.4*len(loops)+0.1,recty+0.3),fontsize=13,color='black') - limits=tracklims(limits,[rectx,rectx+rectw],[recty,recty+recth]) + G += polygon2d([[rectx, recty], [rectx, recty+recth], + [rectx+rectw, recty+recth], [rectx+rectw, recty]], + color='black', fill=False, thickness=4) + G += text(looptext, (rectx+0.5, recty+0.3), color='black', + fontsize=13) + G += point((rectx+0.2, recty+0.3), color='black', size=300, zorder=2) + G += text('Loop(s)', (rectx+0.5+0.4*len(loops)+0.1, recty+0.3), + fontsize=13, color='black') + limits = tracklims(limits, [rectx, rectx+rectw], [recty, recty+recth]) + # deal with parallel elements if len(P) > 0: - # create list of lists where inner lists are parallel classes + # create list of lists where inner lists are parallel classes pcls = [] gnd = sorted(M1.groundset_list()) for g in gnd: pcl = [g] for p in P: - if M.rank([g,p]) == 1: + if M.rank([g, p]) == 1: pcl.extend([p]) pcls.append(pcl) for pcl in pcls: @@ -453,189 +528,223 @@ def addlp(M, M1, L, P, ptsdict, G=None,limits=None): basept = list(ptsdict[pcl[0]]) if len(pcl) <= 2: # add side by side - ptsdict[pcl[1]] = (basept[0],basept[1]-0.13) - G += points(zip([basept[0]], [basept[1]-0.13]),color='black', size=300,zorder=2) - G += text(pcl[1],(float(basept[0]), float(basept[1])-0.13),color='white',fontsize=13) - limits=tracklims(limits,[basept[0]],[basept[1]-0.13]) + ptsdict[pcl[1]] = (basept[0], basept[1]-0.13) + G += points(zip([basept[0]], [basept[1]-0.13]), + color='black', size=300, zorder=2) + G += text(pcl[1], (float(basept[0]), + float(basept[1])-0.13), color='white', + fontsize=13) + limits = tracklims(limits, [basept[0]], [basept[1]-0.13]) else: # add in a bracket - G += text('{ '+", ".join(sorted([str(kk) for kk in pcl[1:]]))+' }',(float(basept[0]), float(basept[1]-0.2)-0.034),color='black',fontsize=13) - limits=tracklims(limits,[basept[0]],[(basept[1]-0.2)-0.034]) - return G,limits - + pce = sorted([str(kk) for kk in pcl[1:]]) + G += text('{ '+", ".join(pce)+' }', (float(basept[0]), + float(basept[1]-0.2)-0.034), color='black', + fontsize=13) + limits = tracklims(limits, [basept[0]], + [(basept[1]-0.2)-0.034]) + return G, limits + + def line_hasorder(l, lodrs=None): """ Determine if an order is specified for a line - + INPUT: - + - ``l`` -- A line specified as a list of ground set elements. - - ``lordrs`` -- (optional) A list of lists each specifying an order on the subset of ground set elements that may or may not correspond to a line in geometric representation. + - ``lordrs`` -- (optional) A list of lists each specifying an order on + a subset of ground set elements that may or may not correspond to a + line in geometric representation. OUTPUT: - + A tuple containing 2 elements in this order: - 1. A boolean indicating whether there is any list in ``lordrs`` that is setwise equal to ``l``. - 2. A list specifying an order on ``set(l)`` if 1. is True, otherwise an empty list. - + 1. A boolean indicating whether there is any list in ``lordrs`` that is + setwise equal to ``l``. + 2. A list specifying an order on ``set(l)`` if 1. is True, otherwise + an empty list. + EXAMPLES:: - - - sage: matroids_plot_helpers.line_hasorder(['a','b','c','d'],[['a','c','d','b'],['p','q','r']]) + + sage: from sage.matroids import matroids_plot_helpers + sage: matroids_plot_helpers.line_hasorder(['a','b','c','d'], + ....: [['a','c','d','b'],['p','q','r']]) (True, ['a', 'c', 'd', 'b']) - sage: matroids_plot_helpers.line_hasorder(['a','b','c','d'],[['p','q','r'],['l','m','n','o']]) + sage: sage: matroids_plot_helpers.line_hasorder(['a','b','c','d'], + ....: [['p','q','r'],['l','m','n','o']]) (False, []) - + .. NOTE:: This method does NOT do any checks. - """ - if lodrs != None: + if lodrs is not None: if len(lodrs) > 0: for i in lodrs: if Set(i) == Set(l): - return True,i - return False,[] + return True, i + return False, [] + def lineorders_union(lineorders1, lineorders2): """ - Return a list of ordered lists of ground set elements that corresponds to union of two sets of ordered lists of ground set elements in a sense. - + Return a list of ordered lists of ground set elements that corresponds to + union of two sets of ordered lists of ground set elements in a sense. + INPUT: - - - ``lineorders1`` -- A list of ordered lists specifying orders on subsets of ground set. - - ``lineorders2`` -- A list of ordered lists specifying orders subsets of ground set. - + + - ``lineorders1`` -- A list of ordered lists specifying orders on subsets + of ground set. + - ``lineorders2`` -- A list of ordered lists specifying orders subsets of + ground set. + OUTPUT: - - A list of ordered lists of ground set elements that are (setwise) in only one of ``lineorders1`` or ``lineorders2`` - along with the ones in lineorder2 that are setwise equal to any list in lineorders1. - + + A list of ordered lists of ground set elements that are (setwise) in only + one of ``lineorders1`` or ``lineorders2`` along with the ones in + lineorder2 that are setwise equal to any list in lineorders1. + EXAMPLES:: - + sage: from sage.matroids import matroids_plot_helpers - sage: matroids_plot_helpers.lineorders_union([['a','b','c'],['p','q','r'],['i','j','k','l']],[['r','p','q']]) + sage: matroids_plot_helpers.lineorders_union([['a','b','c'], + ....: ['p','q','r'],['i','j','k','l']],[['r','p','q']]) [['a', 'b', 'c'], ['p', 'q', 'r'], ['i', 'j', 'k', 'l']] """ - if lineorders1 != None and lineorders2 != None: + if lineorders1 is not None and lineorders2 is not None: lineorders = lineorders1 for order in lineorders2: - x,lo = line_hasorder(order,lineorders1) - if x == False: + x, lo = line_hasorder(order, lineorders1) + if x is False: lineorders.append(order) lineorders.remove(lo) return lineorders - elif lineorders1 == None and lineorders2 != None: - return lineorders2 - elif lineorders1!=None: + elif lineorders1 is None and lineorders2 is not None: + return lineorders2 + elif lineorders1 is not None: return lineorders1 else: - return None - + return None + + def posdict_is_sane(M1, pos_dict): """ Return a boolean establishing sanity of ``posdict`` wrt matroid ``M``. - + INPUT: - + - ``M1`` -- A matroid. - - ``posdict`` -- A dictionary mapping ground set elements to (x,y) positions. - + - ``posdict`` -- A dictionary mapping ground set elements to (x,y) + positions. + OUTPUT: - - A boolean that is ``True`` if posdict indeed has all the required elements to plot the geometric elements, otherwise ``False``. - + + A boolean that is ``True`` if posdict indeed has all the required elements + to plot the geometric elements, otherwise ``False``. + EXAMPLES: - + sage: from sage.matroids import matroids_plot_helpers - sage: M1=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1,0,1],[0, 1, 0, 1, 0, 1, 1,0,0,1,0],[0, 0, 1, 1, 1, 0, 1,0,0,0,0]]) - sage: pos_dict= {0: (0, 0), 1: (2, 0), 2: (1, 2), 3: (1.5, 1.0), 4: (0.5, 1.0), 5: (1.0, 0.0), 6: (1.0, 0.6666666666666666)} + sage: M1=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1,0,1], + ....: [0, 1, 0, 1, 0, 1, 1,0,0,1,0],[0, 0, 1, 1, 1, 0, 1,0,0,0,0]]) + sage: pos_dict= {0: (0, 0), 1: (2, 0), 2: (1, 2), 3: (1.5, 1.0), + ....: 4: (0.5, 1.0), 5: (1.0, 0.0), 6: (1.0, 0.6666666666666666)} sage: matroids_plot_helpers.posdict_is_sane(M1,pos_dict) True - sage: pos_dict= {1: (2, 0), 2: (1, 2), 3: (1.5, 1.0), 4: (0.5, 1.0), 5: (1.0, 0.0), 6: (1.0, 0.6666666666666666)} + sage: pos_dict= {1: (2, 0), 2: (1, 2), 3: (1.5, 1.0), + ....: 4: (0.5, 1.0), 5: (1.0, 0.0), 6: (1.0, 0.6666666666666666)} sage: matroids_plot_helpers.posdict_is_sane(M1,pos_dict) False - - + .. NOTE:: - This method does NOT do any checks. ``M1`` is assumed to be a matroid and ``posdict`` is assumed to be a dictionary. + This method does NOT do any checks. ``M1`` is assumed to be a + matroid and ``posdict`` is assumed to be a dictionary. """ L = set(M1.loops()) sg = sorted(M1.simplify().groundset()) - nP = L|set(M1.simplify().groundset()) + nP = L | set(M1.simplify().groundset()) P = set(M1.groundset())-nP - #pcls=list(set([set(M1.closure([p]))-L for p in list(P)])) - pcls=list(set([frozenset(set(M1.closure([p]))-L) for p in list(P)])) + pcls = list(set([frozenset(set(M1.closure([p])) - L) for p in list(P)])) for pcl in pcls: - pcl_list=list(pcl) + pcl_list = list(pcl) if not any([x in pos_dict.keys() for x in pcl_list]): return False - allP=[] + allP = [] for pcl in pcls: allP.extend(list(pcl)) - if not all([x in pos_dict.keys() for x in list(set(M1.groundset())-(L|set(allP)))]): + if not all([x in pos_dict.keys() + for x in list(set(M1.groundset()) - (L | set(allP)))]): return False return True - -def tracklims(lims,x_i=[],y_i=[]): + + +def tracklims(lims, x_i=[], y_i=[]): """ - Return modified limits list - + Return modified limits list. + INPUT: - + - ``lims`` -- A list with 4 elements ``[xmin,xmax,ymin,ymax]`` - ``x_i`` -- New x values to track - ``y_i`` -- New y values to track - + OUTPUT: - - A list with 4 elements ``[xmin,xmax,ymin,ymax]`` - + + A list with 4 elements ``[xmin,xmax,ymin,ymax]`` + EXAMPLES:: - + sage: from sage.matroids import matroids_plot_helpers - sage: matroids_plot_helpers.tracklims([0,5,-1,7],[1,2,3,6,-1],[-1,2,3,6]) + sage: matroids_plot_helpers.tracklims([0,5,-1,7],[1,2,3,6,-1], + ....: [-1,2,3,6]) [-1, 6, -1, 7] - + .. NOTE:: This method does NOT do any checks. """ - if lims!=None and lims[0]!=None and lims[1]!=None and lims[2]!=None and lims[3]!=None: - lims=[min(min(x_i),lims[0]),max(max(x_i),lims[1]),min(min(y_i),lims[2]),max(max(y_i),lims[3])] + if lims is not None and lims[0] is not None and lims[1] is not None and \ + lims[2] is not None and lims[3] is not None: + lims = [min(min(x_i), lims[0]), max(max(x_i), lims[1]), + min(min(y_i), lims[2]), max(max(y_i), lims[3])] else: - lims=[min(x_i),max(x_i),min(y_i),max(y_i)] - return lims - + lims = [min(x_i), max(x_i), min(y_i), max(y_i)] + return lims + + def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): """ - Return a sage graphics object containing geometric representation of matroid M1. - + Return a sage graphics object containing geometric representation of + matroid M1. + INPUT: - + - ``M1`` -- A matroid. - - ``B1`` -- (optional) A list of elements in ``M1.groundset()`` that correspond to a basis. - of ``M1`` and will be placed as vertices of the triangle in the geometric representation of ``M1``. - - ``lineorders1`` -- (optional) A list of ordered lists of elements of ``M1.grondset()`` such that - if a line in geometric representation is setwise same as any of these then points contained will be - traversed in that order thus overriding internal order deciding heuristic. - - ``pd`` - (optional) A dictionary mapping ground set elements to their (x,y) positions. - - ``sp`` -- (optional) If True, a positioning dictionary and line orders will be placed in ``M._cached_info``. - + - ``B1`` -- (optional) A list of elements in ``M1.groundset()`` that + correspond to a basis of ``M1`` and will be placed as vertices of the + triangle in the geometric representation of ``M1``. + - ``lineorders1`` -- (optional) A list of ordered lists of elements of + ``M1.grondset()`` such that if a line in geometric representation is + setwise same as any of these then points contained will be traversed in + that order thus overriding internal order deciding heuristic. + - ``pd`` - (optional) A dictionary mapping ground set elements to their + (x,y) positions. + - ``sp`` -- (optional) If True, a positioning dictionary and line orders + will be placed in ``M._cached_info``. + OUTPUT: - - A sage graphics object of type that + + A sage graphics object of type that corresponds to the geometric representation of the matroid. - + EXAMPLES:: - + sage: from sage.matroids import matroids_plot_helpers sage: M=matroids.named_matroids.P7() sage: G=matroids_plot_helpers.geomrep(M) sage: G.show(xmin=-2, xmax=3, ymin=-2, ymax=3) - sage: M=matroids.named_matroids.P7() sage: G=matroids_plot_helpers.geomrep(M,lineorders1=[['f','e','d']]) sage: G.show(xmin=-2, xmax=3, ymin=-2, ymax=3) @@ -646,88 +755,103 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): """ G = Graphics() # create lists of loops and parallel elements and simplify given matroid - if B1 == None: - B1 = list(M.basis()) - [M,L,P] = slp(M1,pos_dict=pd,B=B1) + if B1 is None: + B1 = list(M1.basis()) + [M, L, P] = slp(M1, pos_dict=pd, B=B1) M._cached_info = M1._cached_info - - if M.rank()==1: - if M._cached_info != None and 'positions' in M._cached_info.keys() and M._cached_info['positions'] != None: + + if M.rank() == 1: + if M._cached_info is not None and \ + 'positions' in M._cached_info.keys() and \ + M._cached_info['positions'] is not None: pts = M._cached_info['positions'] else: pts = {} gnd = sorted(M.groundset()) - - pts[gnd[0]] = (1,float(2)/3) - G += point((1, float(2)/3),size=300,zorder=2) + pts[gnd[0]] = (1, float(2)/3) + G += point((1, float(2)/3), size=300, zorder=2) pts2 = pts # track limits [xmin,xmax,ymin,ymax] - pl=[list(x) for x in pts2.values()] - lims=tracklims([None,None,None,None],[pt[0] for pt in pl],[pt[1] for pt in pl]) + pl = [list(x) for x in pts2.values()] + lims = tracklims([None, None, None, None], [pt[0] for pt in pl], + [pt[1] for pt in pl]) elif M.rank() == 2: - nB1=list(set(list(M.groundset())) - set(B1)) + nB1 = list(set(list(M.groundset())) - set(B1)) bline = [] for j in nB1: - if M.is_dependent([j,B1[0],B1[1]]): + if M.is_dependent([j, B1[0], B1[1]]): bline.append(j) - interval = len(bline)+1 - - if M._cached_info != None and 'positions' in M._cached_info.keys() and M._cached_info['positions'] != None: + if M._cached_info is not None and \ + 'positions' in M._cached_info.keys() and \ + M._cached_info['positions'] is not None: pts2 = M._cached_info['positions'] else: pts2 = {} - pts2[B1[0]] = (0,0) - pts2[B1[1]] = (2,0) + pts2[B1[0]] = (0, 0) + pts2[B1[1]] = (2, 0) lpt = list(pts2[B1[0]]) rpt = list(pts2[B1[1]]) for k in range(len(bline)): cc = (float(1)/interval)*(k+1) - pts2[bline[k]] = (cc*lpt[0]+(1-cc)*rpt[0],cc*lpt[1]+(1-cc)*rpt[1]) - if sp == True: - M._cached_info['positions']=pts2 + pts2[bline[k]] = (cc*lpt[0]+(1-cc)*rpt[0], + cc*lpt[1]+(1-cc)*rpt[1]) + if sp is True: + M._cached_info['positions'] = pts2 # track limits [xmin,xmax,ymin,ymax] - pl=[list(x) for x in pts2.values()] - lims=tracklims([None,None,None,None],[pt[0] for pt in pl],[pt[1] for pt in pl]) + pl = [list(x) for x in pts2.values()] + lims = tracklims([None, None, None, None], [pt[0] for pt in pl], + [pt[1] for pt in pl]) bline.extend(B1) - ptsx,ptsy,x_i,y_i = createline(pts2,bline,lineorders1) - lims=tracklims(lims,x_i,y_i) - G += line(zip(x_i, y_i),color='black',thickness=3,zorder=1) + ptsx, ptsy, x_i, y_i = createline(pts2, bline, lineorders1) + lims = tracklims(lims, x_i, y_i) + G += line(zip(x_i, y_i), color='black', thickness=3, zorder=1) allpts = [list(pts2[i]) for i in M.groundset()] - xpts = [float(k[0]) for k in allpts] - ypts = [float(k[1]) for k in allpts] - G += points(zip(xpts, ypts), color='black', size=300,zorder=2) + xpts = [float(k[0]) for k in allpts] + ypts = [float(k[1]) for k in allpts] + G += points(zip(xpts, ypts), color='black', size=300, zorder=2) for i in pts2: pt = list(pts2[i]) - G += text(i,(float(pt[0]), float(pt[1])), color='white',fontsize=13) + G += text(i, (float(pt[0]), float(pt[1])), color='white', + fontsize=13) else: - if M._cached_info == None or 'positions' not in M._cached_info.keys() or M._cached_info['positions'] == None: - pts,trilines,nontripts,curvedlines = initial_triangle(M1,B1,list(set(M.groundset())-set(B1)), list(set(L)|set(P))) - pts2= addnontripts([B1[0],B1[1],B1[2]],nontripts,pts) + if M._cached_info is None or \ + 'positions' not in M._cached_info.keys() or \ + M._cached_info['positions'] is None: + (pts, trilines, + nontripts, curvedlines) = it(M1, B1, + list(set(M.groundset())-set(B1)), + list(set(L) | set(P))) + pts2 = addnontripts([B1[0], B1[1], B1[2]], nontripts, pts) trilines.extend(curvedlines) else: - pts2=M._cached_info['positions'] - trilines=[list(set(list(x)).difference(L|P)) for x in M1.flats(2) if len(list(x))>=3] - pl=[list(x) for x in pts2.values()] - lims=tracklims([None,None,None,None],[pt[0] for pt in pl],[pt[1] for pt in pl]) + pts2 = M._cached_info['positions'] + trilines = [list(set(list(x)).difference(L | P)) + for x in M1.flats(2) + if len(list(x)) >= 3] + pl = [list(x) for x in pts2.values()] + lims = tracklims([None, None, None, None], [pt[0] for pt in pl], + [pt[1] for pt in pl]) j = 0 for ll in trilines: - if len(ll)>=3: - ptsx,ptsy,x_i,y_i=createline(pts2,ll,lineorders1) - lims=tracklims(lims,x_i,y_i) - G += line(zip(x_i,y_i),color='black',thickness=3,zorder=1) + if len(ll) >= 3: + ptsx, ptsy, x_i, y_i = createline(pts2, ll, lineorders1) + lims = tracklims(lims, x_i, y_i) + G += line(zip(x_i, y_i), color='black', thickness=3, zorder=1) allpts = [list(pts2[i]) for i in M.groundset()] - xpts = [float(k[0]) for k in allpts] - ypts = [float(k[1]) for k in allpts] - G += points(zip(xpts,ypts),color='black', size=300,zorder=2) + xpts = [float(k[0]) for k in allpts] + ypts = [float(k[1]) for k in allpts] + G += points(zip(xpts, ypts), color='black', size=300, zorder=2) for i in pts2: pt = list(pts2[i]) - G += text(i,(float(pt[0]), float(pt[1])),color='white',fontsize=13) - if sp == True: - M1._cached_info['positions']=pts2 - M1._cached_info['lineorders']=lineorders1 - #deal with loops and parallel elements - G,lims = addlp(M1,M,L,P,pts2,G,lims) + G += text(i, (float(pt[0]), float(pt[1])), color='white', + fontsize=13) + if sp is True: + M1._cached_info['positions'] = pts2 + M1._cached_info['lineorders'] = lineorders1 + # deal with loops and parallel elements + G, lims = addlp(M1, M, L, P, pts2, G, lims) G.axes(False) - G.axes_range(xmin=lims[0]-0.5,xmax=lims[1]+0.5,ymin=lims[2]-0.5,ymax=lims[3]+0.5) + G.axes_range(xmin=lims[0]-0.5, xmax=lims[1]+0.5, ymin=lims[2]-0.5, + ymax=lims[3]+0.5) return G From d280e33dc53ee8bb3ef2be0ca13a6ec6be79ff7d Mon Sep 17 00:00:00 2001 From: Stefan van Zwam Date: Wed, 18 Jun 2014 14:09:53 -0400 Subject: [PATCH 277/546] Documentation and doctest fixes --- src/sage/matroids/matroid.pyx | 112 +++++++++--------- src/sage/matroids/matroids_plot_helpers.py | 131 +++++++++++---------- 2 files changed, 120 insertions(+), 123 deletions(-) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index e7dc0a98e34..04b4bb7ccc4 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -113,6 +113,10 @@ additional functionality (e.g. linear extensions). - Invariants - :meth:`tutte_polynomial() ` - :meth:`flat_cover() ` + +- Visualization + - :meth:`show() ` + - :meth:`plot() ` In addition to these, all methods provided by :class:`SageObject ` are available, @@ -4752,31 +4756,31 @@ cdef class Matroid(SageObject): cpdef plot(self, B=None, lineorders=None, pos_method=None,pos_dict=None,save_pos=False): """ Return geometric representation as a sage graphics object. - + INPUT: - - - ``B`` -- (optional) a list containing elements of the groundset not in any particular order. - If internal point placement is used, these elements will be placed as vertices of a triangle. + + - ``B`` -- (optional) a list containing a basis. + If internal point placement is used, these elements will be placed as vertices of a triangle. - ``lineorders`` -- (optional) A list of lists where each of the inner lists - specify ground set elements in a certain order which will be used to draw the - corresponding line in geometric representation (if it exists). + specify ground set elements in a certain order which will be used to draw the + corresponding line in geometric representation (if it exists). - ``pos_method`` -- An integer specifying positioning method ``0``: default positioning ``1``: use pos_dict if it is not ``None`` - ``2``: Force directed (Not yet implemented). + ``2``: Force directed (Not yet implemented). - ``pos_dict``: A dictionary mapping ground set elements to their (x,y) positions. - - ``save_pos``: A boolean indicating that point placements (either internal or user provided) and - line orders (if provided) will be cached in the matroid (``M._cached_info``) and can be used for - reproducing the geometric representation during the same session - - + - ``save_pos``: A boolean indicating that point placements (either internal or user provided) and + line orders (if provided) will be cached in the matroid (``M._cached_info``) and can be used for + reproducing the geometric representation during the same session + + OUTPUT: - - A sage graphics object of type that + + A sage graphics object of type that corresponds to the geometric representation of the matroid - + EXAMPLES:: - + sage: M=matroids.named_matroids.Fano() sage: G=M.plot() sage: type(G) @@ -4785,21 +4789,21 @@ cdef class Matroid(SageObject): """ import matroids_plot_helpers - if pos_method == 1 and pos_dict!=None: + if pos_method == 1 and pos_dict != None: # check sanity of pos_dict and add it to cached info if sane - if matroids_plot_helpers.posdict_is_sane(self,pos_dict) ==True: - self._cached_info={'positions':pos_dict,'lineorders':lineorders} + if matroids_plot_helpers.posdict_is_sane(self, pos_dict) == True: + self._cached_info={'positions':pos_dict, 'lineorders':lineorders} # placeholder for aditional placement methods. Only need to compute positions and update self._cached_info elif pos_method == 2: raise NotImplementedError - + if self._cached_info == None: self._cached_info={'positions':None,'lineorders': None} if 'positions' not in self._cached_info.keys(): self._cached_info['positions'] = None if 'lineorders' not in self._cached_info.keys(): self._cached_info['lineorders'] = None - + if self.rank() > 3: return elif B == None: @@ -4812,31 +4816,31 @@ cdef class Matroid(SageObject): cpdef show(self,B=None,lineorders=None,pos_method=None,pos_dict=None,save_pos=False,lims=None): """ Show the geometric representation of the matroid. - + INPUT: - + - ``B`` -- (optional) a list containing elements of the groundset not in any particular order. If internal point placement is used, these elements will be placed as vertices of a triangle. - - ``lineorders`` -- (optional) A list of lists where each of the inner lists + - ``lineorders`` -- (optional) A list of lists where each of the inner lists specify ground set elements in a certain order which will be used to draw the corresponding line in geometric representation (if it exists). - - ``pos_method`` -- An integer specifying positioning method + - ``pos_method`` -- An integer specifying positioning method ``0``: default positioning ``1``: use pos_dict if it is not ``None`` - ``2``: Force directed (Not yet implemented). + ``2``: Force directed (Not yet implemented). - ``pos_dict`` -- A dictionary mapping ground set elements to their (x,y) positions. - - ``save_pos`` -- A boolean indicating that point placements (either internal or user provided) and - line orders (if provided) will be cached in the matroid (``M._cached_info``) and can be used for + - ``save_pos`` -- A boolean indicating that point placements (either internal or user provided) and + line orders (if provided) will be cached in the matroid (``M._cached_info``) and can be used for reproducing the geometric representation during the same session - ``lims`` -- A list of 4 elements ``[xmin,xmax,ymin,ymax]`` - + EXAMPLES:: - + sage: M=matroids.named_matroids.TernaryDowling3() sage: M.show(B=['a','b','c']) sage: M.show(B=['a','b','c'],lineorders=[['f','e','i']]) - sage: M.show(pos_method=1, pos_dict=pos,lims=[-3,3,-3,3]) - + sage: pos = {'a':(0,0), 'b': (0,1), 'c':(1,0), 'd':(1,1), 'e':(1,-1), 'f':(-1,1), 'g':(-1,-1),'h':(2,0), 'i':(0,2)} + sage: M.show(pos_method=1, pos_dict=pos,lims=[-3,3,-3,3]) """ if self.rank() > 3: raise NotImplementedError @@ -4848,27 +4852,27 @@ cdef class Matroid(SageObject): lineorders1=lineorders pm=pos_method pd=pos_dict - sp=save_pos + sp=save_pos G=self.plot(B1,lineorders1,pm,pd,sp) if lims == None: G.show() else: G.show(xmin=lims[0], xmax=lims[1], ymin=lims[2], ymax=lims[3]) - return - + return + cpdef _fix_positions(self,pos_dict=None,lineorders=None): """ Cache point positions and line orders without actually plotting - + INPUT: - + - ``pos_dict`` -- (optional) A dictionary mapping ground set elements to their (x,y) positions. - - ``lineorders`` -- (optional) A list of lists where each of the inner lists - specify ground set elements in a certain order which will be used to draw the - corresponding line in geometric representation (if it exists). - + - ``lineorders`` -- (optional) A list of lists where each of the inner lists + specify ground set elements in a certain order which will be used to draw the + corresponding line in geometric representation (if it exists). + EXAMPLES:: - + sage: M=matroids.named_matroids.BetsyRoss() sage: pos={} sage: s="abcde" @@ -4878,28 +4882,18 @@ cdef class Matroid(SageObject): sage: for i in range(5): ....: pos[s[i]]=(RR(x*sin(2*pi*i/5)), RR(x*cos(2*pi*i/5))) ....: pos[t[i]]=(RR(y*sin(2*pi*(i+1/2)/5)), RR(y*cos(2*pi*(i+1/2)/5))) - ....: + ....: sage: pos['k']=(0,0) sage: M._fix_positions(pos_dict=pos) - sage: M._cached_info - {'lineorders': None, - 'positions': {'a': (0.000000000000000, 1.61000000000000), - 'b': (1.53120099123520, 0.497517360943665), - 'c': (0.946334256190882, -1.30251736094367), - 'd': (-0.946334256190882, -1.30251736094367), - 'e': (-1.53120099123520, 0.497517360943665), - 'f': (0.365084007635076, 0.502495027562079), - 'g': (0.590718333102580, -0.191936021350899), - 'h': (0.000000000000000, -0.621118012422360), - 'i': (-0.590718333102580, -0.191936021350899), - 'j': (-0.365084007635077, 0.502495027562079), - 'k': (0, 0)}} - + sage: M._cached_info['lineorders'] is None + True + sage: M._cached_info['positions']['k'] + (0, 0) """ # check sanity of pos_dict and add it to cached info if sane if(pos_dict!=None): import matroids_plot_helpers - if matroids_plot_helpers.posdict_is_sane(self,pos_dict) ==True: + if matroids_plot_helpers.posdict_is_sane(self,pos_dict) ==True: self._cached_info={'positions':pos_dict,'lineorders':lineorders} return - + diff --git a/src/sage/matroids/matroids_plot_helpers.py b/src/sage/matroids/matroids_plot_helpers.py index 9041c697c81..c01bc704b77 100644 --- a/src/sage/matroids/matroids_plot_helpers.py +++ b/src/sage/matroids/matroids_plot_helpers.py @@ -48,11 +48,12 @@ sage: from sage.matroids import matroids_plot_helpers sage: M1=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1,0,1], - [0, 1, 0, 1, 0, 1, 1,0,0,1,0],[0, 0, 1, 1, 1, 0, 1,0,0,0,0]]) + ....: [0, 1, 0, 1, 0, 1, 1,0,0,1,0], [0, 0, 1, 1, 1, 0, 1,0,0,0,0]]) sage: pos_dict= {0: (0, 0), 1: (2, 0), 2: (1, 2), 3: (1.5, 1.0), - 4: (0.5, 1.0), 5: (1.0, 0.0), 6: (1.0, 0.6666666666666666)} - sage: M1._cached_info={'positions':pos_dict,'lineorders':None} - sage: matroids_plot_helpers.geomrep(M1,sp=True) + ....: 4: (0.5, 1.0), 5: (1.0, 0.0), 6: (1.0, 0.6666666666666666), + ....: 7: (3,3), 8: (4,0), 9: (-1,1), 10: (-2,-2)} + sage: M1._cached_info={'positions': pos_dict, 'lineorders': None} + sage: matroids_plot_helpers.geomrep(M1, sp=True) """ # ***************************************************************************** @@ -82,26 +83,26 @@ def it(M, B1, nB1, lps): - ``M`` -- A matroid. - ``B1``-- A list of groundset elements of ``M`` that corresponds to a - basis of matroid ``M``. + basis of matroid ``M``. - ``nB1``-- A list of elements in the ground set of M that corresponds to - ``M.simplify.groundset() \ B1``. + ``M.simplify.groundset() \ B1``. - ``lps``-- A list of elements in the ground set of matroid M that are - loops. + loops. OUTPUT: A tuple containing 4 elements in this order: 1. A dictionary containing 2-tuple (x,y) co-ordinates with - ``M.simplify.groundset()`` elements that can be placed on the sides of - the triangle as keys. + ``M.simplify.groundset()`` elements that can be placed on the sides of + the triangle as keys. 2. A list of 3 lists of elements of ``M.simplify.groundset()`` that can - be placed on the 3 sides of the triangle. + be placed on the 3 sides of the triangle. 3. A list of elements of `M.simplify.groundset()`` that cane be placed - inside the triangle in the geometric representation. + inside the triangle in the geometric representation. 4. A list of lists of elements of ``M.simplify.groundset()`` that - correspond to lines in the geometric representation other than the sides - of the triangle. + correspond to lines in the geometric representation other than the sides + of the triangle. EXAMPLES:: @@ -180,15 +181,15 @@ def trigrid(tripts): INPUT: - ``tripts`` -- A list of 3 lists of the form [x,y] where x and y are the - cartesian co-ordinates of a point. + cartesian co-ordinates of a point. OUTPUT: A list of lists containing 4 points in following order: - 1. Barycenter of 3 input points. - 2,3,4. Barycenters of 1. with 3 different 2-subsets of input points - respectively. + - 1. Barycenter of 3 input points. + - 2,3,4. Barycenters of 1. with 3 different 2-subsets of input points + respectively. EXAMPLES:: @@ -225,11 +226,11 @@ def addnontripts(tripts_labels, nontripts_labels, ptsdict): INPUT: - ``tripts`` -- A list of 3 ground set elements that are to be placed on - vertices of the triangle. + vertices of the triangle. - ``ptsdict`` -- A dictionary (at least) containing ground set elements in - ``tripts`` as keys and their (x,y) position as values. + ``tripts`` as keys and their (x,y) position as values. - ``nontripts``-- A list of ground set elements whose corresponding points - are to be placed inside the triangle. + are to be placed inside the triangle. OUTPUT: @@ -240,23 +241,22 @@ def addnontripts(tripts_labels, nontripts_labels, ptsdict): EXAMPLES:: sage: from sage.matroids import matroids_plot_helpers + sage: from sage.matroids.advanced import setprint sage: ptsdict={'a':(0,0),'b':(1,2),'c':(2,0)} sage: ptsdict_1=matroids_plot_helpers.addnontripts(['a','b','c'], ....: ['d','e','f'],ptsdict) - sage: print ptsdict_1 - sage: sage: print ptsdict_1 - {'a': (0, 0), 'c': (2, 0), 'b': (1, 2), - 'e': (0.6666666666666666, 0.8888888888888888), - 'd': (1.0, 0.6666666666666666), - 'f': (1.3333333333333333, 0.8888888888888888)} + sage: setprint(ptsdict_1) + {'a': [0, 0], 'b': [1, 2], 'c': [0, 2], 'd': [0.6666666666666666, 1.0], + 'e': [0.6666666666666666, 0.8888888888888888], + 'f': [0.8888888888888888, 1.3333333333333333]} sage: ptsdict_2=matroids_plot_helpers.addnontripts(['a','b','c'], ....: ['d','e','f','g','h'],ptsdict) - sage: print ptsdict_2 - {'a': (0, 0), 'c': (2, 0), 'b': (1, 2), - 'e': (0.6666666666666666, 0.8888888888888888), - 'd': (1.0, 0.6666666666666666), 'g': (1.0, 0.2222222222222222), - 'f': (1.3333333333333333, 0.8888888888888888), - 'h': (0.5555555555555555, 0.5185185185185185)} + sage: setprint(ptsdict_2) + {'a': [0, 0], 'b': [1, 2], 'c': [0, 2], 'd': [0.6666666666666666, 1.0], + 'e': [0.6666666666666666, 0.8888888888888888], + 'f': [0.8888888888888888, 1.3333333333333333], + 'g': [0.2222222222222222, 1.0], + 'h': [0.5185185185185185, 0.5555555555555555]} .. NOTE:: @@ -294,28 +294,28 @@ def createline(ptsdict, ll, lineorders2=None): INPUT: - ``ptsdict`` -- A dictionary containing keys and their (x,y) position as - values. + values. - ``ll`` -- A list of keys in ``ptsdict`` through which a line is to be - drawn. + drawn. - ``lineorders2``-- (optional) A list of ordered lists of keys in - ``ptsdict`` such that if ll is setwise same as any of these then points - corresponding to values of the keys will be traversed in that order thus - overriding internal order deciding heuristic. + ``ptsdict`` such that if ll is setwise same as any of these then points + corresponding to values of the keys will be traversed in that order thus + overriding internal order deciding heuristic. OUTPUT: A tuple containing 4 elements in this order: 1. Ordered list of x-coordinates of values of keys in ``ll`` specified in - ptsdict. + ptsdict. 2. Ordered list of y-coordinates of values of keys in ``ll`` specified - in ptsdict. + in ptsdict. 3. Ordered list of interpolated x-coordinates of points through which a - line can be drawn. + line can be drawn. 4. Ordered list of interpolated y-coordinates of points through which a - line can be drawn. + line can be drawn. - Examples:: + EXAMPLES:: sage: from sage.matroids import matroids_plot_helpers sage: ptsdict={'a':(1,3),'b':(2,1),'c':(4,5),'d':(5,2)} @@ -379,11 +379,11 @@ def slp(M1, pos_dict=None, B=None): - ``M1`` -- A matroid. - ``pos_dict`` -- (optional) A dictionary containing non loopy elements of - ``M`` as keys and their (x,y) positions. - as keys. While simplifying the matroid, all except one element in a - parallel class that is also specified in ``pos_dict`` will be retained. + ``M`` as keys and their (x,y) positions. + as keys. While simplifying the matroid, all except one element in a + parallel class that is also specified in ``pos_dict`` will be retained. - ``B`` -- (optional) A basis of M1 that has been chosen for placement on - vertics of triangle. + vertics of triangle. OUTPUT: @@ -392,17 +392,18 @@ def slp(M1, pos_dict=None, B=None): 1. Simple matroid corresponding to ``M1``. 2. Loops of matroid ``M1``. 3. Elements that are in `M1.groundset()` but not in ground set of 1 or - in 2 + in 2 EXAMPLES:: sage: from sage.matroids import matroids_plot_helpers + sage: from sage.matroids.advanced import setprint sage: M1=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1,0,1], ....: [0, 1, 0, 1, 0, 1, 1,0,0,1,0],[0, 0, 1, 1, 1, 0, 1,0,0,0,0]]) sage: [M,L,P]=matroids_plot_helpers.slp(M1) sage: M.is_simple() True - sage: [L,P] + sage: setprint([L,P]) [{7}, {8, 9, 10}] sage: M1=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1,0,1], ....: [0, 1, 0, 1, 0, 1, 1,0,0,1,0],[0, 0, 1, 1, 1, 0, 1,0,0,0,0]]) @@ -411,7 +412,7 @@ def slp(M1, pos_dict=None, B=None): sage: [M,L,P]=matroids_plot_helpers.slp(M1,pos_dict=posdict) sage: M.is_simple() True - sage: [L,P] + sage: setprint([L,P]) [{7}, {0, 9, 10}] .. NOTE:: @@ -458,13 +459,13 @@ def addlp(M, M1, L, P, ptsdict, G=None, limits=None): - ``M`` -- A matroid. - ``L`` -- List of elements in ``M.groundset()`` that are loops of matroid - ``M``. + ``M``. - ``P`` -- List of elements in ``M.groundset()`` not in - ``M.simplify.groundset()`` or ``L``. + ``M.simplify.groundset()`` or ``L``. - ``ptsdict`` -- A dictionary containing elements in ``M.groundset()`` not - necessarily containing elements of ``L``. + necessarily containing elements of ``L``. - ``G`` -- (optional) A sage graphics object to which loops and parallel - elements of matroid `M` added . + elements of matroid `M` added . - ``limits``-- (optional) Current axes limits [xmin,xmax,ymin,ymax]. OUTPUT: @@ -472,10 +473,10 @@ def addlp(M, M1, L, P, ptsdict, G=None, limits=None): A 2-tuple containing: 1. A sage graphics object containing loops and parallel elements of - matroid ``M`` + matroid ``M`` 2. axes limits array - EXAMPLES: + EXAMPLES:: sage: from sage.matroids import matroids_plot_helpers sage: M=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1], @@ -554,15 +555,17 @@ def line_hasorder(l, lodrs=None): - ``l`` -- A line specified as a list of ground set elements. - ``lordrs`` -- (optional) A list of lists each specifying an order on - a subset of ground set elements that may or may not correspond to a - line in geometric representation. + a subset of ground set elements that may or may not correspond to a + line in geometric representation. + OUTPUT: A tuple containing 2 elements in this order: + 1. A boolean indicating whether there is any list in ``lordrs`` that is - setwise equal to ``l``. + setwise equal to ``l``. 2. A list specifying an order on ``set(l)`` if 1. is True, otherwise - an empty list. + an empty list. EXAMPLES:: @@ -570,7 +573,7 @@ def line_hasorder(l, lodrs=None): sage: matroids_plot_helpers.line_hasorder(['a','b','c','d'], ....: [['a','c','d','b'],['p','q','r']]) (True, ['a', 'c', 'd', 'b']) - sage: sage: matroids_plot_helpers.line_hasorder(['a','b','c','d'], + sage: matroids_plot_helpers.line_hasorder(['a','b','c','d'], ....: [['p','q','r'],['l','m','n','o']]) (False, []) @@ -594,9 +597,9 @@ def lineorders_union(lineorders1, lineorders2): INPUT: - ``lineorders1`` -- A list of ordered lists specifying orders on subsets - of ground set. + of ground set. - ``lineorders2`` -- A list of ordered lists specifying orders subsets of - ground set. + ground set. OUTPUT: @@ -636,14 +639,14 @@ def posdict_is_sane(M1, pos_dict): - ``M1`` -- A matroid. - ``posdict`` -- A dictionary mapping ground set elements to (x,y) - positions. + positions. OUTPUT: A boolean that is ``True`` if posdict indeed has all the required elements to plot the geometric elements, otherwise ``False``. - EXAMPLES: + EXAMPLES:: sage: from sage.matroids import matroids_plot_helpers sage: M1=Matroid(ring=GF(2), matrix=[[1, 0, 0, 0, 1, 1, 1,0,1,0,1], From fcbbef2ab0053f186c4303feec27bec138dde60f Mon Sep 17 00:00:00 2001 From: Stefan van Zwam Date: Wed, 18 Jun 2014 14:46:28 -0400 Subject: [PATCH 278/546] Fixed docbuild errors --- src/sage/matroids/matroid.pyx | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index 04b4bb7ccc4..69ed887a997 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -4764,16 +4764,16 @@ cdef class Matroid(SageObject): - ``lineorders`` -- (optional) A list of lists where each of the inner lists specify ground set elements in a certain order which will be used to draw the corresponding line in geometric representation (if it exists). - - ``pos_method`` -- An integer specifying positioning method - ``0``: default positioning - ``1``: use pos_dict if it is not ``None`` - ``2``: Force directed (Not yet implemented). + - ``pos_method`` -- An integer specifying positioning method. + - ``0``: default positioning + - ``1``: use pos_dict if it is not ``None`` + - ``2``: Force directed (Not yet implemented). + - ``pos_dict``: A dictionary mapping ground set elements to their (x,y) positions. - ``save_pos``: A boolean indicating that point placements (either internal or user provided) and line orders (if provided) will be cached in the matroid (``M._cached_info``) and can be used for reproducing the geometric representation during the same session - OUTPUT: A sage graphics object of type that @@ -4820,18 +4820,19 @@ cdef class Matroid(SageObject): INPUT: - ``B`` -- (optional) a list containing elements of the groundset not in any particular order. - If internal point placement is used, these elements will be placed as vertices of a triangle. + If internal point placement is used, these elements will be placed as vertices of a triangle. - ``lineorders`` -- (optional) A list of lists where each of the inner lists - specify ground set elements in a certain order which will be used to draw the - corresponding line in geometric representation (if it exists). + specify ground set elements in a certain order which will be used to draw the + corresponding line in geometric representation (if it exists). - ``pos_method`` -- An integer specifying positioning method - ``0``: default positioning - ``1``: use pos_dict if it is not ``None`` - ``2``: Force directed (Not yet implemented). + - ``0``: default positioning + - ``1``: use pos_dict if it is not ``None`` + - ``2``: Force directed (Not yet implemented). + - ``pos_dict`` -- A dictionary mapping ground set elements to their (x,y) positions. - ``save_pos`` -- A boolean indicating that point placements (either internal or user provided) and - line orders (if provided) will be cached in the matroid (``M._cached_info``) and can be used for - reproducing the geometric representation during the same session + line orders (if provided) will be cached in the matroid (``M._cached_info``) and can be used for + reproducing the geometric representation during the same session - ``lims`` -- A list of 4 elements ``[xmin,xmax,ymin,ymax]`` EXAMPLES:: From 94deba276c175bdd157091d56249122bcbafc8d0 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 18 Jun 2014 16:47:09 -0700 Subject: [PATCH 279/546] Various changes, fixed, additions, and improvements to Lie theory. --- .../lie/affine_finite_crystals.rst | 31 +- .../lie/affine_hw_crystals.rst | 46 ++- .../thematic_tutorials/lie/bibliography.rst | 23 +- .../lie/branching_rules.rst | 73 ++-- .../en/thematic_tutorials/lie/crystals.rst | 112 ++--- .../lie/iwahori_hecke_algebra.rst | 26 +- .../lie/kazhdan_lusztig_polynomials.rst | 2 +- .../en/thematic_tutorials/lie/lie_basics.rst | 385 +++++++++++------- .../en/thematic_tutorials/lie/weight_ring.rst | 20 +- .../en/thematic_tutorials/lie/weyl_groups.rst | 16 +- .../media/hyperbolic_La0.png | Bin 0 -> 31423 bytes 11 files changed, 464 insertions(+), 270 deletions(-) create mode 100644 src/doc/en/thematic_tutorials/media/hyperbolic_La0.png diff --git a/src/doc/en/thematic_tutorials/lie/affine_finite_crystals.rst b/src/doc/en/thematic_tutorials/lie/affine_finite_crystals.rst index ee56d5293b4..f770d7ec45e 100644 --- a/src/doc/en/thematic_tutorials/lie/affine_finite_crystals.rst +++ b/src/doc/en/thematic_tutorials/lie/affine_finite_crystals.rst @@ -120,7 +120,7 @@ In Sage this can be obtained via:: sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) sage: G = K.digraph() - sage: view(G, pdflatex=True, tightpage=True) # optional - dot2tex graphviz + sage: view(G, tightpage=True) # optional - dot2tex graphviz Types `D_n^{(1)}`, `B_n^{(1)}`, `A_{2n-1}^{(2)}` @@ -168,7 +168,8 @@ only holds in the ranges `1\le r\le n-2` for type `D_n^{(1)}`, and sage: K = crystals.KirillovReshetikhin(['D',6,1],4,2) sage: K.classical_decomposition() - The crystal of tableaux of type ['D', 6] and shape(s) [[], [1, 1], [1, 1, 1, 1], [2, 2], [2, 2, 1, 1], [2, 2, 2, 2]] + The crystal of tableaux of type ['D', 6] and shape(s) + [[], [1, 1], [1, 1, 1, 1], [2, 2], [2, 2, 1, 1], [2, 2, 2, 2]] For type `B_n^{(1)}` and `r=n`, one needs to be aware that `\omega_n` is a spin weight and hence corresponds in the partition language to a @@ -471,6 +472,32 @@ crystal are labelled by tuples which specify their nonzero `\phi_i(b)` and :align: center +Single column KR crystals +------------------------- + +A single column KR crystal is `B^{r,1}` for any `r \in I_0`. + +In [LNSSS14I]_ and [LNSSS14II]_, it was shown that single column KR +crystals can be constructed by projecting level 0 crystals (using LS paths). +We first vertify that we do get an isomorphic crystal for `B^{1,1}` +in type `E_6^{(1)}`:: + + sage: K = crystals.KirillovReshetikhin(['E',6,1], 1,1) + sage: K2 = crystals.kirillov_reshetikhin.LSPaths(['E',6,1], 1,1) + sage: K.digraph().is_isomorphic(K2.digraph(), edge_labels=True) + True + +Here is an example in `E_8^{(1)}` and we calculate its +classical decomposition:: + + sage: K = crystals.kirillov_reshetikhin.LSPaths(['E',8,1], 8,1) + sage: K.cardinality() + 249 + sage: L = [x for x in K if x.is_highest_weight([1,2,3,4,5,6,7,8])] + sage: map(lambda x: x.weight(), L) + [-2*Lambda[0] + Lambda[8], 0] + + Applications ------------ diff --git a/src/doc/en/thematic_tutorials/lie/affine_hw_crystals.rst b/src/doc/en/thematic_tutorials/lie/affine_hw_crystals.rst index e7bb2a66d2c..ed395683318 100644 --- a/src/doc/en/thematic_tutorials/lie/affine_hw_crystals.rst +++ b/src/doc/en/thematic_tutorials/lie/affine_hw_crystals.rst @@ -45,11 +45,6 @@ associated digraph:: :scale: 50 :align: center -REFERENCES: - -.. [L1995] P. Littelmann. *Paths and root operators in representation theory*. - Ann. of Math. (2) 142 (1995), no. 3, 499-525. - The Littelmann path model also lends itself as a model for level zero crystals which are bi-infinite. To cut out a slice of these crystals, one can use the direction option in subcrystal:: @@ -67,3 +62,44 @@ can use the direction option in subcrystal:: :scale: 50 :align: center +Modified Nakajima monomials +--------------------------- + +Modified Nakajima monomials have also been implemented in Sage and models +highest weight crystals in all symmetrizable types. The elements are given +in terms of commuting variables `Y_i(n)` where `i \in I` and +`n \in \ZZ_{\geq 0}`. For more information on the modified Nakajima +monomials, see [KKS2007]_. + +We give an example in affine type and verify that up to depth 3, it agrees +with the Littelmann path model:: + + sage: La = RootSystem(['C',3,1]).weight_space().fundamental_weights() + sage: LS = crystals.LSPaths(2*La[1]+La[2]) + sage: SL = LS.subcrystal(max_depth=3) + sage: GL = LS.digraph(subset=SL) + + sage: La = RootSystem(['C',3,1]).weight_lattice().fundamental_weights() + sage: M = crystals.NakajimaMonomials(['C',3,1], 2*La[1]+La[2]) + sage: SM = M.subcrystal(max_depth=3) + sage: GM = M.digraph(subset=SM) + sage: GL.is_isomorphic(GM, edge_labels=True) + True + +Now we do an example of a simply-laced (so symmetrizable) hyperbolic +type `H_1^{(4)}`, which comes from the complete graph on 4 vertices:: + + sage: CM = CartanMatrix([[2, -1, -1,-1],[-1,2,-1,-1],[-1,-1,2,-1],[-1,-1,-1,2]]); M + [ 2 -1 -1 -1] + [-1 2 -1 -1] + [-1 -1 2 -1] + [-1 -1 -1 2] + sage: La = RootSystem(CM).weight_lattice().fundamental_weights() + sage: M = crystals.NakajimaMonomials(CM, La[0]) + sage: SM = M.subcrystal(max_depth=4) + sage: GM = M.digraph(subset=SM) # long time + +.. image:: ../media/hyperbolic_La0.png + :scale: 20 + :align: center + diff --git a/src/doc/en/thematic_tutorials/lie/bibliography.rst b/src/doc/en/thematic_tutorials/lie/bibliography.rst index 2e27e4a37fd..0af8551304b 100644 --- a/src/doc/en/thematic_tutorials/lie/bibliography.rst +++ b/src/doc/en/thematic_tutorials/lie/bibliography.rst @@ -2,6 +2,9 @@ Bibliography ============ +.. [Bourbaki46] Nicolas Bourbaki. *Lie Groups and Lie Algebras: Chapters 4-6*. + Springer, reprint edition, 1998. + .. [BumpNakasuji2010] D. Bump and M. Nakasuji. Casselman's basis of Iwahori vectors and the Bruhat order. arXiv:1002.2996, http://arxiv.org/abs/1002.2996. @@ -66,6 +69,9 @@ Bibliography Affine structures and a tableau model for E_6 crystals *J. Algebra*, 324:2512-2542, 2010 +.. [Kac] Victor G. Kac. *Infinite Dimensional Lie algebras* + Cambridge University Press, third edition, 1994. + .. [Kashiwara1995] M. Kashiwara. On crystal bases. Representations of groups (Banff, AB, 1994), 155--197, CMS Conference Proceedings, 16, American Mathematical Society, Providence, RI, 1995. @@ -90,10 +96,25 @@ Bibliography Affine crystals and vertex models. *Int. J. Mod. Phys.* A 7 (suppl. 1A): 449--484, 1992. +.. [LNSSS14I] C. Lenart, S. Naito, D. Sagaki, A. Schilling, and M. Shimozono. + A uniform model for for Kirillov-Reshetikhin crystals I: Lifting the + parabolic quantum Bruhat graph. (2014) :arxiv:`1211.2042` + +.. [LNSSS14II] C. Lenart, S. Naito, D. Sagaki, A. Schilling, and M. Shimozono. + A uniform model for for Kirillov-Reshetikhin crystals II: Alcove model, + path model, and `P = X`. (2014) :arxiv:`1402.2203` + +.. [L1995] P. Littelmann. *Paths and root operators in representation theory*. + Ann. of Math. (2) 142 (1995), no. 3, 499-525. + .. [McKayPatera1981] W. G. McKay and J. Patera. *Tables of Dimensions, Indices and Branching Rules for Representations of Simple Lie Algebras*. Marcel Dekker, 1981. +.. [KKS2007] S.-J. Kang, J.-A. Kim, and D.-U. Shin. + *Modified Nakajima monomials and the crystal* `B(\infty)`. + J. Algebra, **308** (2007), 524-535. + .. [OkadoSchilling2008] M. Okado, A.Schilling. Existence of crystal bases for Kirillov--Reshetikhin crystals for nonexceptional types. *Representation Theory* 12:186--207, 2008. @@ -118,4 +139,4 @@ Bibliography J. Algebra 122 (1989), no. 2, 299–322. .. [Testerman1992] Testerman, Donna M. The construction of the maximal A1's in - the exceptional algebraic groups. Proc. Amer. Math. Soc. 116 (1992), no. 3, 635–644. \ No newline at end of file + the exceptional algebraic groups. Proc. Amer. Math. Soc. 116 (1992), no. 3, 635–644. diff --git a/src/doc/en/thematic_tutorials/lie/branching_rules.rst b/src/doc/en/thematic_tutorials/lie/branching_rules.rst index ddca38c6668..96eedbfb939 100644 --- a/src/doc/en/thematic_tutorials/lie/branching_rules.rst +++ b/src/doc/en/thematic_tutorials/lie/branching_rules.rst @@ -1,5 +1,7 @@ .. linkall +.. _branch_rules: + ------------------------------------- Maximal Subgroups and Branching Rules ------------------------------------- @@ -54,7 +56,7 @@ the branching rule that we may create as follows:: The name "symmetric" of this branching rule will be explained further later, but it means that `Sp(4)` is the fixed subgroup of an involution of `Sl(4)`. -Here ``A3`` and ``C2`` are the Cartan Types of the groups +Here ``A3`` and ``C2`` are the Cartan types of the groups `G=SL(4)` and `H=Sp(4)`. Now we may see how representations of `SL(4)` decompose @@ -89,7 +91,7 @@ factors through `Sp(4)\times Sp(4)`, while the other factors through `SL(4)`. To check that the embeddings are not conjugate, we branch a (randomly chosen) representation. Observe that we do not have to build the intermediate -WeylCharacterRings. +:class:`Weyl character rings `. :: @@ -182,7 +184,7 @@ Maximal subgroups Sage has a database of maximal subgroups for every simple Cartan type of rank `\le 8`. You may access this with the -``maximal_subgroups`` method of the WeylCharacter Ring:: +``maximal_subgroups`` method of the Weyl character ring:: sage: E7=WeylCharacterRing("E7",style="coroots") sage: E7.maximal_subgroups() @@ -250,6 +252,8 @@ rule, we compose the given one with this automorphism:: sage: b1=branching_rule("E6","E6","automorphic")*b; b1 composite branching rule E6 => (automorphic) E6 => (miscellaneous) A2xG2 +.. _levi_branch_rules: + Levi subgroups -------------- @@ -268,9 +272,9 @@ For example, here is the A3 Dynkin diagram: 1 2 3 A3 -We see that we may remove the node 3 and obtain A2, or the node 2 and -obtain A1xA1. These correspond to the Levi subgroups `GL(3)` and -`GL(2) \times GL(2)` of `GL(4)`. +We see that we may remove the node 3 and obtain `A_2`, or the node 2 +and obtain `A_1 \times A_1`. These correspond to the Levi subgroups +`GL(3)` and `GL(2) \times GL(2)` of `GL(4)`. Let us construct the irreducible representations of `GL(4)` and branch them down to these down to @@ -315,11 +319,12 @@ versus SL(4) \to SL(2) \times SL(2). -Consider the representation `A3(0,1,0)`, which is the six dimensional exterior -square. In the coroot notation, the restriction contained two copies of the -trivial representation, ``2*A1xA1(0,0)``. The other way, we had instead three -distinct representations in the restriction, namely ``A1xA1(1,1,0,0)`` and -``A1xA1(0,0,1,1)``, that is, `\det \otimes 1` and `1 \otimes \det`. +Consider the representation ``A3(0,1,0)``, which is the six dimensional +exterior square. In the coroot notation, the restriction contained two +copies of the trivial representation, ``2*A1xA1(0,0)``. The other way, +we had instead three distinct representations in the restriction, namely +``A1xA1(1,1,0,0)`` and ``A1xA1(0,0,1,1)``, that is, +`\det \otimes 1` and `1 \otimes \det`. The Levi subgroup ``A1xA1`` is actually not maximal. Indeed, we may factor the embedding: @@ -342,8 +347,8 @@ we could accomplish the branching in two steps, thus:: [A1xA1(1,0) + A1xA1(0,1), 2*A1xA1(0,0) + A1xA1(1,1), A1xA1(1,0) + A1xA1(0,1)] As you can see, we've redone the branching rather circuitously this -way, making use of the branching rules ``A3->C2`` and ``B2->D2``, and -two accidental isomorphisms ``C2=B2`` and ``D2=A1xA1``. It is much +way, making use of the branching rules ``A3 -> C2`` and ``B2 -> D2``, and +two accidental isomorphisms ``C2 = B2`` and ``D2 = A1xA1``. It is much easier to go in one step using ``rule="levi"``, but reassuring that we get the same answer! @@ -360,10 +365,10 @@ diagram that we obtain the Dynkin diagram of a subgroup. For example:: 1 2 0 G2~ -Observe that by removing the 1 node that we obtain an A2 Dynkin -diagram. Therefore the exceptional group G2 contains a copy of -`SL(3)`. We branch the two representations of G2 corresponding to the -fundamental weights to this copy of A2:: +Observe that by removing the 1 node that we obtain an `A_2` Dynkin +diagram. Therefore the exceptional group `G_2` contains a copy of +`SL(3)`. We branch the two representations of `G_2` corresponding to the +fundamental weights to this copy of `A_2`:: sage: G2 = WeylCharacterRing("G2", style="coroots") sage: A2 = WeylCharacterRing("A2", style="coroots") @@ -372,7 +377,7 @@ fundamental weights to this copy of A2:: sage: [G2(f).branch(A2, rule="extended") for f in G2.fundamental_weights()] [A2(0,0) + A2(0,1) + A2(1,0), A2(0,1) + A2(1,0) + A2(1,1)] -The two representations of G2, of degrees 7 and 14 respectively, are +The two representations of `G_2`, of degrees 7 and 14 respectively, are the action on the octonions of trace zero and the adjoint representation. @@ -410,7 +415,8 @@ handled as follows:: sage: branching_rule("D4","B3",rule="symmetric") symmetric branching rule D4 => B3 -If `G = \hbox{SO}(r+s)` then `G` has a subgroup `\hbox{SO}(r) \times \hbox{SO}(s)`. This +If `G = \hbox{SO}(r+s)` then `G` has a subgroup +`\hbox{SO}(r) \times \hbox{SO}(s)`. This lifts to an embedding of the universal covering groups .. MATH:: @@ -468,7 +474,7 @@ Similarly we have embeddings:: ['D',k] x ['B',l] --> ['B',k+l] These are also of extended type. For example consider the embedding of -``D3xB2->B5``. Here is the ``B5`` extended Dynkin diagram:: +``D3xB2 -> B5``. Here is the ``B5`` extended Dynkin diagram:: O 0 | @@ -517,7 +523,7 @@ may or may not be implemented in Sage. However if it is not implemented, it may be constructed as a composition of two branching rules. -For example, prior to Sage-6.1 ``branching_rule("E6","A5","levi") +For example, prior to Sage-6.1 ``branching_rule("E6","A5","levi")`` returned a not-implemented error and the advice to branch to ``A5xA1``. And we can see from the extended Dynkin diagram of `E_6` that indeed `A_5` is not a maximal subgroup, since removing node 2 @@ -556,7 +562,7 @@ construct the branching rule to `A_5` we may proceed as follows:: For more detailed information use verbose=True -Note that it is not necessary to construct the WeylCharacterRing +Note that it is not necessary to construct the Weyl character ring for the intermediate group ``A5xA1``. This last example illustrates another common problem: @@ -655,7 +661,7 @@ fall between the cracks. Mostly these involve maximal subgroups of fairly small rank. The rule ``rule="plethysm"`` is a powerful rule that includes any -branching rule from types A, B, C or D as a special case. Thus it +branching rule from types `A`, `B`, `C` or `D` as a special case. Thus it could be used in place of the above rules and would give the same results. However, it is most useful when branching from `G` to a maximal subgroup `H` such that `rank(H) < rank(G)-1`. @@ -681,7 +687,7 @@ homomorphism by invoking its character, to be called ``chi``:: This confirms that the character has degree 6 and is symplectic, so it corresponds to a homomorphism `SL(2) \to Sp(6)`, and there is a -corresponding branching rule ``C3 => A1``:: +corresponding branching rule ``C3 -> A1``:: sage: A1 = WeylCharacterRing("A1", style="coroots") sage: C3 = WeylCharacterRing("C3", style="coroots") @@ -768,10 +774,10 @@ the other hand, the assumption of characteristic `p` is not important for Theorems G.1 and A.1, which describe the torus embeddings, hence contain enough information to compute the branching rule. There -are other ways of embedding ``G_2`` or ``A_2`` into -``E_6``. These may embeddings be characterized by the +are other ways of embedding `G_2` or `A_2` into +`E_6`. These may embeddings be characterized by the condition that the two 27-dimensional representations of -``E_6`` restrict irreducibly to ``G_2`` or ``A_2``. +`E_6` restrict irreducibly to `G_2` or `A_2`. Their images are maximal subgroups. The remaining rules come about as follows. Let `G` be @@ -786,7 +792,7 @@ of subgroups Then the centralizer of `H` is `A_1`, `A_2`, `C_3`, `F_4` (if `H=G_2`) or `A_1` (if `G=E_7` and `H=F_4`). This gives us five of the cases. -Regarding the branching rule ``E_6\to G_2\times A_2``, Rubenthaler +Regarding the branching rule `E_6 \to G_2 \times A_2`, Rubenthaler [Rubenthaler2008]_ describes the embedding and applies it in an interesting way. @@ -902,13 +908,13 @@ you can write your own branching rules. As an example, let us consider how to implement the branching rule ``A3 -> C2``. Here ``H = C2 = Sp(4)`` embedded as a subgroup in -``A3 = GL(4).`` The Cartan subalgebra `\hbox{Lie}(U)` consists of +``A3 = GL(4)``. The Cartan subalgebra `\hbox{Lie}(U)` consists of diagonal matrices with eigenvalues ``u1, u2, -u2, -u1``. Then ``C2.space()`` is the two dimensional vector spaces consisting of the linear functionals ``u1`` and ``u2`` on ``U``. On the other hand `Lie(T) = \mathbf{R}^4`. A convenient way to see the restriction is to think of it as the adjoint of the map ``[u1,u2] -> [u1,u2,-u2,-u1]``, -that is, ``[x0,x1,x2,x3] -> [x0-x3,x1-x2].`` Hence we may encode the +that is, ``[x0,x1,x2,x3] -> [x0-x3,x1-x2]``. Hence we may encode the rule:: def brule(x): @@ -929,7 +935,7 @@ Let us check that this agrees with the built-in rule:: C2(0,0) + C2(1,1) Although this works, it is better to make the rule -into an element of the BranchingRule class, as follows. +into an element of the :class:`BranchingRule` class, as follows. :: @@ -956,7 +962,7 @@ effect may be computed using the branching rule code:: sage: A4(1,0,1,0).branch(A4,rule="automorphic") A4(0,1,0,1) -In the special case where `G=D4`, the Dynkin diagram has +In the special case where ``G=D4``, the Dynkin diagram has extra symmetries, and these correspond to outer automorphisms of the group. These are implemented as the ``"triality"`` branching rule:: @@ -1003,9 +1009,10 @@ permuted by triality:: D4(0,0,0,1) By contrast, ``rule="automorphic"`` simply interchanges the two -spin representations, as it always does in Type D:: +spin representations, as it always does in type `D`:: sage: D4(0,0,0,1).branch(D4,rule="automorphic") D4(0,0,1,0) sage: D4(0,0,1,0).branch(D4,rule="automorphic") D4(0,0,0,1) + diff --git a/src/doc/en/thematic_tutorials/lie/crystals.rst b/src/doc/en/thematic_tutorials/lie/crystals.rst index 7591d78afa8..a9c2f25858b 100644 --- a/src/doc/en/thematic_tutorials/lie/crystals.rst +++ b/src/doc/en/thematic_tutorials/lie/crystals.rst @@ -2,6 +2,11 @@ Classical Crystals ================== +A classical crystal is one coming from the finite (classical) types +`A_r, B_r, C_r, D_r, E_{6,7,8}, F_4`, and `G_2`. Here we +describe some background before going into the general theory of crystals +and the type dependent combinatorics. + Tableaux and representations of `GL(n)` --------------------------------------- @@ -22,8 +27,8 @@ columns are strictly increasing. Thus :scale: 75 :align: center -is a semistandard Young tableau. Sage has a Tableau class, and you may -create this tableau as follows:: +is a semistandard Young tableau. Sage has a :class:`Tableau` class, +and you may create this tableau as follows:: sage: T = Tableau([[1,2,2], [2,3]]); T [[1, 2, 2], [2, 3]] @@ -43,9 +48,9 @@ with `\lambda = (-1, \dots, -1)`, which is a dominant weight but not a partition, and the character is not a polynomial function on `\hbox{Mat}_n(\mathbf{C})`. -**Theorem** (Littlewood) If `\lambda` is a partition, then the number -of Semi-Standard Young Tableaux with shape `\lambda` and entries in -`{1,2,\dots,r+1}` is the dimension of `\pi_\lambda`. +**Theorem** [Littlewood] If `\lambda` is a partition, then the number +of semi-standard Young tableaux with shape `\lambda` and entries in +`\{1,2,\ldots,r+1\}` is the dimension of `\pi_\lambda`. For example, if `\lambda = (3,2)` and `r = 2`, then we find 15 tableaux with shape `\lambda` and entries in `\{1,2,3\}`: @@ -76,7 +81,7 @@ in the tableaux. Then the multiplicity of `\mu` in the character \chi_\lambda(\mathbf{z}) = \sum_T \mathbf{z}^{\hbox{wt}(T)} where the sum is over all semi-standard Young tableaux of shape -`\lambda` that have entries in `{1,2,\dots,r+1}`. +`\lambda` that have entries in `\{1, 2, \ldots, r+1\}`. Frobenius-Schur Duality @@ -119,7 +124,7 @@ Let us say that a semistandard Young tableau `T` of shape `\lambda\vdash k` is *standard* if `T` contains each entry `1,2,\dots,k` exactly once. Thus both rows and columns are strictly increasing. -**Theorem** (Young, 1927) The degree of `\pi_\lambda` is the number of +**Theorem** [Young, 1927] The degree of `\pi_\lambda` is the number of standard tableaux of shape `\lambda`. Now let us consider the implications of Frobenius-Schur duality. @@ -196,8 +201,8 @@ Both the combinatorial fact and the decomposition of `\mathbf{C}[S_k]` show that the number of pairs of standard tableaux of size `k` and the same shape equals `k!`. -- The third combinatorial fact is analogous to the decomposition of the ring of polynomial - functions on `\hbox{Mat}(n, \mathbf{C})` on which +- The third combinatorial fact is analogous to the decomposition of the + ring of polynomial functions on `\hbox{Mat}(n, \mathbf{C})` on which `GL(n, \mathbf{C}) \times GL(n, \mathbf{C})` acts by `(g_1, g_2)f(X) = f({^t g_1}X g_2)`. The polynomial ring decomposes into the direct sum of @@ -212,8 +217,8 @@ Taking traces gives the *Cauchy identity*: where `x_i` are the eigenvalues of `g_1` and `y_j` are the eigenvalues of `g_2`. The sum is over all partitions `\lambda`. -- This is analogous to the decomposition of the exterior algebra over - `\hbox{Mat}(n, \mathbf{C})`. +- The last combinatorial fact is analogous to the decomposition of + the exterior algebra over `\hbox{Mat}(n, \mathbf{C})`. Taking traces gives the *dual Cauchy identity*: @@ -256,17 +261,14 @@ a limiting structure can still be detected. This is the Kashiwara's crystal bases have a combinatorial structure that sheds light even on purely combinatorial constructions on tableaux that predated quantum groups. It gives a good generalization to other -Cartan types. - -We will not make the most general definition of a crystal. See the -references for a more general definition. Let `\Lambda` be the weight -lattice of a classical Cartan type. +Cartan types (or more generally to Kac-Moody algebras). +Let `\Lambda` be the weight lattice of a Cartan type with root system `\Phi`. We now define a *crystal* of type `\Phi`. Let `\mathcal{B}` be a set, and let `0 \notin \mathcal{B}` be an auxiliary element. For each index `1 \le i \le r` we assume there given maps `e_i, f_i : \mathcal{B} \longrightarrow \mathcal{B} \cup \{0\}`, maps -`\varepsilon_i, \phi_i : \mathcal{B} \longrightarrow \mathbf{Z}` and a +`\varepsilon_i, \varphi_i : \mathcal{B} \longrightarrow \mathbf{Z}` and a map `\hbox{wt} : \mathcal{B} \longrightarrow \Lambda` satisfying certain assumptions, which we now describe. It is assumed that if `x, y \in \mathcal{B}` then `e_i (x) = y` if and only if @@ -277,13 +279,13 @@ certain assumptions, which we now describe. It is assumed that if \hbox{wt} (y) = \hbox{wt} (x) + \alpha_i, \qquad \varepsilon_i (x) = \varepsilon_i (y) + 1, - \qquad \phi_i (x) = \phi_i (y) - 1. + \qquad \varphi_i (x) = \varphi_i (y) - 1. Moreover, we assume that .. MATH:: - \phi_i (x) - \varepsilon_i (x) + \varphi_i (x) - \varepsilon_i (x) = \left\langle \hbox{wt} (x), \alpha^{\vee}_i \right\rangle @@ -292,9 +294,9 @@ for all `x \in \mathcal{B}`. We call a crystal *regular* if it satisfies the additional assumption that `\varepsilon_i(v)` is the number of times that `e_i` may be applied to `v`, and that `\phi_i(v)` is the number of times that `f_i` may be applied. That is, -`\phi_i (x) = \max \{k | f_i^k x \neq 0\}` and `\varepsilon_i (x) = \max \{k -| e_i^k (x) \neq 0\}`. -Kashiwara also allows `\varepsilon_i` and `\phi_i` to take the value `-\infty`. +`\varphi_i (x) = \max \{k | f_i^k x \neq 0\}` and `\varepsilon_i (x) = +\max \{k | e_i^k (x) \neq 0\}`. Kashiwara also allows `\varepsilon_i` +and `\varphi_i` to take the value `-\infty`. .. NOTE:: @@ -314,7 +316,8 @@ the irreducible character with highest weight `\lambda`, as in :ref:`representations`. The crystal `\mathcal{B}_\lambda` is not uniquely characterized by the -properties that we have stated so far. For Cartan types `A, D, E` it may +properties that we have stated so far. For Cartan types `A, D, E` +(more generally, any simply-laced type) it may be characterized by these properties together with certain other *Stembridge axioms*. We will take it for granted that there is a unique "correct" crystal `\mathcal{B}_\lambda` and discuss how these @@ -446,7 +449,7 @@ For each of the classical Cartan types there is a *standard crystal* up by taking tensor products and extracting constituent irreducible crystals. This procedure is sufficient for Cartan types `A_r` and `C_r`. For types `B_r` and `D_r` the standard crystal must be -supplemented with a *spin crystal*. See [KashiwaraNakashima1994]_ or +supplemented with *spin crystals*. See [KashiwaraNakashima1994]_ or [HongKang2002]_ for further details. Here is the standard crystal of type `A_r`. @@ -455,7 +458,7 @@ Here is the standard crystal of type `A_r`. :scale: 75 :align: center -You may create the crystal, and work with it as follows:: +You may create the crystal and work with it as follows:: sage: C = crystals.Letters("A6") sage: v0 = C.highest_weight_vector(); v0 @@ -479,7 +482,7 @@ There is, additionally, a spin crystal for `B_r`, corresponding to the `2^r`-dimensional spin representation. We will not draw it, but we will describe it. Its elements are vectors `\epsilon_1\otimes\cdots\otimes\epsilon_r`, where each ``spin`` -`\epsilon_i=\pm 1`. +`\epsilon_i=\pm`. If `i \varepsilon_i (y)$},\\ - x \otimes f_i (y) & \text{if $\phi_i (x) \le \varepsilon_i (y)$}, + f_i (x) \otimes y & \text{if $\varphi_i (x) > \varepsilon_i (y)$},\\ + x \otimes f_i (y) & \text{if $\varphi_i (x) \le \varepsilon_i (y)$}, \end{cases} and @@ -596,23 +598,24 @@ and e_i (x \otimes y) = \begin{cases} - e_i (x) \otimes y & \text{if $\phi_i (x) \ge \varepsilon_i (y)$},\\ - x \otimes e_i (y) & \text{if $\phi_i (x) < \varepsilon_i (y)$}. + e_i (x) \otimes y & \text{if $\varphi_i (x) \ge \varepsilon_i (y)$},\\ + x \otimes e_i (y) & \text{if $\varphi_i (x) < \varepsilon_i (y)$}. \end{cases} It is understood that `x \otimes 0 = 0 \otimes x = 0`. We also define: .. MATH:: - \phi_i (x \otimes y) + \varphi_i (x \otimes y) = - \max (\phi_i (y), \phi_i (x) + \phi_i (y) - \varepsilon_i (y)), + \max (\varphi_i (y), \varphi_i (x) + \varphi_i (y) - \varepsilon_i (y)), .. MATH:: \varepsilon_i (x \otimes y) = - \max (\varepsilon_i (x), \varepsilon_i (x) + \varepsilon_i (y) - \phi_i (x)) . + \max (\varepsilon_i (x), \varepsilon_i (x) + \varepsilon_i (y) + - \varphi_i (x)) . Alternative definition @@ -629,8 +632,8 @@ we denote the ordered pair `(y, x)` with `y \in \mathcal{B}` and f_i (x \otimes y) = \begin{cases} - f_i (x) \otimes y & \text{if $\phi_i (y) \le \varepsilon_i (x)$},\\ - x \otimes f_i (y) & \text{if $\phi_i (y) > \varepsilon_i (x)$}, + f_i (x) \otimes y & \text{if $\varphi_i (y) \le \varepsilon_i (x)$},\\ + x \otimes f_i (y) & \text{if $\varphi_i (y) > \varepsilon_i (x)$}, \end{cases} and @@ -640,23 +643,24 @@ and e_i (x \otimes y) = \begin{cases} - e_i (x) \otimes y & \text{if $\phi_i (y) < \varepsilon_i (x)$},\\ - x \otimes e_i (y) & \text{if $\phi_i (y) \ge \varepsilon_i (x)$}. + e_i (x) \otimes y & \text{if $\varphi_i (y) < \varepsilon_i (x)$},\\ + x \otimes e_i (y) & \text{if $\varphi_i (y) \ge \varepsilon_i (x)$}. \end{cases} It is understood that `y \otimes 0 = 0 \otimes y = 0`. We also define .. MATH:: - \phi_i (x \otimes y) + \varphi_i (x \otimes y) = - \max (\phi_i (x), \phi_i (y) + \phi_i (x) - \varepsilon_i (x)), + \max (\varphi_i (x), \varphi_i (y) + \varphi_i (x) - \varepsilon_i (x)), .. MATH:: \varepsilon_i (x \otimes y) = - \max (\varepsilon_i (y), \varepsilon_i (y) + \varepsilon_i (x) - \phi_i (y)). + \max (\varepsilon_i (y), \varepsilon_i (y) + \varepsilon_i (x) + - \varphi_i (y)). The tensor product is associative: `(x \otimes y) \otimes z \mapsto x \otimes(y \otimes z)` is an @@ -675,6 +679,13 @@ tensor product construction is *a priori* asymmetrical, both constructions produce isomorphic crystals, and in particular Sage's crystals of tableaux are identical to Kashiwara's. +.. NOTE:: + + Using abstract crystals (i.e. they satisfy the axioms but do not arise + from a representation of `U_q(\mathfrak{g})`), we can construct crystals + `\mathcal{B}, \mathcal{C}` such that `\mathcal{B} \otimes \mathcal{C} + \neq \mathcal{C} \otimes \mathcal{B}` (of course, using the + same convention). Tensor products of crystals in Sage ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -751,8 +762,8 @@ Crystals of tableaux as tensor products of crystals Sage implements the :class:`~sage.combinat.crystals.tensor_product.CrystalOfTableaux` as a subcrystal of a tensor product of the -:class:`~sage.combinat.crystals.letters.CrystalOfLetters`. You can see how -its done as follows:: +:class:`~sage.combinat.crystals.letters.ClassicalCrystalOfLetters`. +You can see how its done as follows:: sage: T = crystals.Tableaux("A4",shape=[3,2]) sage: v = T.highest_weight_vector().f(1).f(2).f(3).f(2).f(1).f(4).f(2).f(3); v @@ -762,8 +773,8 @@ its done as follows:: We've looked at the internal representation of `v`, where it is represented as an element of the fourth tensor power of the -:class:`~sage.combinat.crystals.letters.CrystalOfLetters`. We see -that the tableau: +:class:`~sage.combinat.crystals.letters.ClassicalCrystalOfLetters`. +We see that the tableau: .. MATH:: @@ -959,6 +970,9 @@ We see that the type `D_4` crystal indeed decomposes into two type `A_3` compone :scale: 75 :align: center +For more on branching rules, see :ref:`branch_rules` or +:ref:`levi_branch_rules` for specifics on the Levi subgroups. + Subcrystals ----------- diff --git a/src/doc/en/thematic_tutorials/lie/iwahori_hecke_algebra.rst b/src/doc/en/thematic_tutorials/lie/iwahori_hecke_algebra.rst index 3e559be46be..4083f029d66 100644 --- a/src/doc/en/thematic_tutorials/lie/iwahori_hecke_algebra.rst +++ b/src/doc/en/thematic_tutorials/lie/iwahori_hecke_algebra.rst @@ -2,13 +2,13 @@ Iwahori Hecke Algebras ---------------------- -The Iwahori Hecke algebra is defined in [Iwahori1964]_. In that -original paper, the algebra occurs as the convolution ring of -functions on a `p`-adic group that are compactly supported and -invariant both left and right by the Iwahori subgroup. However Iwahori -determined its structure in terms of generators and relations, and it -turns out to be a deformation of the group algebra of the affine Weyl -group. +The :class:`Iwahori Hecke algebra ` is defined +in [Iwahori1964]_. In that original paper, the algebra occurs as the +convolution ring of functions on a `p`-adic group that are compactly +supported and invariant both left and right by the Iwahori subgroup. +However Iwahori determined its structure in terms of generators and +relations, and it turns out to be a deformation of the group algebra +of the affine Weyl group. Once the presentation is found, the Iwahori Hecke algebra can be defined for any Coxeter group. It depends on a parameter `q` which in @@ -48,13 +48,14 @@ varieties, where they are used in the definition of the Kazhdan-Lusztig polynomials. They appear in connection with quantum groups, and in Jones's original paper on the Jones polynomial. -Here is how to create an Iwahori Hecke algebra:: +Here is how to create an Iwahori Hecke algebra (in the `T` basis):: sage: R. = PolynomialRing(ZZ) - sage: H = IwahoriHeckeAlgebra("B3",q).T(); H + sage: H = IwahoriHeckeAlgebra("B3",q) + sage: T = H.T(); T Iwahori-Hecke algebra of type B3 in q,-1 over Univariate Polynomial Ring in q over Integer Ring in the T-basis - sage: T1,T2,T3 = H.algebra_generators() + sage: T1,T2,T3 = T.algebra_generators() sage: T1*T1 (q-1)*T[1] + q @@ -66,7 +67,8 @@ You may coerce a Weyl group element into the Iwahori Hecke algebra:: sage: W = WeylGroup("G2",prefix="s") sage: [s1,s2] = W.simple_reflections() sage: P. = LaurentPolynomialRing(QQ) - sage: H = IwahoriHeckeAlgebra(W,q).T() - sage: H(s1*s2) + sage: H = IwahoriHeckeAlgebra("B3",q) + sage: T = H.T() + sage: T(s1*s2) T[1,2] diff --git a/src/doc/en/thematic_tutorials/lie/kazhdan_lusztig_polynomials.rst b/src/doc/en/thematic_tutorials/lie/kazhdan_lusztig_polynomials.rst index 691d22e62e6..020a4c8f976 100644 --- a/src/doc/en/thematic_tutorials/lie/kazhdan_lusztig_polynomials.rst +++ b/src/doc/en/thematic_tutorials/lie/kazhdan_lusztig_polynomials.rst @@ -19,7 +19,7 @@ Kazhdan-Lusztig polynomials as follows:: sage: KL.P(s2, s2*s1*s3*s2) 1 + q -Thus we have the Kazhdan-Lusztig R and P polynomials. +Thus we have the Kazhdan-Lusztig `R` and `P` polynomials. Known algorithms for computing Kazhdan-Lusztig polynomials are highly recursive, and caching of intermediate results is necessary for the diff --git a/src/doc/en/thematic_tutorials/lie/lie_basics.rst b/src/doc/en/thematic_tutorials/lie/lie_basics.rst index 80983fde8b3..1e60a4126b0 100644 --- a/src/doc/en/thematic_tutorials/lie/lie_basics.rst +++ b/src/doc/en/thematic_tutorials/lie/lie_basics.rst @@ -7,15 +7,15 @@ Goals of this section --------------------- Since we must be brief here, this is not really a place to learn about -Lie groups. Rather, the point of this section is to outline what you -need to know to use Sage effectively for Lie computations, and to fix -ideas and notations. +Lie groups or Lie algebras. Rather, the point of this section is to outline +what you need to know to use Sage effectively for Lie computations, and +to fix ideas and notations. Semisimple and reductive groups ------------------------------- -If `g \in GL(n,\mathbf{C})`, then `g` may be uniquely factored as +If `g \in GL(n,\CC)`, then `g` may be uniquely factored as `g_1 g_2` where `g_1` and `g_2` commute, with `g_1` semisimple (diagonalizable) and `g_2` unipotent (all its eigenvalues equal to 1). This follows from the Jordan canonical form. If `g = g_1` then `g` @@ -32,13 +32,13 @@ the notion of being semisimple or unipotent is intrinsic. Examples: - Complex analytic groups with analytic representations -- Algebraic groups over `\mathbf{R}` with algebraic representations. +- Algebraic groups over `\RR` with algebraic representations. A subgroup of `G` is called *unipotent* if it is connected and all its elements are unipotent. It is called a *torus* if it is connected, abelian, and all its elements are semisimple. The group `G` is called *reductive* if it has no nontrivial normal unipotent subgroup. For -example, `GL(2,\mathbf{C})` is reductive, but its subgroup: +example, `GL(2,\CC)` is reductive, but its subgroup: .. MATH:: @@ -65,7 +65,7 @@ A group has a unique largest normal unipotent subgroup, called the radical is trivial. A Lie group is called *semisimple* it is reductive and furthermore has -no nontrivial normal tori. For example `GL(2,\mathbf{C})` is reductive +no nontrivial normal tori. For example `GL(2,\CC)` is reductive but not semisimple because it has a normal torus: .. MATH:: @@ -77,7 +77,7 @@ but not semisimple because it has a normal torus: \end{array} \right)\right\}. -The group `SL(2,\mathbf{C})` is semisimple. +The group `SL(2,\CC)` is semisimple. Fundamental group and center @@ -103,7 +103,7 @@ maximal normal unipotent subgroup or *unipotent radical* `P` and a reductive subgroup `M`, which is determined up to conjugacy. The subgroup `M` is called a *Levi subgroup*. -**Example:** Let `G = GL_n(\mathbf{C})` and let `r_1, \dots, r_k` be +**Example:** Let `G = GL_n(\CC)` and let `r_1, \ldots, r_k` be integers whose sum is `n`. Then we may consider matrices of the form: .. MATH:: @@ -115,7 +115,7 @@ integers whose sum is `n`. Then we may consider matrices of the form: &&& g_r \end{array}\right) -where `g_i \in GL(r_i,\mathbf{C}`. The unipotent radical consists of +where `g_i \in GL(r_i,\CC)`. The unipotent radical consists of the subgroup in which all `g_i = I_{r_i}`. The Levi subgroup (determined up to conjugacy) is: @@ -133,7 +133,7 @@ the subgroup in which all `g_i = I_{r_i}`. The Levi subgroup \right)\right\}, and is isomorphic to -`M = GL(r_1,\mathbf{C}) \times \cdots \times GL(r_k,\mathbf{C})`. +`M = GL(r_1,\CC) \times \cdots \times GL(r_k,\CC)`. Therefore `M` is a Levi subgroup. The notion of a Levi subgroup can be extended to compact Lie @@ -148,12 +148,12 @@ Cartan types Semisimple Lie groups are classified by their *Cartan types*. There are both reducible and irreducible Cartan types in Sage. Let us start with the irreducible types. Such a type is implemented in Sage as a -pair ``['X',r]`` where 'X' is one of A, B, C, D, E, F or G and `r` is a +pair ``['X', r]`` where 'X' is one of A, B, C, D, E, F or G and `r` is a positive integer. If 'X' is 'D' then we must have `r > 1` and if 'X' is one of the *exceptional types* 'E', 'F' or 'G' then `r` is limited to only a few possibilities. The exceptional types are:: - ['G',2], ['F',4], ['E',6], ['E',7] or ['E',8]. + ['G', 2], ['F', 4], ['E', 6], ['E', 7] or ['E', 8]. A simply-connected semisimple group is a direct product of simple Lie groups, which are given by the following table. The integer `r` is @@ -164,13 +164,13 @@ Here are the Lie groups corresponding to the classical types: +---------------+-------------------------+-------------+ | compact group | complex analytic group | Cartan type | +===============+=========================+=============+ -| `SU(r+1)` | `SL(r+1,\mathbf{C})` | `A_r` | +| `SU(r+1)` | `SL(r+1,\CC)` | `A_r` | +---------------+-------------------------+-------------+ -| `spin(2r+1)` | `spin(2r+1,\mathbf{C})` | `B_r` | +| `spin(2r+1)` | `spin(2r+1,\CC)` | `B_r` | +---------------+-------------------------+-------------+ -| `Sp(2r)` | `Sp(2r,\mathbf{C})` | `C_r` | +| `Sp(2r)` | `Sp(2r,\CC)` | `C_r` | +---------------+-------------------------+-------------+ -| `spin(2r)` | `spin(2r,\mathbf{C})` | `D_r` | +| `spin(2r)` | `spin(2r,\CC)` | `D_r` | +---------------+-------------------------+-------------+ You may create these Cartan types and their Dynkin diagrams as follows:: @@ -181,6 +181,7 @@ You may create these Cartan types and their Dynkin diagrams as follows:: Here ``"D5"`` is an abbreviation for ``['D',5]``. The group `spin(n)` is the simply-connected double cover of the orthogonal group `SO(n)`. + Dual Cartan types ------------------ @@ -189,8 +190,8 @@ Every Cartan type has a dual, which you can get from within Sage:: sage: CartanType("B4").dual() ['C', 4] -Types other than ``B`` and ``C`` are self-dual in the sense that the -dual is isomorphic to the original type; however the isomorphism of a +Types other than `B_r` and `C_r` for `r > 2` are self-dual in the sense that +the dual is isomorphic to the original type; however the isomorphism of a Cartan type with its dual might relabel the vertices. We can see this as follows:: @@ -210,7 +211,7 @@ Reducible Cartan types ---------------------- If `G` is a Lie group of finite index in `G_1 \times G_2`, where `G_1` -and `G_2` are Lie groups of dimension `>0`, then `G` is called +and `G_2` are Lie groups of positive dimension, then `G` is called *reducible*. In this case, the root system of `G` is the disjoint union of the root systems of `G_1` and `G_2`, which lie in orthogonal subspaces of the ambient space of the weight space of `G`. The Cartan @@ -232,18 +233,18 @@ There are some isomorphisms that occur in low degree. +-------------+------------+-----------------+---------------------+ | Cartan Type | Group | Equivalent Type | Isomorphic Group | +=============+============+=================+=====================+ -| B2 | `spin(5)` | C2 | `Sp(4)` | +| `B_2` | `spin(5)` | `C_2` | `Sp(4)` | +-------------+------------+-----------------+---------------------+ -| D3 | `spin(6)` | A3 | `SL(4)` | +| `D_3` | `spin(6)` | `A_3` | `SL(4)` | +-------------+------------+-----------------+---------------------+ -| D2 | `spin(4)` | A1xA1 | `SL(2)\times SL(2)` | +| `D_2` | `spin(4)` | `A1 \times A_1` | `SL(2)\times SL(2)` | +-------------+------------+-----------------+---------------------+ -| B1 | `spin(3)` | A1 | `SL(2)` | +| `B_1` | `spin(3)` | `A_1` | `SL(2)` | +-------------+------------+-----------------+---------------------+ -| C1 | `Sp(2)` | A1 | `SL(2)` | +| `C_1` | `Sp(2)` | `A_1` | `SL(2)` | +-------------+------------+-----------------+---------------------+ -Sometimes the redundant Cartan types such as D3 and D2 are excluded +Sometimes the redundant Cartan types such as `D_3` and `D_2` are excluded from the list of Cartan types. However Sage allows them since excluding them leads to exceptions having to be made in algorithms. A better approach, which is followed by Sage, is to allow the redundant Cartan types, but to implement @@ -260,53 +261,14 @@ consider it to be `SL(2)`, `spin(2)` or `Sp(2)`:: Finite family {1: (2)} -Affine Cartan types -------------------- - -There are also affine Cartan types, which correspond to (infinite) -affine Lie algebras. There is an affine Cartan type of the of the -form ``[`X`,r,1]`` if ``X=A,B,C,D,E,F,G`` and ``[`X`,r]`` is an ordinary -Cartan type. There are also *twisted affine types* of the form ``[X,r,k]`` -where `k = 2` or 3 if the Dynkin diagram of the ordinary Cartan type -``[X,r]`` has an automorphism of degree `k`. - -Illustrating some of the methods available for the untwisted affine -Cartan type ``['A',4,1]``:: - - sage: ct = CartanType(['A',4,1]); ct - ['A', 4, 1] - sage: ct.dual() - ['A', 4, 1] - sage: ct.classical() - ['A', 4] - sage: ct.dynkin_diagram() - 0 - O-----------+ - | | - | | - O---O---O---O - 1 2 3 4 - A4~ - -The twisted affine Cartan types are relabeling of the duals of certain -untwisted Cartan types:: - - sage: CartanType(['A',3,2]) - ['B', 2, 1]^* - sage: CartanType(['D',4,3]) - ['G', 2, 1]^* relabelled by {0: 0, 1: 2, 2: 1} - - Relabeled Cartan types ---------------------- -By default Sage uses the labeling of the Dynkin Diagram from Bourbaki, -*Lie Groups and Lie Algebras* Chapters 4,5,6. There is another -labeling of the vertices due to Dynkin. Most of the literature follows -Bourbaki, though Kac's book *Infinite Dimensional Lie algebras* -follows Dynkin. +By default Sage uses the labeling of the Dynkin diagram from [Bourbaki46]_. +There is another labeling of the vertices due to Dynkin. +Most of the literature follows [Bourbaki46]_, though [Kac]_ follows Dynkin. -If you need to use Dynkin's labeling you should be aware that Sage +If you need to use Dynkin's labeling, you should be aware that Sage does support relabeled Cartan types. See the documentation in ``sage.combinat.root_system.type_relabel`` for further information. @@ -316,16 +278,16 @@ does support relabeled Cartan types. See the documentation in Standard realizations of the ambient spaces ------------------------------------------- -These realizations follow the Appendix in Bourbaki, *Lie Groups and -Lie Algebras, Chapters 4-6*. See the :ref:`Root system plot tutorial -` for how to visualize them. +These realizations follow the Appendix in [Bourbaki46]_. See the +:ref:`Root system plot tutorial ` +for how to visualize them. Type A ^^^^^^ For type `A_r` we use an `r+1` dimensional ambient space. This means -that we are modeling the Lie group `U(r+1)` or `GL(r+1,\mathbf{C})` -rather than `SU(r+1)` or `SL(r+1,\mathbf{C})`. The ambient space is +that we are modeling the Lie group `U(r+1)` or `GL(r+1,\CC)` +rather than `SU(r+1)` or `SL(r+1,\CC)`. The ambient space is identified with `\mathbf{Q}^{r+1}`:: sage: RootSystem("A3").ambient_space().simple_roots() @@ -404,6 +366,10 @@ The dominant weights consist of `r`-tuples of integers `\lambda = (\lambda_1,\dots,\lambda_{r+1})` such that `\lambda_1 \ge \cdots \ge \lambda_{r-1} \ge |\lambda_r|`. + +Exceptional Types +^^^^^^^^^^^^^^^^^ + We leave the reader to examine the exceptional types. You can use Sage to list the fundamental dominant weights and simple roots. @@ -413,11 +379,11 @@ Weights and the ambient space Let `G` be a reductive complex analytic group. Let `T` be a maximal torus, `\Lambda = X^{\ast} (T)` be its group of analytic -characters. Then `T \cong (\mathbf{C}^{\times})^r` for some `r` and -`\Lambda \cong \mathbf{Z}^r`. +characters. Then `T \cong (\CC^{\times})^r` for some `r` and +`\Lambda \cong \ZZ^r`. -**Example 1:** Let `G = \hbox{GL}_{r+1} (\mathbf{C})`. Then `T` is the -diagonal subgroup and `X^{\ast} (T) \cong \mathbf{Z}^{r+1}`. If +**Example 1:** Let `G = \hbox{GL}_{r+1} (\CC)`. Then `T` is the +diagonal subgroup and `X^{\ast} (T) \cong \ZZ^{r+1}`. If `\lambda = (\lambda_1, \dots, \lambda_n)` then `\lambda` is identified with the rational character @@ -432,11 +398,11 @@ with the rational character \end{array}\right) \longmapsto \prod t_i^{\lambda_i}. -**Example 2:** Let `G = \hbox{SL}_{r+1} (\mathbf{C})`. Again `T` is +**Example 2:** Let `G = \hbox{SL}_{r+1} (\CC)`. Again `T` is the diagonal subgroup but now if -`\lambda \in \mathbf{Z}^{\Delta} = \{(d, \cdots, d) | d \in \mathbf{Z}\} \subseteq \mathbf{Z}^{r+1}` +`\lambda \in \ZZ^{\Delta} = \{(d, \cdots, d) | d \in \ZZ\} \subseteq \ZZ^{r+1}` then `\prod t_i^{\lambda_i} = \det ({\bf t})^d = 1`, so -`X^{\ast} (T) \cong \mathbf{Z}^{r+1} /\mathbf{Z}^{\Delta} \cong \mathbf{Z}^r`. +`X^{\ast} (T) \cong \ZZ^{r+1} /\ZZ^{\Delta} \cong \ZZ^r`. - Elements of `\Lambda` are called *weights*. @@ -448,15 +414,15 @@ then `\prod t_i^{\lambda_i} = \det ({\bf t})^d = 1`, so - The nonzero weights of the adjoint representation are called *roots*. -- The *ambient space* of `\Lambda` is `\mathbf{Q} \otimes \Lambda`. +- The *ambient space* of `\Lambda` is `\QQ \otimes \Lambda`. The root system --------------- As we have mentioned, `G` acts on its complexified Lie algebra -`\mathfrak{g}_{\mathbf{C}}` by the adjoint representation. The zero -weight space `\mathfrak{g}_{\mathbf{C}}(0)` is just the Lie algebra of +`\mathfrak{g}_{\CC}` by the adjoint representation. The zero +weight space `\mathfrak{g}_{\CC}(0)` is just the Lie algebra of `T` itself. The other nonzero weights each appear with multiplicity one and form an interesting configuration of vectors called the *root system* `\Phi`. @@ -464,9 +430,9 @@ one and form an interesting configuration of vectors called the It is convenient to partition `\Phi` into two sets `\Phi^+` and `\Phi^-` such that `\Phi^+` consists of all roots lying on one side of a hyperplane. Often we arrange things so that `G` is embedded in -`GL(n,\mathbf{C})` in such a way that the positive weights correspond +`GL(n,\CC)` in such a way that the positive weights correspond to upper triangular matrices. Thus if `\alpha` is a positive root, its -weight space `\mathfrak{g}_{\mathbf{C}}(\alpha)` is spanned by a +weight space `\mathfrak{g}_{\CC}(\alpha)` is spanned by a vector `X_\alpha`, and the exponential of this eigenspace in `G` is a one-parameter subgroup of unipotent matrices. It is always possible to arrange that this one-parameter subgroup consists of upper triangular @@ -475,7 +441,7 @@ matrices. If `\alpha` is a positive root that cannot be decomposed as a sum of other positive roots, then `\alpha` is called a *simple root*. If `G` is semisimple of rank `r`, then `r` is the number of positive -roots. Let `\alpha_1,\dots,\alpha_r` be these. +roots. Let `\alpha_1, \ldots, \alpha_r` be these. The Weyl group @@ -485,22 +451,22 @@ Let `G` be a complex analytic group. Let `T` be a maximal torus, and let `N(T)` be its normalizer. Let `W = N(T)/T` be the *Weyl group*. It acts on `T` by conjugation; therefore it acts on the weight lattice `\Lambda` and its ambient space. The ambient space admits an inner -product that is invariant under this action. Let `\left` +product that is invariant under this action. Let `(v | w)` denote this inner product. If `\alpha` is a root let `r_\alpha` denote the reflection in the hyperplane of the ambient space that is perpendicular to `\alpha`. If `\alpha = \alpha_i` is a simple root, then we use the notation `s_i` to denote `r_\alpha`. -Then `s_1,\dots,s_r` generate `W`, which is a *Coxeter group*. This +Then `s_1, \ldots, s_r` generate `W`, which is a *Coxeter group*. This means that it is generated by elements `s_i` of order two and that if `m(i,j)` is the order of `s_i s_j`, then .. MATH:: - W = \left + W = \left\langle s_i \mid s_i^2=1, (s_i s_j)^{m(i,j)} = 1 \right\rangle -is a presentation. An important function `l: W \to \mathbf{Z}` is the -*length* function, where `l(w)` is the length of the shortest +is a presentation. An important function `\ell : W \to \ZZ` is the +*length* function, where `\ell(w)` is the length of the shortest decomposition of `w` into a product of simple reflections. @@ -509,7 +475,7 @@ The dual root system The *coroots* are certain linear functionals on the ambient space that also form a root system. Since the ambient space admits a -`W`-invariant inner product, they may be identified with elements +`W`-invariant inner product `(\ |\ )`, they may be identified with elements of the ambient space itself. Then they are proportional to the roots, though if the roots have different lengths, long roots correspond to short coroots and conversely. The coroot corresponding @@ -517,7 +483,16 @@ to the root `\alpha` is .. MATH:: - \alpha^\vee = \frac{2\alpha}{\left<\alpha,\alpha\right>}. + \alpha^\vee = \frac{2\alpha}{(\alpha | \alpha)}. + +We can also describe the natural pairing between coroots and roots using +this invariant inner product as + +.. MATH:: + + \langle \alpha^{\vee}, \beta \rangle + = + 2 \frac{(\alpha | \beta)}{(\alpha | \alpha)}. The Dynkin diagram @@ -525,7 +500,7 @@ The Dynkin diagram The Dynkin diagram is a graph whose vertices are in bijection with the set simple roots. We connect the vertices corresponding to roots that -are not orthogonal. Usually two such vertices make an angle of +are not orthogonal. Usually two such roots (vertices) make an angle of `2\pi/3`, in which case we connect them with a single bond. Occasionally they may make an angle of `3\pi/4` in which case we connect them with a double bond, or `5\pi/6` in which case we connect @@ -533,10 +508,9 @@ them with a triple bond. If the bond is single, the roots have the same length with respect to the inner product on the ambient space. In the case of a double or triple bond, the two simple roots in questions have different length, and the bond is drawn as an arrow from the long -root to the short root. Only the exceptional group `G_2` has a triple -bond. +root to the short root. Only the exceptional group `G_2` has a triple bond. -There are various ways to get the Dynkin diagram:: +There are various ways to get the Dynkin diagram in Sage:: sage: DynkinDiagram("D5") O 5 @@ -567,58 +541,36 @@ There are various ways to get the Dynkin diagram:: G2 -The affine root and the extended Dynkin diagram ------------------------------------------------ +The Cartan matrix +----------------- -For the extended Dynkin diagram, we add one negative root -`\alpha_0`. This is the root whose negative is the highest weight in -the adjoint representation. Sometimes this is called the -*affine root*. We make the Dynkin diagram as before by measuring the -angles between the roots. This extended Dynkin diagram is useful for -many purposes, such as finding maximal subgroups and for describing -the affine Weyl group. +Consider the natural pairing `\langle\ ,\ \rangle` between coroots and +roots, then the defining matrix of this pairing is called the +*Cartan matrix*. That is to say, the Cartan matrix `A = (a_{ij})_{ij}` +is given by -The extended Dynkin diagram may be obtained as the Dynkin diagram of -the corresponding untwisted affine type:: +.. MATH:: - sage: ct = CartanType("E6"); ct - ['E', 6] - sage: ct.affine() - ['E', 6, 1] - sage: ct.affine() == CartanType(['E',6,1]) - True - sage: ct.affine().dynkin_diagram() - O 0 - | - | - O 2 - | - | - O---O---O---O---O - 1 3 4 5 6 - E6~ + a_{ij} = \langle \alpha_i^{\vee}, \alpha_j \rangle. -The extended Dynkin diagram is also a method of the ``WeylCharacterRing``:: +This uniquely corresponds to a root system/Dynkin diagram/Lie group. - sage: WeylCharacterRing("E7").extended_dynkin_diagram() - O 2 - | - | - O---O---O---O---O---O---O - 0 1 3 4 5 6 7 - E7~ +We note that we have made a convention choice, and the opposite convention +corresponds to taking the transpose of the Cartan matrix. Fundamental weights and the Weyl vector --------------------------------------- -There are certain weights `\omega_1,\dots,\omega_r` that: +There are certain weights `\omega_1, \ldots, \omega_r` that: .. MATH:: - \frac{2\left<\alpha_i,\omega_j\right>}{\left<\alpha_i,\alpha_i\right>} + \langle \omega_j, \alpha_i \rangle + = + 2 \frac{( \alpha_i | \omega_j )}{( \alpha_i | \alpha_i ) } = - \delta(i,j). + \delta_{ij}. If `G` is semisimple then these are uniquely determined, whereas if `G` is reductive but not semisimple we may choose them conveniently. @@ -631,7 +583,7 @@ we make a different choice, then `\rho` is altered by a vector that is orthogonal to all roots. This is a harmless change for many purposes such as the Weyl character formula. -In Sage, this issue arises only for Cartan type A. See :ref:`SLvsGL`. +In Sage, this issue arises only for Cartan type `A_r`. See :ref:`SLvsGL`. .. _representations: @@ -639,23 +591,23 @@ In Sage, this issue arises only for Cartan type A. See :ref:`SLvsGL`. Representations and characters ------------------------------ -Let `\Lambda = X^{\ast} (T)` be the group of rational characters. Then -`\Lambda \cong \mathbf{Z}^r`. +Let `T` be a maximal torus and `\Lambda = X^{\ast} (T)` be the group +of rational characters. Then `\Lambda \cong \ZZ^r`. -- Recall that elements of `\Lambda \cong \mathbf{Z}^r` are called *weights*. +- Recall that elements of `\Lambda \cong \ZZ^r` are called *weights*. - The Weyl group `W = N(T)/T` acts on `T`, hence on `\Lambda` and its ambient space by conjugation. -- The ambient space `\mathbf{Q} \otimes X^{\ast} (T) \cong \mathbf{Q}^r` +- The ambient space `\QQ \otimes X^{\ast} (T) \cong \QQ^r` has a fundamental domain `\mathcal{C}^+` for the Weyl group `W` called the *positive Weyl chamber*. Weights in `\mathcal{C}^+` are called *dominant*. - Then `\mathcal{C}^+` consists of all vectors such that - `\left<\alpha,v\right> \ge 0` for all positive roots `\alpha`. + `(\alpha | v) \geq 0` for all positive roots `\alpha`. -- It is useful to embed `\Lambda` in `\mathbf{R}^r` and consider +- It is useful to embed `\Lambda` in `\RR^r` and consider weights as lattice points. - If `(\pi, V)` is a representation then restricting to `T`, the @@ -707,7 +659,7 @@ Representations: an example :scale: 75 :align: center -In this example, `G = \hbox{SL}(3,\mathbf{C})`. We have drawn the +In this example, `G = \hbox{SL}(3,\CC)`. We have drawn the weights of an irreducible representation with highest weight `\lambda`. The shaded region is `\mathcal{C}^+`. `\lambda` is a dominant weight, and the labeled vertices are the weights with positive multiplicity in @@ -720,8 +672,8 @@ while the six interior weights (with double circles) have `m(\mu) = 2`. Partitions and Schur polynomials -------------------------------- -The considerations of this section are particular to type A. We review -the relationship between characters of `GL(n,\mathbf{C})` and +The considerations of this section are particular to type `A`. We review +the relationship between characters of `GL(n,\CC)` and symmetric function theory. A *partition* `\lambda` is a sequence of descending nonnegative @@ -750,13 +702,13 @@ dominant weight of ``['A',r]`` is a partition of length `\le n`, where `n = r+1`. Let `\lambda` be a dominant weight, and let `\chi_\lambda` be the -character of `GL(n,\mathbf{C})` with highest weight `\lambda`. If `k` +character of `GL(n,\CC)` with highest weight `\lambda`. If `k` is any integer we may consider the weight `\mu = (\lambda_1+k,\dots,\lambda_n+k)` obtained by adding `k` to each entry. Then `\chi_{\mu} = \det^k \otimes \chi_\lambda`. Clearly by choosing `k` large enough, we may make `\mu` effective. -So the characters of irreducible representations of `GL(n,\mathbf{C})` +So the characters of irreducible representations of `GL(n,\CC)` do not all correspond to partitions, but the characters indexed by partitions (effective dominant weights) are enough that we can write any character `\det^{-k}\chi_{\mu}` where `\mu` is a @@ -777,7 +729,140 @@ This means that if & \ddots \\ && z_n \end{array}\right) - \in GL(n,\mathbf{C}) + \in GL(n,\CC) then `\chi_\lambda(g)` is a polynomial in the eigenvalues of `g`. -This is the *Schur polynomial* `s_\lambda(z_1,\dots,z_n)`. +This is the *Schur polynomial* `s_\lambda(z_1, \ldots, z_n)`. + + +Affine Cartan types +------------------- + +There are also affine Cartan types, which correspond to (infinite dimensional) +affine Lie algebras. There are affine Cartan types of the +form ``[`X`, r, 1]`` if ``X=A,B,C,D,E,F,G`` and ``[`X`, r]`` is an ordinary +Cartan type. There are also *twisted affine types* of the form ``[X, r, k]``, +where `k = 2` or `3` if the Dynkin diagram of the ordinary Cartan type +``[X, r]`` has an automorphism of degree `k`. When `k = 1`, the affine Cartan +type is said to be *untwisted*. + +Illustrating some of the methods available for the untwisted affine +Cartan type ``['A', 4, 1]``:: + + sage: ct = CartanType(['A',4,1]); ct + ['A', 4, 1] + sage: ct.dual() + ['A', 4, 1] + sage: ct.classical() + ['A', 4] + sage: ct.dynkin_diagram() + 0 + O-----------+ + | | + | | + O---O---O---O + 1 2 3 4 + A4~ + +The twisted affine Cartan types are relabeling of the duals of certain +untwisted Cartan types:: + + sage: CartanType(['A',3,2]) + ['B', 2, 1]^* + sage: CartanType(['D',4,3]) + ['G', 2, 1]^* relabelled by {0: 0, 1: 2, 2: 1} + + +The affine root and the extended Dynkin diagram +----------------------------------------------- + +For the extended Dynkin diagram, we add one negative root +`\alpha_0`. For the untwisted types, this is the root whose negative +is the highest weight in the adjoint representation. Sometimes this is +called the *affine root*. We make the Dynkin diagram as before by +measuring the angles between the roots. This extended Dynkin diagram +is useful for many purposes, such as finding maximal subgroups +and for describing the affine Weyl group. + +In particular, the hyperplane for the reflection `r_0`, used in generating +the affine Weyl group is translated off the origin (so it becomes an affine +hyperplane). Now the root system is not described as linear transformations +on an Euclidean space, but instead by *affine* transformations. Thus the +dominant chamber has finite volume and tiles the Eucledian space. Moreover, +each such tile corresponds to a unique element in the affine Weyl group. + +The extended Dynkin diagram may be obtained as the Dynkin diagram of +the corresponding untwisted affine type:: + + sage: ct = CartanType("E6"); ct + ['E', 6] + sage: ct.affine() + ['E', 6, 1] + sage: ct.affine() == CartanType(['E',6,1]) + True + sage: ct.affine().dynkin_diagram() + O 0 + | + | + O 2 + | + | + O---O---O---O---O + 1 3 4 5 6 + E6~ + +The extended Dynkin diagram is also a method of the ``WeylCharacterRing``:: + + sage: WeylCharacterRing("E7").extended_dynkin_diagram() + O 2 + | + | + O---O---O---O---O---O---O + 0 1 3 4 5 6 7 + E7~ + +We note the following important distinctions from the classical cases: + +- The affine Weyl groups are all infinte. +- Type `A_1^{(1)}` has two anti-parallel roots with distinct reflections. + The Dynkin diagram in this case is represented by a double bond with + arrows going in both directions. + + +Twisted affine root systems +--------------------------- + +For the construction of `\alpha_0` in the twisted types, we refer the +reader to Chaper 8 of [Kac]_. As mentioned above, most twisted types can +be constructed by the taking the dual root system of an untwisted type. +However the type `A_{2n}^{(2)}` root system which can only be constructed by +the twisting procedure defined in [Kac]_. It has the following properties: + +- The Dynkin diagram of type `A_2^{(2)}` has a quadruple bond with an arrow + pointing from the short root to the long root. +- Type `A_{2n}^{(2)}` for `n > 1` has 3 different root lengths. + + +Further Generalizations +----------------------- + +If a root system (on an Euclidean space) has only the angles +`\pi/2, 2\pi/3, 3\pi/4, 5\pi/6` between its roots, then we call the +root system *crystallographic* (on :wikipedia:`Root_system`, this +condition is called integrality since for any two roots we have +`\langle \beta, \alpha \rangle \in \ZZ`). So if we look at the reflection +group generated by the roots (this is not a Weyl group), we get general +:wikipedia:`Coxeter groups ` (with non-infinite labels) +and non-crystallographic Coxeter groups are not connected with Lie theory. + +However we can generalize Dynkin diagrams (equivalently Cartan matrices) +to have all its edges labelled by `(a, b)` where `a, b \in \ZZ_{>0}` and +corresponds to having `a` arrows point one way and `b` arrows pointing +the other. For example in type `A_{1}^{(1)}`, we have one edge of `(2, 2)`, +or in type `A_{2}^{(2)}`, we have one edge of `(1, 4)` (equivalently +`(4, 1)`). These edge label between `i` and `j` corresponds to the entries +`a_{ij}` and `a_{ji}` in the Cartan matrix. These are used to construct +a class of (generally infinite dimensional) Lie algebras called +Kac-Moody (Lie) algebras, which in turn are used to construct quantum groups. +We refer the reader to [Kac]_ and [HongKang2002]_ for more information. + diff --git a/src/doc/en/thematic_tutorials/lie/weight_ring.rst b/src/doc/en/thematic_tutorials/lie/weight_ring.rst index 4d738da4406..2fab01a1a2b 100644 --- a/src/doc/en/thematic_tutorials/lie/weight_ring.rst +++ b/src/doc/en/thematic_tutorials/lie/weight_ring.rst @@ -6,25 +6,26 @@ Weight Rings You may wish to work directly with the weights of a representation. -``WeylCharacterRingElements`` are represented internally by a +Weyl character ring elements are represented internally by a dictionary of their weights with multiplicities. However these are subject to a constraint: the coefficients must be invariant under the action of the Weyl group. -The ``WeightRing`` is also a ring whose elements are represented +The :class:`WeightRing` is also a ring whose elements are represented internally by a dictionary of their weights with multiplicities, but it is not subject to this constraint of Weyl group invariance. The weights are allowed to be fractional, that is, elements of the ambient space. In other words, the weight ring is the group algebra over the ambient space of the weight lattice. -To create a ``WeightRing`` first construct the Weyl Character Ring, -then create the ``WeightRing`` as follows:: +To create a :class:`WeightRing` first construct the +:class:`WeylCharacterRing`, then create the +:class:`WeightRing` as follows:: sage: A2 = WeylCharacterRing(['A',2]) sage: a2 = WeightRing(A2) -You may coerce elements of the ``WeylCharacterRing`` into the weight +You may coerce elements of the :class:`WeylCharacterRing` into the weight ring. For example, if you want to see the weights of the adjoint representation of `GL(3)`, you may use the method ``mlist``, but another way is to coerce it into the weight ring:: @@ -76,7 +77,8 @@ Let us confirm the Weyl denominator formula for ``A2``:: Note that we have to be careful to use the right value of `\rho`. The reason for this is explained in :ref:`SLvsGL`. -We have seen that elements of the ``WeylCharacterRing`` can be coerced -into the ``WeightRing``. Elements of the ``WeightRing`` can be coerced -into the ``WeylCharacterRing`` *provided* they are invariant under the -Weyl group. +We have seen that elements of the :class:`WeylCharacterRing` can be coerced +into the :class:`WeightRing`. Elements of the :class:`WeightRing` can be +coerced into the :class:`WeylCharacterRing` *provided* they are invariant +under the Weyl group. + diff --git a/src/doc/en/thematic_tutorials/lie/weyl_groups.rst b/src/doc/en/thematic_tutorials/lie/weyl_groups.rst index 919776b483d..f6d43d31bce 100644 --- a/src/doc/en/thematic_tutorials/lie/weyl_groups.rst +++ b/src/doc/en/thematic_tutorials/lie/weyl_groups.rst @@ -203,10 +203,10 @@ that represents `u`, then `u \le v`. The Bruhat order is implemented in Sage as a method of Coxeter groups, and so it is available for Weyl groups, classical or affine. -If `u`, `v \in W` then ``u.bruhat_le(v)`` returns true of +If `u`, `v \in W` then ``u.bruhat_le(v)`` returns ``True`` if `u \le v` in the Bruhat order. -If `u \le v` then The *Bruhat interval* `[u,v]` is defined to be the +If `u \le v` then the *Bruhat interval* `[u,v]` is defined to be the set of all `t` such that `u \le t \le v`. One might try to implement this as follows:: @@ -242,10 +242,10 @@ References: - [BumpNakasuji2010]_ -The *Bruhat Graph* is a structure on the Bruhat interval. Suppose that +The *Bruhat graph* is a structure on the Bruhat interval. Suppose that `u \le v`. The vertices of the graph are `x` with `u \le x \le v`. -There is a vertex connecting `x,y \in [x,y]` if `x = y.r` where `r` is -a reflection. If this is true then either `x < y` or `y < x`. +There is a vertex connecting `x,y \in [x,y]` if `x = y \cdot r` where +`r` is a reflection. If this is true then either `x < y` or `y < x`. If `W` is a classical Weyl group the Bruhat graph is implemented in Sage:: @@ -259,12 +259,12 @@ The Bruhat graph has interesting regularity properties that were investigated by Carrell and Peterson. It is a regular graph if both the Kazhdan Lusztig polynomials `P_{u,v}` and `P_{w_0v,w_0u}` are 1, where `w_0` is the long Weyl group element. It is closely related to -the *Deodhar conjecture* which was proved by Deodhar, Carrell and +the *Deodhar conjecture*, which was proved by Deodhar, Carrell and Peterson, Dyer and Polo. Deodhar proved that if `u < v` then the Bruhat interval `[u,v]` contains as many elements of odd length as it does of even length. We -observe that often this can be strengthened: if there exists a +observe that often this can be strengthened: If there exists a reflection `r` such that left (or right) multiplication by `r` takes the Bruhat interval `[u,v]` to itself, then this gives an explicit bijection between the elements of odd and even length in `[u,v]`. @@ -295,5 +295,5 @@ all pairs `(u,v)` with `u < v` except the following two: precisely the pairs such that `u\prec v` in the notation of Kazhdan and Lusztig, and `l(v)-l(u) > 1`. One should not rashly suppose that this is a general characterization of the pairs `(u,v)` such that no -reflection stabilizes the Bruhat interval, for this is not true, but +reflection stabilizes the Bruhat interval, for this is not true. However it does suggest that the question is worthy of further investigation. diff --git a/src/doc/en/thematic_tutorials/media/hyperbolic_La0.png b/src/doc/en/thematic_tutorials/media/hyperbolic_La0.png new file mode 100644 index 0000000000000000000000000000000000000000..a710f53a50b7cddb54e73ae3bbc3e200c3381d2d GIT binary patch literal 31423 zcmeEuXIxWHw{9p3hzbgViWm@)B8q~F)Sx0Af}kKpP-#k4Ksp2zMCmHM1q4KT6Hr=^ z-b0bzq<08C2`P6H{_lIv`JH?2r~C254`lC|S+nL@&nh!}$B$Z?O2>|zJpzG1j;Sas zXhR?;2oMNu0wW!`6Ja591^l76yrXdk0x1k*COkU?u4x>#mF`0FI?pYF3#69%!~0}1 zd2DP-NCIBC&(8rTo57)t*wzO$Q3E%lr{1i=ocGHwy*$GfL8Jtd91yi zEG|waBavhwA@b9w?d0~XEV2dJ;?X1WhYw`RKOhK*00Y1T?d>Q1k1aqTJP;LyI}crl zSH?aXObv1z){-}4J;jg^ux#|?T|btDcIN-^_AR~gU5BFny|Jaz^~_{m$%`lf4?SV9 zlK=Oo;CuNKN)Of?E9>H+myh?WzfereoujE5;5BM^^j~GZ^J! za>%OmE5c^*6!jugLK=<86WXId&zh#_m|8Sm1f{gR{n2gH>`ndF_wv?Za*tg}sWEwZ zkdtzY{ZX!d80lB{_lLtHqU6^r%#@o)vURu!TWMqD=U%l3z54LU10Lz)IdWwE5j>V+ zMu>k&pN$gPMq(e)ZvaJ6?j#qS?f4Dzr|S0m<)65YJB@q<8qmO~zl|N38}<{W^b)LW zG)UK8|8u!-b!mhu-7m#> zX%l4%zrRngI=1~ta*o>)h2T7lK&MuNT!WXk|Dp85EbH7i6%=ao9M!0x4C67Xg@Qjm zK1>M8CGDjHGRI&TD2-45xAET~XyulTbU1sAg;H>0e2_gi2#8<*j&uU5XFN z#~5%>-Oew2*v5IunR+kzR{V%FEv5B)X&l!--?B9JDT8^8lJJx!#ux`Q<@$gA$ZKUC zI~bz1+V9+JOzFv}Yk1(@R}VPi+h<82ivniht~3lYQhG3DmEMX^)GZS{$H~P{xQ0Je+i0HWFjWf*rVQoLkMFp{+8Y?8>Rc*W)aa}s@>|FDW?XK6`?Kq%>@Jjf zAT3rmB!ri9FTU?Me{!pYa!0vUvG{~Q*vIQOyWuhU6h_KFm+ENrtjpq?9?9+RZ{oGi zZPfMZ2`@WgnA^ff9r7?&A8GlxqGZgI7{(PcPc9&OE2H8EcIVP~^Z5+WJO`PFboOPwMa z`5V<|0Db>Z*g6s4#B0yD1gzurPgm_}{#G{>Wc~@Z*LB~)04pw*rVZ0aUNdv5$79kD zJpCm#iL7nPy*tB<7=Qohn?KC--cra;*u6%@Z6wXecXjt?lzBhzy~K(?GrQ;>Z0)#8 z{D60^(zb;|wR3mK(*=;Oh=ZjhxYZ$-oJ+J{<~$PK>D<@d*jaV17_$QXR--u#>`P%I z2bOX#gIF4Xa6vJX+I*h7ZE893Sx2Wz@p>^Ym*MK}+&mX0#_*`r9lG6Zm4MbI9ld`j zeqf2V(yLaO+%SM*D2{L3SvtWMNmlZ>(VYgA~ z@v*Uy5w~H~-Wo*{bw}^lnACv~ZDi0^+t9SB$YM)7iPYKol(%cAvyT)j!A6;LVUsYG z%$Y6+n@X3%y(vYMI97q`YQLbY*p!zQbkMSMnTMtRng8MP>ecJ>J#c3f`A2bCS%pXC z-cQGLksFjQViWjG^dirWB$;Ck?N&@8Z9U{5a_2I~G!cDS}=4h0teo z%7MvHrabK;d3BC}GFmJ2)?X+iVOsA}5X9ZkZkNozQW@${MYnF!dUu(HlHO-paSc2> zi?h}K0}pt2=L=wjT&mz2)l+#fTMs_%+Mz_YO&@aL9~`Kx7H!&)!2G9n`TGYCLxd}k zz;pS*_4)DS9QX-CSMF1;6UbxxbxP(bSLi9u^LhJ9)0N7(SL`*0-J;A@WfCha`P7vZ zM10~fmK2>k*Yx1Feel7Q+LZMk+Puf@zb7|Vxg7wF@GoVh5G-c0=oMAU+WxLChr1G0 zm8Wkj3OS6Qk%EGvtrJdWJENshDG@IL(hY`kIWjn8Mt=b z!HRclJmhfif_gXIWF!MSO5ZHg2G#myHPM2e2LQLHrA?Hy|I@4r$ zecm`TNV18S5ZmNM6+=>?!rweP_Ly08pYQM_`odQd2AGLB2^h}9<`O6=s*El60~~0I zF6zS90TRdy102-6ICu#vK2*%ZE4PB~EcN!*S_{=8XA*$vU<*_Pm@Cd%Po~Fg75Twh zmZSa0J%pIO4j5hU1TfHF`ocO1x9a@*kKh`S0}_OB7^|?6($?nEVT3&D4A(TT&mUz( zh^11^el#RaE>JQ<9s8vu{@FjO3Z@i3@6<+$Fd}qkN#?v=8{<2jk^b@kB6=`3@&qa8 zG2mcHQ5WUTvYp6IA*@J%(85GrC!_kGTD9x*eQczec0k_M@y<>cs>ubDsZt|BgU)}U z@R2FP^Kkt34@@iDB)oc&vRZl4lh6_ja1TpAnE$I=J^wP1!<6>*d0JKyyXqgPxBVd0 zm`XfdvmY_Ve+1_JVE5Cg*10UE-LB7^{}yx{qI$6!0xLjOLb)6Ff?2d7AxHts_VzoB z@Qw;e+S42$Aj8KGM&q{3g36!@Q9CO-m*cPZnh8OBV;D+Or5bmR?UZAG?d5Z|$XCbk zbu4Xn4H-fHt2vr_rIOw8cmPJgb8jCmN}Po5P`Ll}t$iwmIw>be5smu5XU%!Tsi-|P z4wOVHHYAi_jznQHoZb^BU8n;DbK8Im0ngCD>KLS&r=Tt~9|Q5@`1F;^l`w5qQp6Zw ztN}$6pPGGs>FHikX5#5ce{T`MW-Dut;ze1I;%QHwa$MViVhrvx=j%r7 z-;k(caof2{7z*VeCL|1AS7%p1VBPoKEH;fnDB{K58m_f|)R0gXXNX4JGH3b)x-L55 z7tzkewrI@9;!h-u^nD}7i+>={Fb4K^9(tQgPq4*j{9#unSqc?EpaJ+X z16(Mj68ocv?YJAla|ziT#vvW$5(4>`ocD)qgmLZX0tcYR)Gi(7{UCUC+x3;2!*Dl+ z2${D^?mj;uu@9FH1ExbdRIw^k`!%{U=T~}1_pQcl3zS{*huu^wc?^h0dNv4^3o}ol z>=ETjV<8-la_D^*!}W#TNJF+-WYkK4Lgh}tL>oY#QniJf@--x|I6P>Vj57FxNtD5# zCgljBHHy}h@O37$ro7>lO1wFBtDD2v#UEEH0mla^TUj;F^ao%pQ<21nr#vsc1~jcJ z>dJm{0EPoUx*@^RTG2K&htQJ0H;HzS=(S}+D0dJ^y@2UWkvjpLlp^)lHDeGPlVSIB zi3Fvp%sIGS6*HnAZh$U@ubl32<^bl?XWW*ZCSh{F)#?Ho3*T=@@E|f&ZjRPo{)XQ* zsHn;u(enm_G~-UZl$uMP0ZRiYFKN**A#HsWbY)X4(LErJ||yj~CLmS6pVUj0z-iQ~Zbo9lKt{?WwWtxO0(KN)II?C98w>z0`;zjz5LTEP1DdR>&z z{xtB@^G=!)%!Sjn(7^8|9Qa&VCB@%r0uxMLOx`B}0b-Q-efqT8V`)#2Sbd7Y>>$ol zf}6b6)h0?T7NnnKp>ey+?3GIikm29I$2jJbeWE#x*+Vb^7-}%SI6p#-B0?7?StiSe z5W{Ysi!5X_N(pie36e;lq|S?4Xf5YX0D`L52TMwP37BFdX`TIdVw5ZYQyX**sFc`E z!st=mHZmPEDHY$`0{@{ zMGt4f;mo3~_po%P|B5I+$%1q0ZcK=$U%QLCCL;Y0Lwst>&t0Aq*+~iZgJEL-Rkvu# z{5xGl#9mrIvIjt;7F##%9)nO6l+xS5q$ZB zcLT_F6eP@6w3$Ue+`-bdas8*foVH3eCJpi=N#22COP>g^zN3z8B>AaMMSR`*V@g0( zIV}WK^u;kEcy5ABEK4!k(=+p&@BU4 z9y&&$l-O9);|B{b`402^CuE*J0ix5fR{V>}LoFvrHG!imCRN)36BI!{4m#~;wD}3X zr>Iw(XH~q|9pC=}(?gB_$-wehPzzIuyntiw?OjJqeSdLAuEu-#dAJm7>-Qlifo5cn92T@!#JF?7Akm>vczW8 zKgpkY6X15=3DyH8wNh#mhe-$E0?#|cxj@%~I|!^HrTgQ|HcCo`Hgip~(92Wi(7hl$ zH_8>U(_PEFxw!mbB88jg*}`7H#F?w~cxvI2`JnyGk!d(=AGb3S>QitOUk9a>o$^zP z+R%oCl+)E9h|-Keg_v_Gc2zq=VTvy;&cVrPyUEU{U`)Y0g_5YAo&XD;B)V+Fq-j*2 z4?;dyX6$;qm@+4+ZkqKt!_4 z&LMM9f+DiPbn^O%CP%fANgT$kX$WVUt^sNcprm*oZLRh*kX9uyiOXzoV*hcZq^7;=q8v{0Ufh*p{7Gf zjR^4{mtvP^CpuLaxY9s^C?3n>k`?kvbj@9~8|}|c*Dme>304EKN>y zgxjmmEGuW9b`DMV8DtkeDhu#wZYN>3X79Fj$bJ)mBT}2hPOKMnVMAWeV82k`DG+kHFh~I*C++3%yH?7wWM%**qnuiv7$ zH*1&t#t$qnCYUF9|G<)A2+lf@AKHY}wjHO% zHXD{dl<1mflW>ZS0l{_~t8aW9;CE8LG|A{_jSkQ%JJA|C$57DI&(%l#8W=vW|EzMu z^H|R~$){G6y>=)>fY0QY<`+{CBSX@rkfJ5Y4&vr1mXit|Je+dZozApBaa%NKa=@Vt4++xnNTWj}y zF&W7l7|T$hg)dF26B_p-SEDUO8uh?}J&x^6W|u9;XC89 z;fb9_d`?L4(KAC+gW+YDk%1TnRf$93(8p2ZPA~K__k>o}*l^Mhg%eK~t-rgNR8>^R z{*W66^f7z6t<7)XUHNNwDEwoJIuRF`K1^}~xLp#E=F^iqHOUfTe3 zeWH0V5)Lv}dfZ#NcwY<6JCT`Tk=_Yr#!1H=T@GDzId;=}Gq&r(U}O%V1?|zG*$VTIe;phNQR8Wpwc}P22JFIT9mCVEqrGx>#`!uOEQX z&7B5sa3|Vq%m}SYSUQX3pZ6NqbG~VwKR7+2nDsh-f$*nApb`IqLgGk#<0fC!2v{C^ zTcFNIyHg$zu%Z}mw)vfflcV9WIsLUZZ0gqQJfm-#a+L6>;DP{Tgg6G>^r#1|;n>kH z?H8;eKJq^|X|xk$pXZ3tDwA6IKi%(#Kzv`#(#6O^94QAR|M9_X&7$&6yhp$m2?BYB zC(dF}spRA2Vaibv#23FcyPThzA3$DV0+&AWL}|iAH^Kx(C=`JBpc7WtSH?2%M@({* z51(|}5dYLMYBVtzT_enWu_Oi6&Gu9kLIe3S275BGHf{_{_h8sY-SarB+-jch@u~T} zU7kw=tVZ|^+i3r~s0mpbj})kZ__&nGPPptkhwV96BhyLRwNRex^BEZ~A3NSnL7zsJ zGuf}BCkISW)#u%5k3v4ItjmnA$r+i9S#CLiu0jlB?=DIRkZ5$j$JgF6QH8jHPOYmX ztNCYp*w>b@x&d|)q~)iyEHMgm2~6JV)H{@O2?NoxBDQumRd`5G6j!59kRTs_E|7kz zduK}wRCw$=Uk$IabB`yb(z8M$x(kZ7RPA2*!Q^RAW^WUGu?&wt+LgzXU9dX~(G*E1 zADGXNsS8WPc?mR8F5|0X>7}nXSPE#zwoUG`xk3cr5i`^;AGAijN}p$`hsn>C$5yKT zp*LpYfiV8*022(gqJXCqq}jLVxsz^N2O@lI-iUjo!%V(j!T?C+mET7u<~#KKR>abY z%vv(@zS&HQ5mU0QxcD@=&xyxCm&eIPt~#=_FDG0p`K@K|B1t-5E6VG7Fg zXzZMqD1t%bZzP1)Cz9)-^LX29^LZgZSk0)e_9BiB$oOhkUCpiWm*(kg;DaHzBas-yUV0n@RDV+TVmT4*7 z#G*IO!y`F%*E5=8`XYeDI{jXS`6LT#@7Us^n>VVWIB|O9vi-^M2)hT>-4m3V#9Y|- z?>wYpUjL;GASz%DG)b`gR6X=Bj@7(ftIMP==fiYSH0r=E^-e*%$51zX^=KI(bK*HG z1fs|SR0m5}|H^x~;&Sbt5Q*kw&y^_&qBd=F1p{Q`-`*KOPCbI~8QL^F!2N^W9n|^+ zMc;FKdeSTvO*FbxW!g7ax3>*_tDgXEeC&Ktd$529OgsSpni;P5$p>CzfgK{g*3Vf7 zTocJP#H^Folj*|&XRld29&$t3z^gTc2+ce z-zIeTwq;E!KB^CHZzu@A^3FQM7}}5o>3(Stoa&bw=NbAy-%QP z&*xf!@4|uJwBHWe-nQKZu|gg+Wpi!AC0@{y8%B=GE8FEA&K=#mf{m5O(s@3_9+(m5 zbdOAP5FCA2K@68Uzb*&-JrN-HH`#-AoQ0o6GuM5kjs>?~U_y1$Bda7;^ZCc#E0w85TNi+;T@?OH zdTVd6TvIEngp~u5%xI#DwhCK2+d$wGy_-jk-&muvQ5XsNor#bF^3UTcV#e~wm|z{Bz8%l-os%pvGORd8Ou=m|RSJ3RYk<{4oG791#c|_~#zI0ew1s73gI839|ubcj~<5WKXi8PkQ<_$R+66?K6=JN>*Cf$l$@9t zl*v?bkxSux76bcd(ga%0$X8T7Q`~Y_JWI3I<*{2=3Ho7Itx7$I?+(soZ=ZXq=%v?+ zv0f)ujV-$^buv*fDbn-}T9)vIf1|Q4{ODzAld+ubkry}BNb(!tEeT@Uj?I2O&5=9% zk|yu=l&Qy^`hjmbw;EvSWFB9hH*L~~u1-%(4y2m2-STyjJi;2hpS=Qta&>g(o4EKA zZPCAgxCuA-uG;dLfhd6^AhN5g`t9ta()%7?64*8`Ut_i(T0o^Ip=fiubBnxA=+od z+%C&T@7+@)(DC<|Fh~6nrP*G#J_(8e!SML(#Hz9Bs#Cho7Gj`p8*OBgnmU1QmY<+f zi<*hAp$j*(1ng_7V9Cx~X*UD4f8@JE!m`nEScXI zE>*pb9?vwVpn?LE@VCa)s**>UQZWik&nICRT1_Wa(fnnIDC=m$iylOIe73(6#UdA;U|N6uH-fu{Ai#l^&` z0&GA$mO)h!`z3VZ-AQ?nCE16m;q}l3g0F+tqMJTYgmV(^jz5T)g5RQBJEeuCIl)O{ z>tD@xd-OoOx*u$Di5f4HA3t=ydILzWL9ekyCVk-nO0tsD*4LAnDnaZ%^eVr|u^SM? zRajO)nLuaGOK7QAqn1^ho-_o2c|~id|0RNNSE7pt=z?1mAo+raC}wl;KlG$7wxM+#%3SvXRU* z*<5`pZ;$8=Ny-36A40GBs-iQ0{u=k%He_Xt#aNyq9aV*Z3a~&5u;l8Sd*ER7RpxfW zxA^5TS>#97tCQ#~f;1tENv6gs&;F6~@mLJQ+c({-`nzA2no_Y0;3XY>eqe;6QgT|6 z<6kiO_qKOya1UPdDJWw!#T#~muvx>?oO;Q$=8Pw;Q);4gBvx;Tmi?>wGIQ)j~3nh>tU?NC*gVb zsTE0#+Z(Bz6`S8Iz#1P^(b;65Q)GD_KiIFmb{g!sOlPzDE?nLQ4Z@!_z>U)10U`CkWRcGgNec#f&%31svRb#AOijzJFb6NJ7; zpOjC6(6!z%w|oxFJ(-By1r`?oBBC)FH6-p`H45G6?H&&ey@fspA-)^X%@TN<@Fe^< z5io&qkl5E*w+0G`cJO?lVHZ@Z@m1lJ9+xKJ8^rSwl)Vw-^00%6xg`hi*1P+eJ&rVT zUmIXr^0mlo|Lkb!r$Dv^<&p@f$qs$%al8Zk)zuF+xj^+-!Jm|rDlA>hGs(g};8tci z1gCU(;Rkz$lYT*)PtU5Q@K5dpoj}-N?*n_Zj4G{jKn;#b_$vU`kp)F_%g;c8x7v%p zT=VR}{HJS?Jj9Q2HBhi^7KB75szhNJ0v9d!YWPeV_(;CBtR(tn;I8s(V7Cri8JEji zQGX8gCht$=lSz9p6w&AFgT-G3^)M}ZhAQK-0qFw1x9LnW;f7$h`vgcEL@1$2mN5BG zb}C6^my{kyUqDXs)pDXn&ZNNR^#l`utST6p}cmfsB*=3?Hp?tx;rqmOJ=Tr;9 zKnRUcg;eNOdgMdrOVR;RrFUNw7Lv{zxh`Bjx>r%r18pwMd!nUE{<=LM4K|E%yY?n| zbmlNKWdxmIXb>Ih!|~Nr1Y&-p#m+~j=TTw8@vy_z2$Ig;8RY@uUZN6;9z5u29-f?x zM(xsupX&3boZ9@ikI(1tw^rz075Rv>u3!H9qrsQP*Q%^EewX&Pe$AN=d#7FvPu2Lm zLih4__E64g?Kr2*XnM$=uMS7qSiA|LajW8x;1-|vtnL#--uu07X}BEOy8G?PhcAbe z$y>kAJkE>lyLotK*I?AC-E+%r8Hbp2#2U$dv`W_H4O8@!=2F z+^ue*m$q=>gH5OWVN#b+`8)KXyn-W4(TzsdQqHdYvPg-uk*)9F9hQ4!YkNyN*NSW+ zaVtKg#Q%N!x$C#pINfu>p*>1spi(CXFWC8lFYk@utYlpK{JrN;J$CX!47S?)bZO0X z`gG~Zo)~M5rsMf(^IO&6**uM0+4#Au;X!k|4eu+V-Fe4sgEc3ZtNRU_?56-nfhZkK z4#A;+%OWhq>{e?I_KY^MMqvC5-Scj8OIP+Jp_f5QX^E+o>Tx`%`k}Ux*=DK6)8FAf%d3?c<*f(gHDSerldnG1@Wc0c*o?nwV$V zLxH5i!uD{qQe{$lP^IqJ`gn8QgM@BHqh+mr#~ZQefO@$+K|=R+oR=5O=94L7t8*F~0g|J3?CBty{pWj74Bs;M~<& z4SP>B6bPc_mr?qH^Hg4$4tx&f-gtk-_2`{U&7a$I1_JSQmkV)~!}{y_gx6@sVw)RW z#hUp;;2lb{lmV%iyKY~aTPVv)_WL(7E2(_r1&6|T1^bwy$BmZF99{V(koGnuBk{)t zVsd+Jj*`;Htg8B246JXKE`gT^vqH*Hx1~t73Y7zAM+g@T-?E2Ib)PUbQ9mz+mkNFT z0TCLSq^zS^BZ*Ed6MvPx<9t|(CP?lbI;m#9`grWt>9a7Al#9I^m3rH363+qEG-SjY zLle{Ix7;gohdArCtdDC(hRkneR1r6!`~JOU5)&*h%StD{k_$KLPFy#!_S_1xVbYck zY*GyNy6)CIxO}^jcUMx%`GST_#hjdrF}kLIi+n55C#C&wu>i&`|K6O;`$iTu!6qk} z^;5)|kQiVywy@wrQ-O5cIczfZ&3bZ-*?f8u!ZKAmvpPy0di+dw6-=)B)_`@0z4xap zcLJ3*9*2GG7QJ-*j0bnq?Rq(-CV1LMfxngWov)NryG>54(H}KGjFk07dvJ@*Ur*a( zolOXxul$Pqd7`(l&U)(8b?23|8%VnKT#10(pIK-`zp?J*;%qnq!3~zQw&Ken)mIc?zo7FTBX97TW?yH7OBc* zVNRXMJ;cp@s|eR(7*`$2UL+IP)Q?s7p#8h^hC{A#0x6;u=(xSA5a;Q9<#niTe*o%U zLgfiC|BL_2{GqJ>ZGH!{%Jcte{(YSv{Wixtf9wgotSTgVH~+}a6XF}sKg7+oTaJ54 zHHVmN;6!zdoEcV1?cDVGaaX}6Nhz66-x8mwC8QEhO@aqa_4i~C#s0R0|J4Z#brD~V z*h9?tz7uK+=kwFnZ0b#VigYd!H`kN^(d!cQjD1sQ1ddQ#@q1`X5)=DkadZwt$g&@} z+_xKWhtF~hi_H|@Z8qrYj-T!BfDiRVyh#gReISDy}ionO5Z4nu z@^{Fo_^l77e=v>n%icrhFFEhvoNUTQa$lBp5Kq`o!>Sg}K4F$}FGbmqimzzsD;+5g zn_8$b*cA*_^gK^eMQ zY9h!20kAsLh1sWX{InVuU5D9Lpn1;U?CU9HAbbmbA_SMdHZ^|CTMkLbrkOXa_@)B+ zbJscpd1GI$eIi$}*hJ@YRvt-;)Eku7SRa%((19pT|G2Eo+xg%e_v4`R4W@kWO+3ZS zrk2|g!Ei}^o>51qsl^>U)D+R9j`tiKv?`OZ^fYq&p5ZF)n$Q;B0-x2Db0Z5(h#U<| zS!lR~V3!~4-S{CeC}6beS@dtGO_y1%`QRmi#d#iA@3Ldz;_En9e)E<${{FyBlihRM zmvd6u*crjlje_;u*mUokju0J%G;k#MEMSm{XH zx%xKld6lDQqz`0slhZzK;jY>8O~Wr>!a9CUtX-r0lEB(oK=Ephh$}V2X8_F#3so09Ww`PeL%o+K_&%XQw73(GI z`1++uEq$N4WxwM*M=wi0ex8$|mz*z(SdmrkBJOOgu2`?OMw_54$B(o_mq32SZTjba{z$PRjd4j_?hp!< zc!u=ws>c}8`4ZIjjm0^W=nP~oGs$#^MP$`te%EzgZhUZp>=okjhGSjAG5nKTD$=bM zshVCsHoE(rufAd+bnm-Ha6t8i9;8EN6M3wM_&Q-b!@-5slJ{)H-TjOGsVhp?8;zXc zsG*d~;_-xEc`KFsi}&xsNH|NS+Sh^~13i&}wR8MwU+=llXE&)QO(fRhPtBOO%3p-9L z4gOx7@7+M=E0OoDO=>UTZzdt_rozbhiS?iN2IBIXS_1BWMS|ZWsG3)CSZ3ac&>Nkv zx)4@qU95&^HQP5!dg+YV6JGlbOtfDocBs{M+Dv@T%$?nS5Vx2aw-l@~8UI?*OLtcQ z?56$gJ|gYH^5@G1NSoH7Z=A1OK{YqLuGZQXM__d_e^_&Gu&AHdcgt5E4X8e>_oW9} z^r8c&YqC1tbl!uM>VfawgsNtbe=sTJwZO7nA-&e#u;LqM_m-%5`Ujbhs)t>RUJrY1 zErY^yW^@lU@BEB1$r=6HjlM7mCqKh=MtL2Fu0y;#^>q13_e|cwxfbD)IUPhr821GF zb&T-~lIBPIHtP+9r3bsmj6@K^^-+i++ss#K58k=;dmAkE9DT0Uv0I`<+|7xxS5MkR zb!|2lEDJI7xbc}8g_6ssB?h*PKS`d)2Pz{}Q^pby?p1P0%LW+}1Y*V#I$}Mq^47q5 zUg+FukFHgH!o7>6kjb1P*e7qoR)ewskVHN4EP7&|Wc`}UtEzi5sPFuungT57J!}f< zYFP~sp9q^L&iZX<&~sQ{d`-MIaduv&{BjQ4-)Ff zTy$YhV^KHyB!z_UCAuA9>!)J(Qu04+3%uItXedIq4%O`t5G|X~*3_Gb{2s|VWUINo z^C6SF$hvut^D~wbbG>g-C;~W@YvK65L9ohGG~q)Eaz{^_+2a;0GXchYb`?WitD&C_ z|GMS>R_fch5Tuh1^Bpn55@jo%@DMXcxq0kc*$ep_yjlzJPD|6V#mdN1Mglu{%12N_#!9q;z`m6gkVYoa_?+H zeURMtsZ15b+aLX-j~$rxnu1Iio){-&k<@)|NtN8^{KI_(yH~AG(9zEImrHq|B*AtHYktY@+`+C;@ZnQed3!fttDkqOE$p|A?`>@*Qlm!6524xTM} zr*pG-p>snFb*Pt+`1hf8Vy-`Vv%40tmAqd!h1?u^yrPTAleS+O4?~a!CnEYCZYG&{ zN#e^!5C0%spleZtee?$oqnb7W_4sA7d=_PFqy=>n}ZMO*JQNqV3Rb53iOq zeno6GEfNq)gnL3)o=oHV)=k%1E$NCME-gozIX+QEwWVg8LXHofpBBk-SflwXRA? zw+*xjFW z*7hD2*#;e>TfeUo`twVARw;4i>aCI8X(Vb_Zn6UY4S-%}!ZiEM!246+kFUs?IxzJ! z)j5z4fI}X;dNUpNdr)yk*H&2l(m~&unK~;0j&rS;o(SEz6}`V- zHU1W{fgVUDe?%}6~cR)d2Go%-TpGSg^g^zUh8uJ}EjajhmRp zNdiBF6cDf;zjNej3&=h#vcUiC4QvdYb~MkT#LDHWRU90@~U>pB@kWP6NME zE;An3{1sku5+~cf7-mk|U9U4Cdu`~lhNppCq58dhfa?lMSsC9Aj{}J!;H%m$Fzshx z+Rt=B8q8O7T`+jwA%>at8;A@@R*B}@fxk*Ewo8^}0`39f*R z0VooGl?o6z^Bv8DSCODriv}aoD%bgm0PyaBYVD5W&B|8rwN?HE637mulDLqI=nQ(0 ze4f~Eyse%wl+Co1yIQVBDpw)g$?vyTe5r<2zfWRZZ+^(XE|P=#rV}Z{_>I_JNRM_PoI^FZKg~43WqlW2PfS7 zovsbc_cf%tC+4tVM$T8{AF4;Hui-qJ6^@`ij>(}zdmsN6CJGhk3REp_tn5np=Xx)L z|K5OEjU;1YdvVO-#tAlZD(7nf6Y ze+OP*J7Vc?Q5E~wcP}@-zNb}`Oq3$5CZ_nT?B(Wn^c7*TelTKV zc86`)UG1Zb;N(bUn&LSb$^F`EEUyusk)7}NX)@>5XOs~`@J&MvnFG)H({W_EOIl^S zS$g4;gC_uXiW}2gxAvyBtmdNxNIcyS7ySH>6Vy|Y!IXme$?}hS*o?6O+T(tY+k*A0 zouN-8b}c;W6296avi}>%Xi6aW+~DZuPA}-#>eVqXzFSlkyRz$EWmd{ZN_h$j+5Apz z?@#P!y{d+KHiK~H+(a`Dia~w00VZkNT?1;tJVc>{9nH%-13#sz;TxXaghZq75}%h! zw>uvQ8LUY zN$)SBv^~c!e;&SPB6{cTcE$^doLHHhGRK6uzK1B>zW@GXUF&QewamPyQj_r6k(UEm zzvLf&F+sU?Yl!HqH^C57&PwTCMOcwKt=6lp6aT1Ma;KL}f>N#P24j54OqtuOxY6Gmdk7_y2H1=N z03@KtYWt>xIml`oxtH)Qw+V~nw`&SFMI187l>_Jq(S}~nh07O+LQ(Cy3?{HN!cnh3 zgt0`C?Czx-JGH4nlj5-7)CsG z9~K(`i*(P{VIzlMp4Wevrva+L=&*B(@YgmjMebsRl}X;!=mpUP9b)XM#}hIt#irvA z0!OMBc5k#_E_W7>#OFG>Kg)N|v|td|of&M8|}_udeu*;>nkkq+|R%(n~+Y-QfN{!uiV+H5|yr zB(sMUcfSEDHRJI|Rb}nv*G!&m7YAZ8v@`7_$`a4a+~EM!JMUbCeVqooUl_HR|K2V% zzu5Im$3%9b#<}htJb4_?j+vOrfEB}$tOM9d&D4DPm`Ux370SdKs{pu;OuN|rk z2bPq=>zyU$Tq2wxhqKJXipurJesx&rXK!bm?vEdmWA{c7B3hQW<57mBd$*QyEiSJP zC`%r=P30H=$SHfQ=oi>ElBX;li4E{v7;yD2Z`tN26I++wmnA8lajh*}TCjWs3c(Ke zmD#@GYxizZLd%?Uu~bJo?0&)cP%Hkf^`3BcZx*N}^apPbsUvWs50>&@dAn!n8$T1+ zWM9@ubopY!4u}TbtA>{~4d_o0zD+DFYgnP*ovfWiZ7x{80Zi=4ds7d~QMCZj6uB)d z&pJHBe{tD4RjH&GI~xW<;&}_+u`l00sl0WM_-%d8i6t%FU*@ocPStO;2)Oq#HBA-7=~fC~l6D8RChg8F#ZVh%-qU%djH(~OeSEJG)5&k&=Yox+XwZFqeX zh?!eYdy%WtHq){pLP<8gIweI%zDGJd3+pvLYe>98dglN2EL!5S>tvnCSx|o~yu>xo z<34!na*n!=5ywB^?GF8LVEtWr-*ZkpADps5^QY^*d7qLlU?j> z6VWT@pW1=LuP;I|+Q_b3TIl%!)dA6WB%1cAiV%q;7wrCX@7$ozCN*+KU%k)qAmquA zxu~!2*F9#hc`jO4$k6Uj3rp?(L@wA6N3aqUsrR4%(E2X9DNvrEnRLC1wD8PI{+Otq zxGjnZ{);0#!!>#}dztl!v8O5dRSj3p|D|_cEYtZD#x?_2B-h&-h0;6a-(9V|V~y|8 zJ6-?_of4EQ!tNCBg!t#rH6buxndE(czk2DE{le~#_9dN%EnDr(kyYRCKk|*`MtDbamHDpMQ_xU@LxAcAMiCO)YxdN|2=aBGpH9A@JNj z@6EJdE$MfiB;(tk9yfk5C3bzf87UAgHBTZ<&TdE6T+Pklf`~>{4OlNqBpJ52o@U#f z-1cuF+UO>Y6_#!dJ{PEJO4YFY=||&lO(*t;rDRk5l|1RiEx)Xd-RZ*THGVWSN0rC- zmMpSvUyw}wUAdh;Qp50?;r;Yk_fr`@9qqMaQ^T6uErBSV^JLy14{A7%M&0wayKAlN zn5yO@)0;W|OCTAslFJ1d*f7~`EZ%;Tzs94=C>|bo*JMP8p)Eac zBWU#a>Wm4zu`B8tuiIqMW1RkN9)uIuUGwJynvuI|Pvzx>X4XCU-7cJBVUp_v%8b=W ze_dfY+QeR7SGc2(b#t&4!W+rya3BPLE^J$iL6yn!Hp-60YW}N}NVS-E(+k(KZ-0^A zR2x^*YIV$4Cl4A)K#ubvrg3?8egeyik5ck)l+6@Yt0j1Ox8!?E?=L?Hp5EjA+Gz;m zXojx7*J8SfXE;nBk*w*J{~)37RS1(qY^$nC+%cmFqVHskLFMt06_Vz&R3GU7Y3(cH zqU_$SMU+-rKnYPoBt;rw=e?*3M$Y9WMDcpR} zRCbj6IIb8{5~ajU!G&!d%ho7E1K&*wsE0JBfY91-j30YgZ6&(R(T};1|BXe^yBn-&O)r0*{tqR}*yE3#-{cWjn>yZ0yic5~&%P6i$9~ ziuzkewWx^%+AW@1DscDt4m z9m$T{+`n+k20Vg-esbsF&vt^iuwUBL4hg$~o4vB?Zw-^s@@=Slqt#>2hM#gnA)Hr8 z2K`%2Ta?0{6dvu?e`#iO6|i{d$;wZqU3@* z=0V0Fg+aC5&tilp0sg;*P8Uj;9treo)#hF`7w*QH?s)ILJvQ^9RLcdk#e$d!#Bs*% zxc3$3(ASY`aOtB%(T4MF+Pt9I@_m4NvbT7ieacafh5?g`!SAnUM}ut#WH6@R=sosz zg9Oe%H~GHd38$9nsB&TFjjTc0QmEnZ_4%3kTmMwk<~|g$_0*%!B@d4-5bK-FD})*r z&tHVJ_|z{%^6WWnTrLTK6a^GArN#9Exi8E^k#Ed#0yoFL&7B2dU@%-YXTAC}pp);I zn}!nbY;Suh3K;ol)ZwqA)SCw3dQWk|%GIMHA?Ax$4rDG1F5RcB1|K9-4E0~{OIL-d zi3JH#o^6ED$o?t4Dt&)wxkHx(MDh^n^|1o|Q3eq&Ukr{>%NWshPIllj+5I+>(IILG zSNm&rhP1z9?+Gy-sQwq`aQuUZo}I+4K>YijN%bxLE!3aARJ- zjM`PvEc^4g%|$(-B^6greQs_Ct<>Y&HtNCUR|mS=p9~3&)+lW~1gjq$bfcL87KIqVB7hYi-RCHq#H$5Os~ayJ38E!C)sA)ku2T z`u$yU+D#<7z9$FMVWj0m2|rfCRAv-Q_$fCs3+(qClCw>h5&OLHNV>MvMA%A_a7)*u#J8&^H${;XxzF$4J!d z69^=5iibvi&TAbj{@J>!yRfA(0)wD%W2{i;bg$W7PXef{6i(vNQPvtbWC-pkljexQ z&u74^{U^al3=}5U8U5;w2~T=kiK3}s8(I(ViUVfw&hXZ=RCUWEq7}29a>!*zsS4IO z78}ub{-p3d`TE6Q%M+e*mp1stImS7mT5vRfx-Grh_P`fxal!6;D=#nB9>L>BL9Wk2 z7y^PUdh2gY1dSTc6EXK=9#(18M&zfJIM45C=^#U=ss<&>1;Emc>g?QM z)w(y6L%@I7a)K-fJ|aR`UlhA1Opgu0-u)b_a1?%8(AtI?8oT4 zhaM(*)fpN!&c4}MJrcuVivR7DmiRFYIs2fwkAVLLduF`QuPblcgPpvaP>KF7(P zhHK$v;pnj%a}K*h9j`yStVUm}0@I&uZ2e)LdJG9b+X_|irOQhOGzfLM?s;rf-!5!8 z`vZN$Is%H4zU)3bNux0KuXC&W=rnx~BY1`XVvQaMRaTKwHQqHj4cmnu<#G8>p}e^~ z^h^;#Z3W3PB!f8P$rJ?f(xkqGWaUlb`Q^JZ{7GFkLRDO!@Q;9og0TVnShfJ_Z*qso z{z-JF*KUvEsdZ(c0f+c45815ZFO|HOLlZ&`o)cHLkF!l zmx!n5s+z}VwR8Y7$46a3yzC0#Pmx^tbdWx}5s-3c$=+478xpc%{rID9$$Pt!ZLc#fx4j+oz~rDO1zI!^jY~({e1gPC!uLPe3dOA& zPz{zqT`Qrb)a|Kb4Ar`2+E9QV!5n^dl#ecDeI4j7{qb!ho)P+d68=%Fh7{o;`1aUz zHC^~`WyB;NVdgXO;p(lw=}edL^dq3mGPg?pbcj!Fmj7{QZiZbfOoo`?VvQf*TOR%K zO3U;O9wLU~{==JWD*xL<_oYu+jRI&=$_DxUQ;z&bz?*>PB?E-TGtumbnEf3<(RwTT zr))8U_WcXrHTbaCQNw|EW|^wMdEmR}O>OvJ5hrQLK84^qUo-0G0dxQy*DgD(*fVs^ ztHwUt@rNphEJ!|g(>M=l!AGJXA1RE)PDZo|YV>LFk@ zCqJW&#YU6uJyQAbkND~O(rObix9h58kLFmmVJ<6u{$EhP5S5yE9gnBlEAt))(lEhs^+CWxv4 zGMj>(-*l_)yMWvE$pzWDs#x!mH~P?esrl7;br6J5l8)K3E&%kcYUJv-iRUd30mBdmkMrUN4HKbEI=p%DG zFR3n}x9S7$K2sqC2EPd7c+7tNfdWGVlnDi8Kstg{?*?}N2Wa-=hRDvLxuWsKB#NRw z&L;Fei;V3OCgHj$oOD5jnd`o>oRNR)P=L=7Y~XM_33~+8(m4)v@ZNsM#htI<8xK2j zz#a6Eicr2UnA^6`kM~-przEfc9s7|@t~spn*70E07(xcQB5U^aS(gNDiMpSD;tqLa z)84j_^-h&T9h?Dq*W+cnW56aF#D+3KCDQN!Vt58@u8NB8=s`|L3- zIbse!2ng64IN`V;CHpsf)vOCQH6B$*rQ;J04+NqzyIr>7_x?=rqdkN;(b3t2XfrM& zM1|_xs~i|b{0=Eu#8XtwK=a4!#hXRMv-6mkYjD%GD{|pX9Qnhyz(=ks2tWycgkFbc)XC+)q4;H zSKP8fqE}YV$$i!|H+`BfucNkT&@I>XaTln3?~$LxJQt+YfDU`?y+19nhK44!xShA7 zi9S$w&@VQ2RVShT0L(ZJx}jtXrEPZSy2>lsDk49Jk%&yga)n?5{3}<4oEea&cF63r zVQX|uXRU;(FTPF!(v37z6NC{5`Y~UT#-umug98H2VUAD7biW)D?GsW7*%A$3q2ASB zR&8y)LLavD?DUveHqyp|;45_bp!Y8BCYlQcS7_W3Q2KMA_vZMT`A*ck$hDc0v2|YZ zdO{VtPPk1+x3cG7R+4voHa5-I{_sXJScSUR_iP&9`E5tj^^KtWs6KUWy2t?kRbj1a z5qT91FAO`=mDl5!=9O*+B(-OFiFv7XxPx+FC!%NfNpJdlIh*?zq_5zW@^6w@wzXoG zj{t2ifp7kL{Q^mChpf4w+1@P@`XNGAk4m#9Rb|sXJ{{^qtm91ukdpCI2(?hS=c>ix zIToVF_5oo3k4>H+5K?h3FuibxGQfe1!1h%d1)@(L=$BZ*u2^r`{1(M9ZX^Ru?{Y4Fg5f7TRZP*|N-k;iO=0geH zLVLggo$Rt^FI;8AT6Lm5_YdqIBYH%FPl1TbI9oVBsGkl{@q{99Q;=Z|@^iFeX*8xe zaMR4$Ek)C#a_s$>3Y;79s4a))OMe{r3Eli>S1kE=m!kvTa%hWCNP4DA`0nn2aU_sd zCQAd*u8)>tf8k4P&}Y8)w=8VY0FWPe?Le3)M1 z+(g&O9^NV(wCJ*9&5-UpyN6GWR-e#?m>fY;>V8O~d>^3J9+zVJtbM|c0a8`tvw4kj z*co3q0yhmAmNg|nTbnV(@q%@bv~7rBiPL}M-c9zG6K;W%GDimW(NUXY63i9|iv z1rW(qQcF!A#|+&=26~yp#x5Aqm)K^OFRMqMK;PKR3#~7s0hyM#?3QqJOTv7F(;DU* zTVu|O`12DM5)bZLMeEO)jj&sM^XGjexOK{yw$vBn4^EkVZN~mO_NWp623Ut-bXU^k zi3+(w8~MDLr{5DWu%gkChFnud0|J-V(wNRb;o-)YyfR`a5|i#zZR)*qO{xljggh$-} z;%v3(Myql7J5Jl0L3ZWXA9;6fS`m+ZqmRC&?0Mzri(XD&T30>iNfX;(TvjvR0M07_&H+Jr&{+(pDRB{U7h%a0 zuOORh&7<+z`Xfy5bS2koS*J&vE4*FIbOJ5Y|D>v)->NfPPMRd2hqPSkz~Y=glDyMKj#g}zWnE|}FF+&EJ&#slh}IJx>vgC7VM zB|3h0^4{>#c?><0;1aOrKo%gizEJ>ENmT;a#)8!O<^N_tii_(z8jccQ<^x)}xp(uD zszkDt#j6HQGY3^eF5vidTH<7S(za1@FljbDHXG1Fh`Rpf3T;F~1ppyCwoOo;tgim3VhA++1QHyTi7yg9`&v`|xvFTy)6-LH% z?+wp!>0!CHBbNUDMINvR|9wbNM84RtYUjwh3%}Xkg|D+TT&zi%!=2oL8x*Nh?*1b{ z=C(;<$z5vi$1fn$ukP+%ti0}xy%_uRylcEFB|sB2x;Nk#6xtmHGTn0KX>05Gc}d~T23@DQOwtf)%SdfSdHPf^Gx94+7IRe|muU*p~*yU&Q( z4L;>}0fIJp@XT`z6W!@Vd#Br{>;7er(87X(aGK3Qk5Cj(_aY|Ynt)^fKY2l^~3PJh-4Beoue=Y4hl z%jN&EqD9oc!~2Ne1kO0(u_q9{#;9eW%XCAR4~v=q8H){ZbJ&4h63J3mEyH!mw*AZ4 zOI7*wLg7Cb%}Bv>YVRM9MI}V1yqRe1l9|O6QAj6;ERBs1n*~gaMvR+)Xdb>zAJ6dY zv5KFCTLe~u*nZD;3>Yq++VUoQ^EO`TQlZWeZg@kQ9YcK{XrLD~mc(ijfl@Kp&J6ph zMZU#tAenW}0E-?A!1$-mXxYa_Rc3bDyO?kV2|wwgm|jAh0*#2SxD8%*SIb3T^#-}W zj7G1|5BI@b(k5%JZ|v@0%2IVk3Ow%hLZ%Iy-fu94o?&X{v%EaDIT%7*cjZ$iA z_;Bla;ntwvcN=tAu#pAofUJF@BRqp6-wa}!FZ^*^3nsCt#!5x?dhUy(n%2Ogw}0nH zy(-K=flo#_^GDDs0^mn-_&qN$?KM|hs! zT=ANx%vkmYHCn`p=)6DK94`wG%{KFgvtN;(?wlSmGxw}SALhqogQ%;xck={wWy&C_ zslmIlIo_Ww3lSwC*pIn?cuMZr zJG-rG)EtabapZ5$N}x47WIlhVgB7FP;@lY7j<#w0c7y9fe_?&{cAI5zcSSswl`A^l zs`<&KHSy)QzKto{6<(u<78Ei`2MoA7U4gAlP zVfzI-t-cfpF({j+6-%1k3x2xuAYW-uN4)ARAG>|`54K`xZ-)^bTBr)Fslc}=v@=>Pe_`Txl$v9-}%O+L)|CyYOd z8}6xofn(KdGoaKdz62b;k90Zv{7L^q%JLV9a0HZ#XD(^q4H{`)ZH2@zAPLBhqe!Sr zT5|fW@tVjOXNa@VmwN`bXS^3lGIzq*{OT#;iiO_+ZM{U>VJA^;%5V09!~)v{WVFnI z_FFKTOM;)E{)8�gpJ$FXcko|A3_C$uWU`obt4k=x;lb0d&!Y%~Mw1wP$g;5B@GR zfr+pY2TW|a_s1Uo7|DDco=$MNd=hm3#o|I7yZtst`Z8}rDVWeLj0ICHpp(H;jH(o^8IHQr1tVd)vF0Oz zel_jn*^5N@Fgma=Q&lkhC=COD)%KZMiv``chBoxGFXrmj&Q4=^<{CmjuU8reKG7m! zj+H<;PZLSg#2Us179{ft(~06LgciQ?0brGR1NCs6|EcpwWiBMXe8*yEH z>|_>kiUy4$lQ@HdbZ(LkD}-AIe)OvNS8rtNA1(rRkz)9#os+OedTTO+ah~ZYgboc# zcQ|K<>07}>h5aaRvTLiM$juyESYaPbhdXjSClR$a%5~^v$GcK9qo7)#-wsvyacr0$ zxL)~SH78ymnas|=vGf&#w395}Ppi3YKX2w&cG~pE7QaAIAJmQvyr7Ydzx!v6FPANf zg{VRf`&fe+J0ZjB7q$=l3(fl&@}&y0&Uq*LD)-{b zA)Yd5$RoSOQ5LeCe6EyJJ`pEoZr8~4_Ry=y+VltG!dI_@A_~GQ+X5fu$A$!RbOLip zCPP+?U(Ty>TOVS`iL!F42!+}I10_jFQ}3glz~tl_VLC9bg0Xh52-Uu7wn?me$u#Zg zYvo#W{)z?lFWgTdT-hg)Z+6FU*bAZ=n+*oo7ese3(Q?ZP}_5U=BjlIg*C@KAt1{Hbky{FdIteTbHhZ5hT+j zyXRQZ-~A$b8org<=NNs~+FkO@t}j?7Zap8VPY~BXWci+AJASqjpRuhiJsaPS<_QnT zw{H$IoDE;wi*8i6zsGDo-k0y8YC`WoiMEa|lzz#REA&k%P%0r@!BnyY*{5gOntlEY zEBR!a>Pu#BC@+XpP?U;)JyK#^wNhiEc6Yn=KVfQ3Qj$;SET9f|^fUGO*N*DehhM3j zw$c>v6gZW~|AV6YNbpi176VM1oH^~cZBE9q<0thAlK&TEYz9w$cp-BT%9adMeA?h( zf7tn!eDu0ut=$Ytux#NVlsvakR6xW>aQjwvGLyLBA7Z}fM<|%;mNfP0<+f%^p`lce zsQ2Fud}dD1D%Rp%B}-rNpm>U;{Uf4V2H)-^%b6GC7Ix>?F)bW=nGWL(VfA61eE+Ce z_AKv5tEZ5Ha-l(095|H$Z>XQ3Ul2MCrF2sSJyGH!;PZilGd1QuocRX8fd@if0S|@x#qTxPa zA4mh}3Kohdpm@sH?D=A+`XsJn3P$31GQ34UdmZK5N0fe~5;{M~)zx-Lq1#tAoQ{?W zYUgsb@N?n5bj4)`O zENjF`xzbqHbT^sj(S(+rZ5S&Ttu$eZM>q1SK4w+)C={;z032LHrJuA81R)9fpote9 z*71#O{wZym4*c~i3P20PjLo}`v5a-Qv6{p9uVoJ*a?b6l|A-}o31;-ml z{CPEdab^Y`gI+s`2ZjuTFCA0^Lzp($@jxuB{efh7{^RryA7v3yt)XKQ3a!=gu*VqR zq;@!Sr|f%3$jEf*YmSC=)D4h3o2uElefac!j2OrJdm!0vp~8wTfj74Tb518(%U!V> zMqH?kf-{|P@)AJ*c<#zr+pGLMPa2f50!XV1ECxo$ZEvl2UsIXPYnG3mG=+r$JsZvx z^CqRJPf7>;CAYY#`yk7ys`AN&?Su(EhTA*%ifZLVOmODNeZki7Z2pwi6Kp;H48!ko zB#tG*;hs(8v!k1;kGq9eqRds|_qBIUjx$1*YbSo3{P3&lb=qg7L$O6>VEqOeLd{Mp z8`BRIe~si+NuMw?O6x-#Lb51)t!CkQfre3IhaJOm?0%-r;s(tTPO(1Kf zub%%Zc<;+FVY0_twtxbr@J4bzdg2s$h8BD2_@3`WlMF9dL7f`9CgYy!{B!yd2nM^d zgnNSN$m2ZyRoVOMHUX)oeWO1Hm##63pN%*wF@)7j(MZoNqWzOa2onzb!wx`h#&ZlJ z;aO9%>UA+s$j`|eXOp5bj4Sc@%Bti8Nc@~fkF1%qvc|+^JVLa~2j)0p0UYR*@krSE zYl;CJ1|GJNS&HL@Pm=3tyc+BHos~Z)&vA2yhQf+k1KAf%BeAw%Js_d!~&` zD5*xNcJ3I*rMj{T8=)rOePY5&&JZ{4g5{9`a4u4K-`HRKOw&W+>AtE?ZFpl?hm2C8 z4#At_Ekh~WI&{cNqt86M`f(O0htni$A9ESCDeTTUdO5Z zhh0{k;d@e&%wz_06!lLr+Rk@a@40o?EQ>34PvI~9JTgVl1$MwsT7jPD0m*~rZq@9| zI9zDk>VVy8ryjig+c;9ZzgSpKb=Owoj)qDQ8n-DqN zlzZx{>M3XC$9v+l7jCm!RdPNPZ?tmYD{JaQ4}-x)jl`P#A0ovbH_}@1pTG?y2jbQs8Wk;5mA2yBiYOwbAQd6Mfg)AEgMMyK7bVj5SF>WxFh z=&FlE|Hf~%CQ0$hBvL~)Q`zTx@AY&#-6V>c7?%oWCUtp0+RQmU=p6SVE;TBMO#Z*X z$=v}bkH1>-CT%piw~xB8Nokw=S#tH)z|8;VozHEjQqiUouljt+FWj&ZrMU7Zzx!MR z_RWc~KJq$szb@i{v0v{>_#!nYuc;9IMm{(UI6hIP5p!oG&jY%WCf8-3Yb5@Gp7Y>K zp#p^9+2Wb-e;g|raI9XO?YPpg4@|svtin2Bu&TYDCbfPC5|5Kx&3cGt=GmVJC&AilGn{p7#!s}>*epqY)xh6pq{E5a>j9ZPxKo@%@dv_)`?$Fnq zqz!tKFR>@d$Htabzmk-Nv!-7$>a<29m%>kVSvZ*VIAaWl=N5BQ}GYP7dneBhdvZ|rwk?7Y)r>H|546U(=7;;&l8RB$(vk=R~VQj%KgIB;ovpS{&$+;i( z9P0=0KPjf589QrZWZh#guHJ_HWq??{*ZVCG27XZY))}QoHVfB~$<6E(VzLWKwa?32 z$rVY75az0twpS4S3P&-8rV%B@es-imDs~5xA@nZGgG9%AcLtIaWB>tD#*w&4+}wV9 z(_+`7v4s4ZN>qT7-_w~D`y^8=Tk=|^OD^oQ$7yIT^TrbW{L}GgX?>{#FTFoXCN=je zG2xX;2smFiLG_XFF@}9bu{GuDpWYg8Q%9_dUwk3}uCPotii$7a)lA|<6uKid7p{}< zZ*qzV;0DsJO=n&<{eu!Od`0$kob8jZ2zPOqMrOSrC@h;l?oN1G+=A0m{PE{=1`8sy z0F|PO8+K$quRYmI!dL#MMutK``%xKgd@-Xr?~Zy88!!0gpk4^`2fsh+3w^$7u>f8b z+z)*)EAqFm?t=!Uua~yQNTM2<%}Nwdj@^R48TnjhJLGlQjH?+hW* zhHEO*s7`d?+IGK=>p6}Vr|g^brn0YYaQPVv84TLKW)3-dTdP&CNN5t6(Q|D(X~Kp< zVtAJ$3ty0riwvk%icL(*p(k{0z<*R%x1-jbk?WBq78<2}kKr$WmZToar&sOO#2Bpo zxyc?a$)RJGw~V(;4S11I!5{cw=H21u-%%SbN~WVUG$jW6jS1i9vl^8ibqDW z%*W&T-hqZF5BzV?jxF_x$Oh8uRq~%JcBkS}pXFPFwN$>2pcy=h{4)Gr0#8m74Z>2x ziHR^^NS4SlCApNE?%u4o&GGMxn#8s%$dW_I1NUU`%-JZut$i`26tDkte030^o(P(p zIFVvZC9*CyqIl&-AmLle1TEJoQVfY$iB=tE`Gx2nCi=BsfaLBPHAgboaVFmjV{2h! z0uh`#nr4RmOokN4#vwB`@1R@TR$g~b#I#^duCmWcmG^P9^ekJHZ){&iwR4NQ)d{dECNZL!K*#y(ngdm0)i34F#iZ z%HTgn%0rNk7il0-k-RpoNZ7M!cwUBH=&1)KD0VJ_Ni0j3!hs3Iqi-@aKw+S=8_UWQ zW<6Hv_aa%ox8RE&Mv;h@maC9{b$Kj6BGcPdFZwE8%hofHd4XG}RB{OBd_I>C7 zr|!^?!ZGjX-Eua6#nY>QGK{T9{AFEhVR4ma@GnJ@Cm=ZuG?-p1#Y7VMr!vCj3Qi9_N&7hZ-l zWldx#RW=AGuBLta%vJm6wp2dMK2GBDgd=8*`6+Xk5@|%haV5j<3-^w%`-V>$Ec+Rh ze7WsDDNw)qsE~gsWX^k=Wo=YdIT3sDgL?n++V|nLL@#2IFow4Q=Mq`;zA7iG{^pDsFBx2W$DH5 z^_~Ud`o_DuAfd1Emd3yq8m3og)Bqm_oenF;MV zhj&z{g5mL5&2QV?#u^p!XZ+}8NcS?&BCSfrn*lnnK1rO4m$t81|hws YYn^(2T|As1arb?BX%(qb3A4ce0xkXK%K!iX literal 0 HcmV?d00001 From 793bb058a06b3dfb31c6029f6c30960d1dee8cc7 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Thu, 19 Jun 2014 11:11:36 +0200 Subject: [PATCH 280/546] 8734: reraise in "other" cases with bare "raise" --- src/sage/interfaces/maxima_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 4a934cbf379..b02db2db03d 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -577,7 +577,7 @@ def _create(self, value, name=None): if "Is" in s: # Maxima asked for a condition self._missing_assumption(s) else: - raise error + raise return name def _function_class(self): From 2928b9a4b17c7ccb49a72ee1f1e39b734803ae7e Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Thu, 19 Jun 2014 14:23:08 +0100 Subject: [PATCH 281/546] Fix Singular pexpect timeouts on some platforms This patch calls tcsetattr later in an attempt to work around a bug on OSX Maverics and ancient Linux kernels. --- src/sage/interfaces/expect.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/sage/interfaces/expect.py b/src/sage/interfaces/expect.py index a4ddc514cad..4e47d545ecd 100644 --- a/src/sage/interfaces/expect.py +++ b/src/sage/interfaces/expect.py @@ -437,8 +437,6 @@ def _start(self, alt_message=None, block_during_init=True): os.chdir(current_path) self._expect.timeout = self.__max_startup_time - if not self._terminal_echo: - self._expect.setecho(0) #self._expect.setmaxread(self.__maxread) self._expect.maxread = self.__maxread @@ -451,6 +449,14 @@ def _start(self, alt_message=None, block_during_init=True): failed_to_start.append(self.name()) raise RuntimeError("Unable to start %s"%self.name()) self._expect.timeout = None + + # Calling tcsetattr earlier exposes bugs in various pty + # implementations, see :trac:`16474`. Since we haven't + # **written** anything so far it is safe to wait with + # switching echo off until now. + if not self._terminal_echo: + self._expect.setecho(0) + with gc_disabled(): if block_during_init: for X in self.__init_code: From ef88d2aacafeef5e2286382752de5d314b7f98d2 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 19 Jun 2014 10:30:20 -0700 Subject: [PATCH 282/546] Fixed trivial typo with doctest. --- src/doc/en/thematic_tutorials/lie/affine_hw_crystals.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/thematic_tutorials/lie/affine_hw_crystals.rst b/src/doc/en/thematic_tutorials/lie/affine_hw_crystals.rst index ed395683318..770234ac9d8 100644 --- a/src/doc/en/thematic_tutorials/lie/affine_hw_crystals.rst +++ b/src/doc/en/thematic_tutorials/lie/affine_hw_crystals.rst @@ -89,7 +89,7 @@ with the Littelmann path model:: Now we do an example of a simply-laced (so symmetrizable) hyperbolic type `H_1^{(4)}`, which comes from the complete graph on 4 vertices:: - sage: CM = CartanMatrix([[2, -1, -1,-1],[-1,2,-1,-1],[-1,-1,2,-1],[-1,-1,-1,2]]); M + sage: CM = CartanMatrix([[2, -1, -1,-1],[-1,2,-1,-1],[-1,-1,2,-1],[-1,-1,-1,2]]); CM [ 2 -1 -1 -1] [-1 2 -1 -1] [-1 -1 2 -1] From 8394df4bdcba536e8645d6c36a21344e4580f462 Mon Sep 17 00:00:00 2001 From: Andrey Novoseltsev Date: Thu, 19 Jun 2014 14:44:55 -0600 Subject: [PATCH 283/546] Improve LaTeXing again. --- src/sage/numerical/interactive_simplex_method.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/sage/numerical/interactive_simplex_method.py b/src/sage/numerical/interactive_simplex_method.py index 7d363b71ded..bba520850ae 100644 --- a/src/sage/numerical/interactive_simplex_method.py +++ b/src/sage/numerical/interactive_simplex_method.py @@ -162,7 +162,7 @@ #***************************************************************************** -import re, sys +import operator, re, sys from copy import copy @@ -294,7 +294,13 @@ def _latex_product(coefficients, variables, if latex(c).strip().startswith("-"): sign = "-" c = - c - t = latex(v) if c == 1 else latex(c) + " " + latex(v) + if c == 1: + t = latex(v) + else: + t = latex(c) + if SR(c).operator() in [operator.add, operator.sub]: + t = r"\left( " + t + r" \right)" + t += " " + latex(v) entries.extend([sign, t]) if drop_plus: # Don't start with + for i, e in enumerate(entries): From 135202713a3b0454aa95b896c2c308a8af6d8059 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 19 Jun 2014 16:28:20 -0700 Subject: [PATCH 284/546] Fixed up documentation and some other misc speedups. --- src/sage/game_theory/cooperative_game.py | 339 +++++++++++------------ 1 file changed, 168 insertions(+), 171 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index f24949d8b33..5ede7f91596 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -1,17 +1,14 @@ """ -Co-operative games with N players. +Co-operative Games With Finite Players -This module implements a **basic** implementation of a characteristic function -cooperative game. The main contribution is a class for a characteristic -function game. Methods to calculate the Shapley value (a fair way of sharing -common resources: (see 'Computational aspects of cooperative game theory' by Chalkiadakis et al.) as well as -test properties of the game (monotonicity, super additivity) are also included. +This module implements a class for a characteristic function cooperative +game. Methods to calculate the Shapley value (a fair way of sharing +common resources: see [CEW2011]_) as well as test properties of the game +(monotonicity, superadditivity) are also included. -AUTHOR: - - - James Campbell 06-2014: Original version - - Vince Knight 06-2014 +AUTHORS: +- James Campbell and Vince Knight (06-2014): Original version """ #***************************************************************************** @@ -25,52 +22,51 @@ #***************************************************************************** from itertools import permutations, combinations from sage.misc.misc import powerset -from sage.rings.arith import factorial +from sage.rings.integer import Integer from sage.structure.sage_object import SageObject - class CooperativeGame(SageObject): r""" An object representing a co-operative game. Primarily used to compute the - Shapley Value, but can also provide other information. + Shapley value, but can also provide other information. INPUT: - - ``characteristic_function`` - a dictionary containing all possible sets of players. - * Key - each set must be entered as a tuple, not a string. A - single element tuple must end with a comma. - * Value - A real number - representing each set of players contribution. + - ``characteristic_function`` -- a dictionary containing all possible + sets of players: + + * key - each set must be entered as a tuple, not a string + * value - a real number representing each set of players contribution EXAMPLES: The type of game that is currently implemented is referred to as a - Characteristic Function Game. This is a game on a set $\Omega$ of players - that is defined by a value function $v:C\to \mathbb{R}$ where - $C=2^{\Omega}$ is set of all coalitions of players. An example of such a - game is shown below: - - - `v(c) = \begin{cases} - 0,&\text{ if }c=\emptyset\\ - 6,&\text{ if }c=\{1\}\\ - 12,&\text{ if }c=\{2\}\\ - 42,&\text{ if }c=\{3\}\\ - 12,&\text{ if }c=\{1,2\}\\ - 42,&\text{ if }c=\{1,3\}\\ - 42,&\text{ if }c=\{2,3\}\\ - 42,&\text{ if }c=\{1, 2,3\}\\ - \end{cases}` - - - The function $v$ can be thought of as as a record of contribution of + Characteristic Function Game. This is a game on a set of players + `\Omega` that is defined by a value function `v : C \to \RR` where + `C = 2^{\Omega}` is set of all coalitions of players. Let `N := |\Omega|`. + An example of such a game is shown below: + + .. MATH:: + + v(c) = \begin{cases} + 0 &\text{if } c = \emptyset, \\ + 6 &\text{if } c = \{1\}, \\ + 12 &\text{if } c = \{2\}, \\ + 42 &\text{if } c = \{3\}, \\ + 12 &\text{if } c = \{1,2\}, \\ + 42 &\text{if } c = \{1,3\}, \\ + 42 &\text{if } c = \{2,3\}, \\ + 42 &\text{if } c = \{1,2,3\}. \\ + \end{cases} + + The function `v` can be thought of as as a record of contribution of individuals and coalitions of individuals. Of interest, becomes how to - fairly share the value of the grand coalition ($\Omega$)? This class + fairly share the value of the grand coalition (`\Omega`)? This class allows for such an answer to be formulated by calculating the Shapley value of the game. - Basic examples of how to implement a co-operative game. These functions will - be used repeatedly in other examples. :: + Basic examples of how to implement a co-operative game. These functions + will be used repeatedly in other examples. :: sage: integer_function = {(): 0, ....: (1,): 6, @@ -96,12 +92,12 @@ class CooperativeGame(SageObject): Characteristic function games can be of various types. - A characteristic function game $G=(N,v)$ is monotone if it satisfies - $v(C_2)\geq v(C_1)$ for all $C_1\subseteq C_2$. A characteristic function - game $G=(N,v)$ is super-additive if it satisfies $v(C_2)\geq v(C_1)$ for - all $C_1\subseteq C_2$ such that `C_1\cap C_2 = \emptyset`. + A characteristic function game `G = (N, v)` is monotone if it satisfies + `v(C_2) \geq v(C_1)` for all `C_1 \subseteq C_2`. A characteristic + function game `G = (N, v)` is super-additive if it satisfies `v(C_2) + \geq v(C_1)` for all `C_1 \subseteq C_2` such that `C_1 \cap C_2 = \emptyset`. - We can test if a game is Monotonic or Superadditive. :: + We can test if a game is monotonic or superadditive. :: sage: letter_game.is_monotone() True @@ -109,37 +105,49 @@ class CooperativeGame(SageObject): False Instances have a basic representation that will display basic information - about the game. :: + about the game:: sage: letter_game - A 3 player Co-operative Game. + A 3 player co-operative game - It can be shown that the 'fair' payoff vector, referred to as the + It can be shown that the "fair" payoff vector, referred to as the Shapley value is given by the following formula: - $\phi_i(G)=\frac{1}{N!}\sum_{\pi\in\Pi_n}\Delta_\pi^G(i)$ + .. MATH:: + + \phi_i(G) = \frac{1}{N!} \sum_{\pi\in\Pi_n} \Delta_{\pi}^G(i), where the summation is over the permutations of the players and the marginal contributions of a player for a given permutation is given as: - $\Delta_\pi^G(i)=v(S_{\pi}(i)\cup i)-v(S_{\pi}(i))$ + .. MATH:: + + \Delta_{\pi}^G(i) = v\bigl( S_{\pi}(i) \cup \{i\} \bigr) + - v\bigl( S_{\pi}(i) \bigr) + + where `S_{\pi}(i)` is the number of predecessors of `i` in `\pi`, i.e. + `S_{\pi}(i) = \{ j \mid \pi(i) > \pi(j) \}` (or the number of inversions + of the form `(i, j)`). Note that an equivalent formula for the Shapley value is given by: - $\phi_i(G)=\sum_{s\subseteq\Omega}\sum_{p\in S}\frac{(|s|-1)!(N-|S|)!}{N!}v(s)-v(s\setminus \{i\})$ + .. MATH:: + + \phi_i(G) = \sum_{S \subseteq \Omega} \sum_{p \in S} + \frac{(|S|-1)!(N-|S|)!}{N!} \bigl( v(S) - v(S \setminus \{p\}) \bigr) + = \sum_{S \subseteq \Omega} \sum_{p \in S} + \frac{1}{|S|\binom{N}{|S|}} \bigl( v(S) - v(S \setminus \{p\}) \bigr). This later formulation is implemented in Sage and - requires $2^N-1$ calculations instead of $N!$. + requires `2^N-1` calculations instead of `N!`. - To compute the Shapley value in Sage is simple. :: + To compute the Shapley value in Sage is simple:: sage: letter_game.shapley_value() {'A': 2, 'C': 35, 'B': 5} The following example implements a (trivial) 10 player characteristic - function game: - - $v(c)=|c|\text{ for all }c\in 2^{\Omega}$ + function game with `v(c) = |c|` for all `c \in 2^{\Omega}`. :: @@ -155,30 +163,24 @@ class CooperativeGame(SageObject): various approximation approaches to obtaining the Shapley value of a game. Implementing these would be a worthwhile development. - We can test 3 basic properties of any Payoff Vector $\lambda$. + We can test 3 basic properties of any payoff vector `\lambda`. The Shapley value (described above) is none to be the unique payoff vector that satisfies these and 1 other property - not implemented here (additivity). - They are: - - * Efficiency - `\sum_{i=1}^N\lambda_i=v(\Omega)` - In other words, no value of the total coalition is lost. + not implemented here (additivity). They are: - * The nullplayer property - If `\exists` `i` such that - `v(C\cup i)=v(C)` for all - `C\in 2^{\Omega}` then, `\lambda_i=0`. - In other words: if a player does not - contribute to any coalition then that - player should receive no payoff. + * Efficiency - `\sum_{i=1}^N \lambda_i = v(\Omega)` + In other words, no value of the total coalition is lost. - * Symmetry property - A payoff vector possesses the symmetry property - if `v(C\cup i)=v(C\cup j)` for all - `C\in 2^{\Omega}\setminus{i,j}`, then - `x_i=x_j` + * The nullplayer property - If there exists an `i` such that + `v(C \cup i) = v(C)` for all `C \in 2^{\Omega}` then, `\lambda_i = 0`. + In other words: if a player does not contribute to any coalition then + that player should receive no payoff. - If players contribute symmetrically then they should get the same payoff. + * Symmetry property - A payoff vector possesses the symmetry property + if `v(C \cup i) = v(C \cup j)` for all + `C \in 2^{\Omega} \setminus \{i,j\}`, then `x_i=x_j`. - :: + If players contribute symmetrically then they should get the same payoff:: sage: payoff_vector = letter_game.shapley_value() sage: letter_game.is_efficient(payoff_vector) @@ -188,8 +190,8 @@ class CooperativeGame(SageObject): sage: letter_game.symmetry(payoff_vector) True - Any Payoff Vector can be passed to the game and these properties - can once again be tested. :: + Any payoff vector can be passed to the game and these properties + can once again be tested:: sage: payoff_vector = {'A': 0, 'C': 35, 'B': 3} sage: letter_game.is_efficient(payoff_vector) @@ -201,7 +203,7 @@ class CooperativeGame(SageObject): TESTS: - Checks that the order within a key does not affect other functions. :: + Check that the order within a key does not affect other functions:: sage: letter_function = {(): 0, ....: ('A',): 6, @@ -225,7 +227,7 @@ class CooperativeGame(SageObject): sage: letter_game.symmetry({'A': 2, 'C': 35, 'B': 5}) True - Any Payoff Vector can be passed to the game and these properties can once + Any payoff vector can be passed to the game and these properties can once again be tested. :: sage: letter_game.is_efficient({'A': 0, 'C': 35, 'B': 3}) @@ -235,23 +237,28 @@ class CooperativeGame(SageObject): sage: letter_game.symmetry({'A': 0, 'C': 35, 'B': 3}) True - """ + REFERENCES: + .. [CEW2011] Georgios Chalkiadakis, Edith Elkind, and Michael Wooldridge. + *Computational Aspects of Cooperative Game Theory*. + Morgan & Claypool Publishers, (2011). + ISBN 9781608456529, :doi:`10.2200/S00355ED1V01Y201107AIM016`. + """ def __init__(self, characteristic_function): r""" Initializes a co-operative game and checks the inputs. TESTS: - An attempt to construct a game from an integer. :: + An attempt to construct a game from an integer:: sage: int_game = CooperativeGame(4) Traceback (most recent call last): ... - TypeError: Characteristic function must be a dictionary + TypeError: characteristic function must be a dictionary This test checks that an incorrectly entered singularly tuple will be - changed into a tuple. In this case `(1)` becomes `(1,)`. :: + changed into a tuple. In this case ``(1)`` becomes ``(1,)``:: sage: tuple_function = {(): 0, ....: (1): 6, @@ -263,7 +270,7 @@ def __init__(self, characteristic_function): ....: (1, 2, 3,): 42} sage: tuple_game = CooperativeGame(tuple_function) - This test checks that if a key is not a tuple an error is raised. :: + This test checks that if a key is not a tuple an error is raised:: sage: error_function = {(): 0, ....: (1,): 6, @@ -276,10 +283,10 @@ def __init__(self, characteristic_function): sage: error_game = CooperativeGame(error_function) Traceback (most recent call last): ... - TypeError: Key must be a tuple + TypeError: key must be a tuple - A test to ensure that the Characteristic Function is the power - set of the grand coalition (ie all possible sub-coalitions). :: + A test to ensure that the characteristic function is the power + set of the grand coalition (ie all possible sub-coalitions):: sage: incorrect_function = {(): 0, ....: (1,): 6, @@ -289,17 +296,17 @@ def __init__(self, characteristic_function): sage: incorrect_game = CooperativeGame(incorrect_function) Traceback (most recent call last): ... - ValueError: Characteristic Function must be the power set + ValueError: characteristic function must be the power set """ if type(characteristic_function) is not dict: - raise TypeError("Characteristic function must be a dictionary") + raise TypeError("characteristic function must be a dictionary") self.ch_f = characteristic_function for key in self.ch_f: if len(str(key)) == 1 and type(key) is not tuple: - self.ch_f[key, ] = self.ch_f.pop(key) + self.ch_f[(key,)] = self.ch_f.pop(key) elif type(key) is not tuple: - raise TypeError("Key must be a tuple") + raise TypeError("key must be a tuple") for key in self.ch_f: sortedkey = tuple(sorted(list(key))) self.ch_f[sortedkey] = self.ch_f.pop(key) @@ -307,17 +314,17 @@ def __init__(self, characteristic_function): self.player_list = max(characteristic_function.keys(), key=lambda key: len(key)) for coalition in powerset(self.player_list): if tuple(sorted(list(coalition))) not in sorted(self.ch_f.keys()): - raise ValueError("Characteristic Function must be the power set") + raise ValueError("characteristic function must be the power set") self.number_players = len(self.player_list) def shapley_value(self): r""" - Return the payoff vector for co-operative game. + Return the payoff vector for ``self``. EXAMPLES: - A typical example of the use of shapley_value. :: + A typical example of the use of shapley_value:: sage: integer_function = {(): 0, ....: (1,): 6, @@ -333,7 +340,7 @@ def shapley_value(self): sage: integer_game.shapley_value() {1: 2, 2: 5, 3: 35} - A longer example of the shapley_value. :: + A longer example of the Shapley value:: sage: long_function = {(): 0, ....: (1,): 0, @@ -356,26 +363,27 @@ def shapley_value(self): {1: 70/3, 2: 10, 3: 25/3, 4: 70/3} """ payoff_vector = {} + n = Integer(len(self.player_list)) for player in self.player_list: weighted_contribution = 0 - for coalition in [coalition for coalition in powerset(self.player_list) if len(coalition) != 0]: - weight = factorial(len(coalition) - 1) - weight *= factorial(len(self.player_list) - len(coalition)) - weight /= factorial(len(self.player_list)) - contribution = (self.ch_f[tuple(coalition)] - self.ch_f[tuple([p for p - in coalition if p != player])]) - weighted_contribution += weight * contribution + for coalition in powerset(self.player_list): + if coalition: # If non-empty + k = Integer(len(coalition)) + weight = 1 / (n.binomial(k) * k) + t = tuple(p for p in coalition if p != player) + weighted_contribution += weight * (self.ch_f[tuple(coalition)] + - self.ch_f[t]) payoff_vector[player] = weighted_contribution return payoff_vector def is_monotone(self): r""" - Returns True if co-operative game is monotonic. + Return ``True`` if ``self`` is monotonic. EXAMPLES: - Shows the use of is_monotone on a simple game that is monotone. :: + A simple game that is monotone:: sage: integer_function = {(): 0, ....: (1,): 6, @@ -389,7 +397,7 @@ def is_monotone(self): sage: integer_game.is_monotone() True - Shows the use of is_monotone for a game that is not monotone. :: + An example when the game is not monotone:: sage: integer_function = {(): 0, ....: (1,): 6, @@ -403,7 +411,7 @@ def is_monotone(self): sage: integer_game.is_monotone() False - Shows the use of is_monotone for a longer game. :: + An example on a longer game:: sage: long_function = {(): 0, ....: (1,): 0, @@ -425,17 +433,16 @@ def is_monotone(self): sage: long_game.is_monotone() True """ - sets = list(self.ch_f.keys()) return not any([set(p1) <= set(p2) and self.ch_f[p1] > self.ch_f[p2] - for p1, p2 in permutations(sets, 2)]) + for p1, p2 in permutations(self.ch_f.keys(), 2)]) def is_superadditive(self): r""" - Returns True if co-operative game is superadditive. + Return ``True`` if ``self`` is superadditive. EXAMPLES: - An example that returns False. :: + An example that is not superadditive:: sage: integer_function = {(): 0, ....: (1,): 6, @@ -449,7 +456,7 @@ def is_superadditive(self): sage: integer_game.is_superadditive() False - An example that returns True. :: + An example that is superadditive:: sage: A_function = {(): 0, ....: (1,): 6, @@ -463,9 +470,7 @@ def is_superadditive(self): sage: A_game.is_superadditive() True - An example for is_superadditive with a longer game that returns True. - - :: + An example with a longer game that is superadditive:: sage: long_function = {(): 0, ....: (1,): 0, @@ -487,9 +492,7 @@ def is_superadditive(self): sage: long_game.is_superadditive() True - An example for is_superadditive with a longer game that returns False. - - :: + An example with a longer game that is not:: sage: long_function = {(): 0, ....: (1,): 0, @@ -511,21 +514,19 @@ def is_superadditive(self): sage: long_game.is_superadditive() False """ - sets = list(self.ch_f.keys()) + sets = self.ch_f.keys() for p1, p2 in combinations(sets, 2): - if set(p1) & set(p2) == set(): - union = tuple(sorted(list(set(p1) | set(p2)))) + if not (set(p1) & set(p2)): + union = tuple(sorted(set(p1) | set(p2))) if self.ch_f[union] < self.ch_f[p1] + self.ch_f[p2]: return False return True def _repr_(self): r""" - Returns a concise description of the Game. - - EXAMPLES: + Return a concise description of ``self``. - Basic description of the game shown when calling the game instance. :: + EXAMPLES:: sage: letter_function = {(): 0, ....: ('A',): 6, @@ -537,19 +538,15 @@ def _repr_(self): ....: ('A', 'B', 'C',): 42} sage: letter_game = CooperativeGame(letter_function) sage: letter_game - A 3 player Co-operative Game. - + A 3 player co-operative game """ - np = self.number_players - return "A %s player Co-operative Game." % np + return "A {} player co-operative game".format(self.number_players) def _latex_(self): r""" - Returns the LaTeX code representing the characteristic function. - - EXAMPLES: + Return the LaTeX code representing the characteristic function. - Basic description of the game shown when calling the game instance. :: + EXAMPLES:: sage: letter_function = {(): 0, ....: ('A',): 6, @@ -562,41 +559,39 @@ def _latex_(self): sage: letter_game = CooperativeGame(letter_function) sage: latex(letter_game) v(c) = \begin{cases} - 0,&\text{ if }c=\emptyset\\ - 6,&\text{ if }c=\{A\}\\ - 42,&\text{ if }c=\{C\}\\ - 12,&\text{ if }c=\{B\}\\ - 42,&\text{ if }c=\{B, C\}\\ - 12,&\text{ if }c=\{A, B\}\\ - 42,&\text{ if }c=\{A, C\}\\ - 42,&\text{ if }c=\{A, B, C\}\\ + 0, & \text{if } c = \emptyset \\ + 6, & \text{if } c = \{A\} \\ + 42, & \text{if } c = \{C\} \\ + 12, & \text{if } c = \{B\} \\ + 42, & \text{if } c = \{B, C\} \\ + 12, & \text{if } c = \{A, B\} \\ + 42, & \text{if } c = \{A, C\} \\ + 42, & \text{if } c = \{A, B, C\} \\ \end{cases} """ cf = self.ch_f output = "v(c) = \\begin{cases}\n" - for key in sorted(cf.keys(), key=lambda key: len(key)) : - if key == (): + for key in sorted(cf.keys(), key=lambda key: len(key)): + if not key: # == () coalition = "\\emptyset" else: - coalition = "\{" - for player in key[:-1]: - coalition += "%s, " % player - coalition += "%s\}" % key[-1] - output += "%s,&\\text{ if }c=%s\\\\\n" % (cf[key], coalition) + coalition = "\\{" + ", ".join(str(player) for player in key) + "\\}" + output += "{}, & \\text{{if }} c = {} \\\\\n".format(cf[key], coalition) output += "\\end{cases}" return output def is_efficient(self, payoff_vector): r""" - Returns True if the current payoff_vector is efficient. + Return ``True`` if ``payoff_vector`` is efficient. INPUT: - - ``payoff_vector`` - a dictionary where the key is the player and the value is their payoff. + - ``payoff_vector`` -- a dictionary where the key is the player + and the value is their payoff EXAMPLES: - An efficient payoff_vector. :: + An efficient payoff vector:: sage: letter_function = {(): 0, ....: ('A',): 6, @@ -622,7 +617,7 @@ def is_efficient(self, payoff_vector): sage: letter_game.is_efficient({'A': 10, 'B': 14, 'C': 14}) False - A longer example for is_efficient. :: + A longer example:: sage: long_function = {(): 0, ....: (1,): 0, @@ -649,16 +644,17 @@ def is_efficient(self, payoff_vector): def nullplayer(self, payoff_vector): r""" - Returns True if the current payoff_vector possesses the null - player property. + Return ``True`` if ``payoff_vector`` possesses the nullplayer + property. INPUT: - - ``payoff_vector`` - a dictionary where the key is the player and the value is their payoff. + - ``payoff_vector`` -- a dictionary where the key is the player + and the value is their payoff EXAMPLES: - A payoff_vector that returns True. :: + A payoff vector that returns ``True``:: sage: letter_function = {(): 0, ....: ('A',): 0, @@ -672,7 +668,7 @@ def nullplayer(self, payoff_vector): sage: letter_game.nullplayer({'A': 0, 'B': 14, 'C': 14}) True - A payoff_vector that returns False. :: + A payoff vector that returns ``False``:: sage: A_function = {(): 0, ....: (1,): 0, @@ -686,7 +682,7 @@ def nullplayer(self, payoff_vector): sage: A_game.nullplayer({1: 10, 2: 10, 3: 25}) False - A longer example for nullplayer. :: + A longer example for nullplayer:: sage: long_function = {(): 0, ....: (1,): 0, @@ -710,7 +706,7 @@ def nullplayer(self, payoff_vector): TESTS: - Checks that the function is going through all players. :: + Checks that the function is going through all players:: sage: A_function = {(): 0, ....: (1,): 42, @@ -725,27 +721,27 @@ def nullplayer(self, payoff_vector): False """ for player in self.player_list: - coalitions = [coal for coal in self.ch_f if player in coal] results = [] - for coalit in coalitions: - results.append(self.ch_f[coalit] == self.ch_f[tuple( - sorted(list(set(coalit) - {player})))]) + for coalit in self.ch_f: + if player in coalit: + t = tuple(sorted(set(coalit) - {player})) + results.append(self.ch_f[coalit] == self.ch_f[t]) if all(results) and payoff_vector[player] != 0: return False return True def symmetry(self, payoff_vector): r""" - Returns True if the current payoff_vector possesses the symmetry - property. + Return ``True`` if ``payoff_vector`` possesses the symmetry property. INPUT: - - ``payoff_vector`` - a dictionary where the key is the player and the value is their payoff. + - ``payoff_vector`` -- a dictionary where the key is the player + and the value is their payoff EXAMPLES: - A Payoff Vector that returns True. :: + A payoff pector that has the symmetry property:: sage: letter_function = {(): 0, ....: ('A',): 6, @@ -759,7 +755,7 @@ def symmetry(self, payoff_vector): sage: letter_game.symmetry({'A': 5, 'B': 14, 'C': 20}) True - A Payoff Vector that returns False. :: + A payoff vector that returns ``False``:: sage: integer_function = {(): 0, ....: (1,): 12, @@ -773,7 +769,7 @@ def symmetry(self, payoff_vector): sage: integer_game.symmetry({1: 2, 2: 5, 3: 35}) False - A longer example for symmetry. :: + A longer example for symmetry:: sage: long_function = {(): 0, ....: (1,): 0, @@ -795,14 +791,15 @@ def symmetry(self, payoff_vector): sage: long_game.symmetry({1: 20, 2: 20, 3: 5, 4: 20}) True """ - sets = list(self.ch_f.keys()) + sets = self.ch_f.keys() element = [i for i in sets if len(i) == 1] for c1, c2 in combinations(element, 2): results = [] for m in sets: - junion = tuple(sorted(list(set(c1) | set(m)))) - kunion = tuple(sorted(list(set(c2) | set(m)))) + junion = tuple(sorted(set(c1) | set(m))) + kunion = tuple(sorted(set(c2) | set(m))) results.append(self.ch_f[junion] == self.ch_f[kunion]) - if (all(results) and payoff_vector[c1[0]] != payoff_vector[c2[0]]): + if all(results) and payoff_vector[c1[0]] != payoff_vector[c2[0]]: return False return True + From 6cdd7851601e2fb7764f91d425c93e4649d2e152 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Fri, 20 Jun 2014 00:34:30 +0100 Subject: [PATCH 285/546] Upgrade to libgap 4.7.5.1 and doctest writing to files --- build/pkgs/libgap/checksums.ini | 6 +++--- build/pkgs/libgap/package-version.txt | 2 +- src/sage/libs/gap/test.py | 26 ++++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 src/sage/libs/gap/test.py diff --git a/build/pkgs/libgap/checksums.ini b/build/pkgs/libgap/checksums.ini index 46267a893ac..a575c3e6351 100644 --- a/build/pkgs/libgap/checksums.ini +++ b/build/pkgs/libgap/checksums.ini @@ -1,4 +1,4 @@ tarball=libgap-VERSION.tar.gz -sha1=4ae83174267adbe615be9cab0d1f4a3c53bddb5e -md5=e69513efa609ac1fa095db981b6c7c2b -cksum=2720542483 +sha1=6880c1c36a59051a1e94f4866848a37f4d79cb7c +md5=f797ca3b8d3232cb24b5fe3be704a08a +cksum=1046627126 diff --git a/build/pkgs/libgap/package-version.txt b/build/pkgs/libgap/package-version.txt index 70bc9a9f64c..b37b05b50a9 100644 --- a/build/pkgs/libgap/package-version.txt +++ b/build/pkgs/libgap/package-version.txt @@ -1 +1 @@ -4.7.5 +4.7.5.1 diff --git a/src/sage/libs/gap/test.py b/src/sage/libs/gap/test.py new file mode 100644 index 00000000000..a5eb4c4b026 --- /dev/null +++ b/src/sage/libs/gap/test.py @@ -0,0 +1,26 @@ +""" +Short tests for libGAP +""" + +from sage.libs.all import libgap +from sage.misc.temporary_file import tmp_filename + + +def test_write_to_file(): + """ + Test that libgap can write to files + + See :trac:`16502`, :trac:`15833`. + + EXAMPLES:: + + sage: from sage.libs.gap.test import test_write_to_file + sage: test_write_to_file() + """ + fname = tmp_filename() + message = "Ceci n'est pas une groupe" + libgap.PrintTo(fname, message) + with open(fname, 'r') as f: + assert f.read() == message + SystemFile = libgap.function_factory('StringFile') + assert SystemFile(fname).sage() == message From 1d4cb9318d8cda41082fcefe01f072237ffd79e9 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 19 Jun 2014 16:41:44 -0700 Subject: [PATCH 286/546] Adding additional doc to other functions. --- src/sage/game_theory/cooperative_game.py | 42 ++++++++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 5ede7f91596..a5e982f38d9 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -41,7 +41,7 @@ class CooperativeGame(SageObject): EXAMPLES: The type of game that is currently implemented is referred to as a - Characteristic Function Game. This is a game on a set of players + Characteristic function game. This is a game on a set of players `\Omega` that is defined by a value function `v : C \to \RR` where `C = 2^{\Omega}` is set of all coalitions of players. Let `N := |\Omega|`. An example of such a game is shown below: @@ -94,7 +94,7 @@ class CooperativeGame(SageObject): A characteristic function game `G = (N, v)` is monotone if it satisfies `v(C_2) \geq v(C_1)` for all `C_1 \subseteq C_2`. A characteristic - function game `G = (N, v)` is super-additive if it satisfies `v(C_2) + function game `G = (N, v)` is superadditive if it satisfies `v(C_2) \geq v(C_1)` for all `C_1 \subseteq C_2` such that `C_1 \cap C_2 = \emptyset`. We can test if a game is monotonic or superadditive. :: @@ -176,9 +176,8 @@ class CooperativeGame(SageObject): In other words: if a player does not contribute to any coalition then that player should receive no payoff. - * Symmetry property - A payoff vector possesses the symmetry property - if `v(C \cup i) = v(C \cup j)` for all - `C \in 2^{\Omega} \setminus \{i,j\}`, then `x_i=x_j`. + * Symmetry property - If `v(C \cup i) = v(C \cup j)` for all + `C \in 2^{\Omega} \setminus \{i,j\}`, then `x_i = x_j`. If players contribute symmetrically then they should get the same payoff:: @@ -320,11 +319,20 @@ def __init__(self, characteristic_function): def shapley_value(self): r""" - Return the payoff vector for ``self``. + Return the Shapley value for ``self``. + + The Shapley value is the "fair" payoff vector and + is computed by the following formula: + + .. MATH:: + + \phi_i(G) = \sum_{S \subseteq \Omega} \sum_{p \in S} + \frac{1}{|S|\binom{N}{|S|}} + \bigl( v(S) - v(S \setminus \{p\}) \bigr). EXAMPLES: - A typical example of the use of shapley_value:: + A typical example of computing the Shapley value:: sage: integer_function = {(): 0, ....: (1,): 6, @@ -381,6 +389,9 @@ def is_monotone(self): r""" Return ``True`` if ``self`` is monotonic. + A game `G = (N, v)` is monotonic if it satisfies + `v(C_2) \geq v(C_1)` for all `C_1 \subseteq C_2`. + EXAMPLES: A simple game that is monotone:: @@ -440,6 +451,10 @@ def is_superadditive(self): r""" Return ``True`` if ``self`` is superadditive. + A game `G = (N, v)` is superadditive if it satisfies + `v(C_2) \geq v(C_1)` for all `C_1 \subseteq C_2` such + that `C_1 \cap C_2 = \emptyset`. + EXAMPLES: An example that is not superadditive:: @@ -584,6 +599,10 @@ def is_efficient(self, payoff_vector): r""" Return ``True`` if ``payoff_vector`` is efficient. + A payoff vector `v` is efficient if + `\sum_{i=1}^N \lambda_i = v(\Omega)`; + in other words, no value of the total coalition is lost. + INPUT: - ``payoff_vector`` -- a dictionary where the key is the player @@ -647,6 +666,11 @@ def nullplayer(self, payoff_vector): Return ``True`` if ``payoff_vector`` possesses the nullplayer property. + A payoff vector `v` has the nullplayer property if there exists + an `i` such that `v(C \cup i) = v(C)` for all `C \in 2^{\Omega}` + then, `\lambda_i = 0`. In other words: if a player does not + contribute to any coalition then that player should receive no payoff. + INPUT: - ``payoff_vector`` -- a dictionary where the key is the player @@ -734,6 +758,10 @@ def symmetry(self, payoff_vector): r""" Return ``True`` if ``payoff_vector`` possesses the symmetry property. + A payoff vector possesses the symmetry property if + `v(C \cup i) = v(C \cup j)` for all + `C \in 2^{\Omega} \setminus \{i,j\}`, then `x_i = x_j`. + INPUT: - ``payoff_vector`` -- a dictionary where the key is the player From c8f5e49fd56d7ccbc763f421d2917db8413b8906 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 19 Jun 2014 16:57:57 -0700 Subject: [PATCH 287/546] Fixed typo and added projection image for level 0 crystals. --- .../en/thematic_tutorials/lie/affine_finite_crystals.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/en/thematic_tutorials/lie/affine_finite_crystals.rst b/src/doc/en/thematic_tutorials/lie/affine_finite_crystals.rst index f770d7ec45e..a3e0cd141a0 100644 --- a/src/doc/en/thematic_tutorials/lie/affine_finite_crystals.rst +++ b/src/doc/en/thematic_tutorials/lie/affine_finite_crystals.rst @@ -478,9 +478,9 @@ Single column KR crystals A single column KR crystal is `B^{r,1}` for any `r \in I_0`. In [LNSSS14I]_ and [LNSSS14II]_, it was shown that single column KR -crystals can be constructed by projecting level 0 crystals (using LS paths). -We first vertify that we do get an isomorphic crystal for `B^{1,1}` -in type `E_6^{(1)}`:: +crystals can be constructed by projecting level 0 crystals of LS paths onto +the classical weight lattice. We first verify that we do get an isomorphic +crystal for `B^{1,1}` in type `E_6^{(1)}`:: sage: K = crystals.KirillovReshetikhin(['E',6,1], 1,1) sage: K2 = crystals.kirillov_reshetikhin.LSPaths(['E',6,1], 1,1) From f406d2e45dde540c5e75837660a18d9bdf076944 Mon Sep 17 00:00:00 2001 From: Jayant Date: Thu, 19 Jun 2014 23:50:37 -0400 Subject: [PATCH 288/546] Fixed bugs reported by Stefan in comment 15 and 18. --- src/sage/matroids/matroid.pyx | 18 ++++---- src/sage/matroids/matroids_plot_helpers.py | 49 ++++++++++++---------- 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index 69ed887a997..fe3ce494e58 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -4761,7 +4761,7 @@ cdef class Matroid(SageObject): - ``B`` -- (optional) a list containing a basis. If internal point placement is used, these elements will be placed as vertices of a triangle. - - ``lineorders`` -- (optional) A list of lists where each of the inner lists + - ``lineorders`` -- (optional) A list of lists where each of the inner lists specify ground set elements in a certain order which will be used to draw the corresponding line in geometric representation (if it exists). - ``pos_method`` -- An integer specifying positioning method. @@ -4792,20 +4792,20 @@ cdef class Matroid(SageObject): if pos_method == 1 and pos_dict != None: # check sanity of pos_dict and add it to cached info if sane if matroids_plot_helpers.posdict_is_sane(self, pos_dict) == True: - self._cached_info={'positions':pos_dict, 'lineorders':lineorders} + self._cached_info={'plot_positions':pos_dict, 'lineorders':lineorders} # placeholder for aditional placement methods. Only need to compute positions and update self._cached_info elif pos_method == 2: raise NotImplementedError if self._cached_info == None: - self._cached_info={'positions':None,'lineorders': None} - if 'positions' not in self._cached_info.keys(): - self._cached_info['positions'] = None + self._cached_info={'plot_positions':None,'lineorders': None} + if 'plot_positions' not in self._cached_info.keys(): + self._cached_info['plot_positions'] = None if 'lineorders' not in self._cached_info.keys(): self._cached_info['lineorders'] = None if self.rank() > 3: - return + raise NotImplementedError elif B == None: B = list(self.basis()) elif B != None and self.is_basis(B)==False: @@ -4888,13 +4888,15 @@ cdef class Matroid(SageObject): sage: M._fix_positions(pos_dict=pos) sage: M._cached_info['lineorders'] is None True - sage: M._cached_info['positions']['k'] + sage: M._cached_info['plot_positions']['k'] (0, 0) """ # check sanity of pos_dict and add it to cached info if sane + if self.rank() > 3: + raise NotImplementedError if(pos_dict!=None): import matroids_plot_helpers if matroids_plot_helpers.posdict_is_sane(self,pos_dict) ==True: - self._cached_info={'positions':pos_dict,'lineorders':lineorders} + self._cached_info={'plot_positions':pos_dict,'lineorders':lineorders} return diff --git a/src/sage/matroids/matroids_plot_helpers.py b/src/sage/matroids/matroids_plot_helpers.py index c01bc704b77..51de416082a 100644 --- a/src/sage/matroids/matroids_plot_helpers.py +++ b/src/sage/matroids/matroids_plot_helpers.py @@ -34,7 +34,7 @@ 3) Internal point placement and orders deciding heuristics If a custom point placement and/or line orders is desired, then user can simply specify the custom points dictionary as ``M.cached info = - {'positions':,'lineorders':}`` + {'plot_positions':,'plot_lineorders':}`` @@ -52,7 +52,7 @@ sage: pos_dict= {0: (0, 0), 1: (2, 0), 2: (1, 2), 3: (1.5, 1.0), ....: 4: (0.5, 1.0), 5: (1.0, 0.0), 6: (1.0, 0.6666666666666666), ....: 7: (3,3), 8: (4,0), 9: (-1,1), 10: (-2,-2)} - sage: M1._cached_info={'positions': pos_dict, 'lineorders': None} + sage: M1._cached_info={'plot_positions': pos_dict, 'plot_lineorders': None} sage: matroids_plot_helpers.geomrep(M1, sp=True) """ @@ -509,7 +509,7 @@ def addlp(M, M1, L, P, ptsdict, G=None, limits=None): color='black', fill=False, thickness=4) G += text(looptext, (rectx+0.5, recty+0.3), color='black', fontsize=13) - G += point((rectx+0.2, recty+0.3), color='black', size=300, zorder=2) + G += point((rectx+0.2, recty+0.3), color='gray', size=300, zorder=2) G += text('Loop(s)', (rectx+0.5+0.4*len(loops)+0.1, recty+0.3), fontsize=13, color='black') limits = tracklims(limits, [rectx, rectx+rectw], [recty, recty+recth]) @@ -517,7 +517,7 @@ def addlp(M, M1, L, P, ptsdict, G=None, limits=None): if len(P) > 0: # create list of lists where inner lists are parallel classes pcls = [] - gnd = sorted(M1.groundset_list()) + gnd = sorted(list(M1.groundset())) for g in gnd: pcl = [g] for p in P: @@ -531,9 +531,9 @@ def addlp(M, M1, L, P, ptsdict, G=None, limits=None): # add side by side ptsdict[pcl[1]] = (basept[0], basept[1]-0.13) G += points(zip([basept[0]], [basept[1]-0.13]), - color='black', size=300, zorder=2) + color='gray', size=300, zorder=2) G += text(pcl[1], (float(basept[0]), - float(basept[1])-0.13), color='white', + float(basept[1])-0.13), color='black', fontsize=13) limits = tracklims(limits, [basept[0]], [basept[1]-0.13]) else: @@ -765,14 +765,17 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): if M.rank() == 1: if M._cached_info is not None and \ - 'positions' in M._cached_info.keys() and \ - M._cached_info['positions'] is not None: - pts = M._cached_info['positions'] + 'plot_positions' in M._cached_info.keys() and \ + M._cached_info['plot_positions'] is not None: + pts = M._cached_info['plot_positions'] else: pts = {} gnd = sorted(M.groundset()) pts[gnd[0]] = (1, float(2)/3) - G += point((1, float(2)/3), size=300, zorder=2) + G += point((1, float(2)/3), size=300, color='gray', zorder=2) + pt=[1, float(2)/3] + G += text(gnd[0], (float(pt[0]), float(pt[1])), color='black', + fontsize=13) pts2 = pts # track limits [xmin,xmax,ymin,ymax] pl = [list(x) for x in pts2.values()] @@ -786,9 +789,9 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): bline.append(j) interval = len(bline)+1 if M._cached_info is not None and \ - 'positions' in M._cached_info.keys() and \ - M._cached_info['positions'] is not None: - pts2 = M._cached_info['positions'] + 'plot_positions' in M._cached_info.keys() and \ + M._cached_info['plot_positions'] is not None: + pts2 = M._cached_info['plot_positions'] else: pts2 = {} pts2[B1[0]] = (0, 0) @@ -800,7 +803,7 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): pts2[bline[k]] = (cc*lpt[0]+(1-cc)*rpt[0], cc*lpt[1]+(1-cc)*rpt[1]) if sp is True: - M._cached_info['positions'] = pts2 + M._cached_info['plot_positions'] = pts2 # track limits [xmin,xmax,ymin,ymax] pl = [list(x) for x in pts2.values()] lims = tracklims([None, None, None, None], [pt[0] for pt in pl], @@ -812,15 +815,15 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): allpts = [list(pts2[i]) for i in M.groundset()] xpts = [float(k[0]) for k in allpts] ypts = [float(k[1]) for k in allpts] - G += points(zip(xpts, ypts), color='black', size=300, zorder=2) + G += points(zip(xpts, ypts), color='gray', size=300, zorder=2) for i in pts2: pt = list(pts2[i]) - G += text(i, (float(pt[0]), float(pt[1])), color='white', + G += text(i, (float(pt[0]), float(pt[1])), color='black', fontsize=13) else: if M._cached_info is None or \ - 'positions' not in M._cached_info.keys() or \ - M._cached_info['positions'] is None: + 'plot_positions' not in M._cached_info.keys() or \ + M._cached_info['plot_positions'] is None: (pts, trilines, nontripts, curvedlines) = it(M1, B1, list(set(M.groundset())-set(B1)), @@ -828,7 +831,7 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): pts2 = addnontripts([B1[0], B1[1], B1[2]], nontripts, pts) trilines.extend(curvedlines) else: - pts2 = M._cached_info['positions'] + pts2 = M._cached_info['plot_positions'] trilines = [list(set(list(x)).difference(L | P)) for x in M1.flats(2) if len(list(x)) >= 3] @@ -844,14 +847,14 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): allpts = [list(pts2[i]) for i in M.groundset()] xpts = [float(k[0]) for k in allpts] ypts = [float(k[1]) for k in allpts] - G += points(zip(xpts, ypts), color='black', size=300, zorder=2) + G += points(zip(xpts, ypts), color='gray', size=300, zorder=2) for i in pts2: pt = list(pts2[i]) - G += text(i, (float(pt[0]), float(pt[1])), color='white', + G += text(i, (float(pt[0]), float(pt[1])), color='black', fontsize=13) if sp is True: - M1._cached_info['positions'] = pts2 - M1._cached_info['lineorders'] = lineorders1 + M1._cached_info['plot_positions'] = pts2 + M1._cached_info['plot_lineorders'] = lineorders1 # deal with loops and parallel elements G, lims = addlp(M1, M, L, P, pts2, G, lims) G.axes(False) From b90cb76eb10225c28257081feafdd4bb3effbd40 Mon Sep 17 00:00:00 2001 From: Punarbasu Purkayastha Date: Fri, 20 Jun 2014 12:19:59 +0800 Subject: [PATCH 289/546] sort and reverse the excluded_points in one command --- src/sage/plot/plot.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index 5ec17a4a1b9..7f792a55116 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -1375,7 +1375,7 @@ def _plot(funcs, xrange, parametric=False, excluded_points.append(x) data = newdata - excluded_points.sort() + excluded_points.sort(reverse=True) G = Graphics() fillcolor = options.pop('fillcolor', 'automatic') @@ -1456,7 +1456,6 @@ def _plot(funcs, xrange, parametric=False, # setup for exclusion points exclusion_point = 0 if excluded_points: - excluded_points.reverse() exclusion_point = excluded_points.pop() flag = True From e79bdb8e73d8ebdca5cb6782ca1e4ae7cd17b96e Mon Sep 17 00:00:00 2001 From: Punarbasu Purkayastha Date: Fri, 20 Jun 2014 17:20:56 +0800 Subject: [PATCH 290/546] fix parametric and polar plots if exclude point is outside the domain --- src/sage/plot/plot.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index 7f792a55116..18bda40ebd5 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -1137,6 +1137,17 @@ def plot(funcs, *args, **kwds): Check that :trac:`15030` is fixed:: sage: plot(abs(log(x)), x) + + Check that if excluded points are less than xmin then the exclusion + still works for polar and parametric plots. The following should + show two excluded points:: + + sage: set_verbose(-1) + sage: polar_plot(sin(sqrt(x^2-1)), (x,0,2*pi), exclude=[1/2,2,3]) + + sage: parametric_plot((sqrt(x^2-1),sqrt(x^2-1/2)), (x,0,5), exclude=[1,2,3]) + + sage: set_verbose(0) """ G_kwds = Graphics._extract_kwds_for_show(kwds, ignore=['xmin', 'xmax']) @@ -1478,7 +1489,16 @@ def _plot(funcs, xrange, parametric=False, start_index = i+2 # exclude points - if flag and (x0 <= exclusion_point <= x1): + if x0 > exclusion_point: + while exclusion_point <= x1: + try: + exclusion_point = excluded_points.pop() + except IndexError: + # all excluded points were considered + flag = False + break + + elif flag and (x0 <= exclusion_point <= x1): G += line(data[start_index:i], **options) start_index = i + 2 while exclusion_point <= x1: From 162b83c0772a9c2a02eb3cea71028e8d14a6c0a7 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 20 Jun 2014 12:03:30 +0200 Subject: [PATCH 291/546] trac #16430: Many bugfixes --- src/sage/combinat/designs/bibd.py | 53 ++++++++++++++----- src/sage/combinat/designs/designs_pyx.pyx | 3 ++ .../combinat/designs/orthogonal_arrays.py | 45 ++++++++++------ 3 files changed, 72 insertions(+), 29 deletions(-) diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index b36333a9e94..ffbc5beb147 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -677,32 +677,61 @@ def _check_pbd(B,v,S): Traceback (most recent call last): ... RuntimeError: All integers of S must be >=2 + + TESTS:: + + sage: _check_pbd([[1,2]],2,[2]) + Traceback (most recent call last): + ... + RuntimeError: The PBD covers a point 2 which is not in [0,...,1] + sage: _check_pbd([[1,2]]*2,2,[2]) + Traceback (most recent call last): + ... + RuntimeError: The pair (1,2) is covered more than once + sage: _check_pbd([],2,[2]) + Traceback (most recent call last): + ... + RuntimeError: The pair (0,1) is not covered + sage: _check_pbd([[1,2],[1]],2,[2]) + Traceback (most recent call last): + ... + RuntimeError: A block has size 1 while S=[2] """ from itertools import combinations from sage.graphs.graph import Graph - if not all(len(X) in S for X in B): - raise RuntimeError("Some block has wrong size: this is not a nice honest PBD from the good old days !") + for X in B: + if len(X) not in S: + raise RuntimeError("A block has size {} while S={}".format(len(X),S)) if any(x < 2 for x in S): raise RuntimeError("All integers of S must be >=2") if v == 0 or v == 1: if B: - raise RuntimeError("This is not a nice honest PBD from the good old days!") - else: - return + raise RuntimeError("A PBD with v<=1 is expected to be empty.") g = Graph() + g.add_vertices(range(v)) m = 0 for X in B: - g.add_edges(list(combinations(X,2))) - if g.size() != m+binomial(len(X),2): - raise RuntimeError("This is not a nice honest PBD from the good old days !") - m = g.size() - - if not (g.is_clique() and g.vertices() == range(v)): - raise RuntimeError("This is not a nice honest PBD from the good old days !") + for i,j in combinations(X,2): + g.add_edge(i,j) + m_tmp = g.size() + if m_tmp != m+1: + raise RuntimeError("The pair ({},{}) is covered more than once".format(i,j)) + m = m_tmp + + if g.vertices() != range(v): + p = list(set(g.vertices())-set(range(v)))[0] + raise RuntimeError("The PBD covers a point {} which is not in [0,...,{}]".format(p,v-1)) + if not g.is_clique(): + for p1 in g: + if g.degree(p1) != v-1: + break + neighbors = g.neighbors(p1)+[p1] + p2 = list(set(g.vertices())-set(neighbors))[0] + raise RuntimeError("The pair ({},{}) is not covered".format(p1,p2)) return B diff --git a/src/sage/combinat/designs/designs_pyx.pyx b/src/sage/combinat/designs/designs_pyx.pyx index 862272a8dad..d211f3af79c 100644 --- a/src/sage/combinat/designs/designs_pyx.pyx +++ b/src/sage/combinat/designs/designs_pyx.pyx @@ -95,6 +95,9 @@ def is_orthogonal_array(OA, int k, int n, int t=2, verbose=False, terminology="O "MOLS" : "All squares do not have dimension n^2={}^2".format(n)}[terminology] return False + if n == 0: + return True + cdef int i,j,l # A copy of OA diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 86c237e3157..f6187dd1a73 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -265,7 +265,7 @@ def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): sage: designs.transversal_design(None, 1) Traceback (most recent call last): ... - ValueError: there are no bound on k when 0<=n<=1 + ValueError: there is no upper bound on k when 0<=n<=1 """ # Is k is None we find the largest available if k is None: @@ -273,7 +273,7 @@ def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): if existence: from sage.rings.infinity import Infinity return Infinity - raise ValueError("there are no bound on k when 0<=n<=1") + raise ValueError("there is no upper bound on k when 0<=n<=1") k = orthogonal_array(None,n,existence=True) if existence: @@ -801,25 +801,34 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): TESTS: - The special case `n=1`:: + The special cases `n=0,1`:: + sage: designs.orthogonal_array(3,0) + [] sage: designs.orthogonal_array(3,1) [[0, 0, 0]] + sage: designs.orthogonal_array(None,0,existence=True) + +Infinity sage: designs.orthogonal_array(None,1,existence=True) +Infinity sage: designs.orthogonal_array(None,1) Traceback (most recent call last): ... - ValueError: there are no bound on k when 0<=n<=1 - sage: designs.orthogonal_array(None,14,existence=True) - 6 + ValueError: there is no upper bound on k when 0<=n<=1 + sage: designs.orthogonal_array(None,0) + Traceback (most recent call last): + ... + ValueError: there is no upper bound on k when 0<=n<=1 + sage: designs.orthogonal_array(16,0) + [] sage: designs.orthogonal_array(16,1) [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] when `t>2` and `k=None`:: - sage: designs.orthogonal_array(None,5,t=3,existence=True) - 1 + sage: t = 3 + sage: designs.orthogonal_array(None,5,t=t,existence=True) == t + True """ from latin_squares import mutually_orthogonal_latin_squares @@ -832,21 +841,23 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): if existence: from sage.rings.infinity import Infinity return Infinity - raise ValueError("there are no bound on k when 0<=n<=1") + raise ValueError("there is no upper bound on k when 0<=n<=1") elif t == 2 and projective_plane(n,existence=True): k = n+1 else: - for k in range(1,n+2): + for k in range(t-1,n+2): if not orthogonal_array(k+1,n,t=t,existence=True): break if existence: return k - if k < 2: - raise ValueError("undefined for k less than 2") + if k < t: + raise ValueError("undefined for k= n+t: # When t=2 then k=n+t except when n<=1") - elif k == t: + elif k <= t: if existence: return True from itertools import product - OA = map(list, product(range(n), repeat=k)) + return map(list, product(range(n), repeat=k)) elif n in OA_constructions and k <= OA_constructions[n][0]: if existence: @@ -1453,7 +1464,7 @@ def OA_from_PBD(k,n,PBD, check=True): sage: _ = OA_from_PBD(3,6,pbd) Traceback (most recent call last): ... - RuntimeError: This is not a nice honest PBD from the good old days ! + RuntimeError: The PBD covers a point 8 which is not in [0,...,5] """ # Size of the sets of the PBD K = set(map(len,PBD)) From 33449c150096c7db29eb46f2810f3694bfaf47bf Mon Sep 17 00:00:00 2001 From: Punarbasu Purkayastha Date: Fri, 20 Jun 2014 20:59:12 +0800 Subject: [PATCH 292/546] add "# rel tol" to the tests --- src/sage/functions/trig.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/functions/trig.py b/src/sage/functions/trig.py index 020c560f19c..757d0d5bb3f 100644 --- a/src/sage/functions/trig.py +++ b/src/sage/functions/trig.py @@ -36,7 +36,7 @@ def __init__(self): sage: conjugate(sin(x)) sin(conjugate(x)) - sage: sin(complex(1,1)) + sage: sin(complex(1,1)) # rel tol (1.2984575814159773+0.6349639147847361j) """ @@ -76,7 +76,7 @@ def __init__(self): sage: conjugate(cos(x)) cos(conjugate(x)) - sage: cos(complex(1,1)) + sage: cos(complex(1,1)) # rel tol (0.8337300251311491-0.9888977057628651j) """ @@ -120,7 +120,7 @@ def __init__(self): sage: conjugate(tan(x)) tan(conjugate(x)) - sage: tan(complex(1,1)) + sage: tan(complex(1,1)) # rel tol (0.2717525853195118+1.0839233273386946j) """ @@ -176,7 +176,7 @@ def _evalf_(self, x, parent=None, algorithm=None): Test complex input:: - sage: sec(complex(1,1)) + sage: sec(complex(1,1)) # rel tol (0.49833703055518686+0.5910838417210451j) """ @@ -275,7 +275,7 @@ def _evalf_(self, x, parent=None, algorithm=None): Test complex input:: - sage: csc(complex(1,1)) + sage: csc(complex(1,1)) # rel tol (0.6215180171704284-0.30393100162842646j) """ @@ -403,7 +403,7 @@ def _evalf_(self, x, parent=None, algorithm=None): Test complex input:: - sage: cot(complex(1,1)) + sage: cot(complex(1,1)) # rel tol (0.21762156185440273-0.8680141428959249j) """ @@ -636,7 +636,7 @@ def _evalf_(self, x, parent=None, algorithm=None): Test complex input:: - sage: arccot(complex(1,1)) + sage: arccot(complex(1,1)) # rel tol (0.5535743588970452-0.4023594781085251j) """ @@ -718,7 +718,7 @@ def _evalf_(self, x, parent=None, algorithm=None): Test complex input:: - sage: arccsc(complex(1,1)) + sage: arccsc(complex(1,1)) # rel tol (0.45227844715119064-0.5306375309525178j) """ @@ -794,7 +794,7 @@ def _evalf_(self, x, parent=None, algorithm=None): Test complex input:: - sage: arcsec(complex(1,1)) + sage: arcsec(complex(1,1)) # rel tol (1.118517879643706+0.5306375309525178j) """ From f1b82be958b1d4d1653a57113b087ca349818788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 20 Jun 2014 20:40:18 +0200 Subject: [PATCH 293/546] trac #14288 correct doctest continuations --- .../numerical/interactive_simplex_method.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/sage/numerical/interactive_simplex_method.py b/src/sage/numerical/interactive_simplex_method.py index bba520850ae..54a8b842e0b 100644 --- a/src/sage/numerical/interactive_simplex_method.py +++ b/src/sage/numerical/interactive_simplex_method.py @@ -214,7 +214,7 @@ def _assemble_arrayl(lines, stretch=None): EXAMPLES:: sage: from sage.numerical.interactive_simplex_method \ - ... import _assemble_arrayl + ....: import _assemble_arrayl sage: lines = ["1 + 1", "2"] sage: print _assemble_arrayl(lines) \begin{array}{l} @@ -279,7 +279,7 @@ def _latex_product(coefficients, variables, TESTS:: sage: from sage.numerical.interactive_simplex_method import \ - ... _latex_product + ....: _latex_product sage: var("x, y") (x, y) sage: print _latex_product([-1, 3], [x, y]) @@ -353,7 +353,7 @@ def variable(R, v): EXAMPLES:: sage: from sage.numerical.interactive_simplex_method \ - ... import variable + ....: import variable sage: R = PolynomialRing(QQ, "x3, y5, x5, y") sage: R.inject_variables() Defining x3, y5, x5, y @@ -468,12 +468,12 @@ class LPProblem(SageObject): Same problem, but more explicitly:: sage: P = LPProblem(A, b, c, ["C", "B"], - ... constraint_type="<=", variable_type=">=") + ....: constraint_type="<=", variable_type=">=") Even more explicitly:: sage: P = LPProblem(A, b, c, ["C", "B"], problem_type="max", - ... constraint_type=["<=", "<="], variable_type=[">=", ">="]) + ....: constraint_type=["<=", "<="], variable_type=[">=", ">="]) Using the last form you should be able to represent any LP problem, as long as all like terms are collected and in constraints variables and constants @@ -2005,7 +2005,7 @@ def slack_variables(self): sage: P.slack_variables() (x3, x4) sage: P = LPProblemStandardForm(A, b, c, ["C", "B"], - ... slack_variables=["L", "F"]) + ....: slack_variables=["L", "F"]) sage: P.slack_variables() (L, F) """ @@ -2610,7 +2610,7 @@ class LPDictionary(LPAbstractDictionary): sage: c = vector(QQ, (10, 5)) sage: R = PolynomialRing(QQ, "x1, x2, x3, x4", order="neglex") sage: from sage.numerical.interactive_simplex_method \ - ... import LPDictionary + ....: import LPDictionary sage: D2 = LPDictionary(A, b, c, 0, R.gens()[2:], R.gens()[:2], "z") sage: D2 == D True @@ -2628,7 +2628,7 @@ def __init__(self, A, b, c, objective_value, sage: c = vector(QQ, (10, 5)) sage: R = PolynomialRing(QQ, "x1, x2, x3, x4", order="neglex") sage: from sage.numerical.interactive_simplex_method \ - ... import LPDictionary + ....: import LPDictionary sage: D = LPDictionary(A, b, c, 0, R.gens()[2:], R.gens()[:2], "z") sage: TestSuite(D).run() """ @@ -2667,7 +2667,7 @@ def __eq__(self, other): sage: c = vector(QQ, (10, 5)) sage: R = PolynomialRing(QQ, "x1, x2, x3, x4", order="neglex") sage: from sage.numerical.interactive_simplex_method \ - ... import LPDictionary + ....: import LPDictionary sage: D2 = LPDictionary(A, b, c, 0, R.gens()[2:], R.gens()[:2], "z") sage: D2 == D True @@ -3055,7 +3055,7 @@ def random_dictionary(m, n, bound=5, special_probability=0.2): EXAMPLES:: sage: from sage.numerical.interactive_simplex_method \ - ... import random_dictionary + ....: import random_dictionary sage: random_dictionary(3, 4) LP problem dictionary (use typeset mode to see details) """ @@ -3152,7 +3152,7 @@ class LPRevisedDictionary(LPAbstractDictionary): sage: c = (10, 5) sage: P = LPProblemStandardForm(A, b, c) sage: from sage.numerical.interactive_simplex_method \ - ... import LPRevisedDictionary + ....: import LPRevisedDictionary sage: D = LPRevisedDictionary(P, [1, 2]) sage: D.basic_variables() (x1, x2) @@ -3204,7 +3204,7 @@ def __init__(self, problem, basic_variables): sage: c = (10, 5) sage: P = LPProblemStandardForm(A, b, c) sage: from sage.numerical.interactive_simplex_method \ - ... import LPRevisedDictionary + ....: import LPRevisedDictionary sage: D = LPRevisedDictionary(P, [1, 2]) sage: TestSuite(D).run() """ @@ -3237,7 +3237,7 @@ def __eq__(self, other): sage: c = (10, 5) sage: P = LPProblemStandardForm(A, b, c) sage: from sage.numerical.interactive_simplex_method \ - ... import LPRevisedDictionary + ....: import LPRevisedDictionary sage: D1 = LPRevisedDictionary(P, [1, 2]) sage: D2 = LPRevisedDictionary(P, [1, 2]) sage: D1 is D2 From aa1fdf0341c089c183e8a0fb8ee4eea22c35f393 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 20 Jun 2014 12:31:41 -0700 Subject: [PATCH 294/546] First pass: documentation formatting. --- .../numerical/interactive_simplex_method.py | 1607 ++++++++--------- 1 file changed, 802 insertions(+), 805 deletions(-) diff --git a/src/sage/numerical/interactive_simplex_method.py b/src/sage/numerical/interactive_simplex_method.py index 54a8b842e0b..338b3dfeeac 100644 --- a/src/sage/numerical/interactive_simplex_method.py +++ b/src/sage/numerical/interactive_simplex_method.py @@ -2,7 +2,7 @@ Interactive Simplex Method The purpose of this module is to support learning and exploring of the simplex -method. While it does allow solving Linear Programming Problems (LPPs) in a +method. While it does allow solving Linear Programming Problems (LPPs) in a number of ways, it may require explicit (and correct!) description of steps and is likely to be much slower than "regular" LP solvers. Therefore, do not use this module if all you want is the result. If, however, you want to @@ -10,7 +10,7 @@ using different strategies, but don't want to deal with tedious arithmetic, this module is for you! -Historically it was created to complement Math 373 course on Mathematical +Historically it was created to complement the Math 373 course on Mathematical Programming and Optimization at the University of Alberta, Edmonton, Canada. AUTHORS: @@ -24,14 +24,14 @@ .. admonition:: Corn & Barley A farmer has 1000 acres available to grow corn and barley. - Corn has a net profit of 10 dollars per acre while barley has a net profit - of 5 dollars per acre. - The farmer has 1500 kg of fertilizer available with 3 kg per acre needed for - corn and 1 kg per acre needed for barley. + Corn has a net profit of 10 dollars per acre while barley has a net + profit of 5 dollars per acre. + The farmer has 1500 kg of fertilizer available with 3 kg per acre + needed for corn and 1 kg per acre needed for barley. The farmer wants to maximize profit. (Sometimes we also add one more constraint to make the initial dictionary infeasible: the farmer has to use at least 40% of the available land.) - + Using variables `C` and `B` for land used to grow corn and barley respectively, in acres, we can construct the following LP problem:: @@ -45,7 +45,7 @@ It is recommended to copy-paste such examples into your own worksheet, so that you can run these commands with typeset mode on and get -.. math:: +.. MATH:: \begin{array}{l} \begin{array}{lcrcrcl} @@ -59,14 +59,14 @@ Since it has only two variables, we can solve it graphically:: sage: P.plot() - - + + The simplex method can be applied only to :class:`problems in standard form `, which can be created either directly :: sage: LPProblemStandardForm(A, b, c, ["C", "B"]) LP problem (use typeset mode to see details) - + or from an already constructed problem of "general type" :: sage: P = P.standard_form() @@ -87,10 +87,10 @@ sage: D = P.initial_dictionary() sage: D LP problem dictionary (use typeset mode to see details) - + Using typeset mode as recommended, you'll see -.. math:: +.. MATH:: \renewcommand{\arraystretch}{1.5} \begin{array}{|rcrcrcr|} @@ -108,7 +108,7 @@ True sage: D.is_optimal() False - + You can look at many of its pieces and associated data:: sage: D.basic_variables() @@ -124,7 +124,7 @@ sage: D.enter("C") sage: D.leave(4) sage: D.update() - + If everything was done correctly, the new dictionary is still feasible and the objective value did not decrease:: @@ -132,7 +132,7 @@ True sage: D.objective_value() 5000 - + If you are unsure about picking entering and leaving variables, you can use helper methods that will try their best to tell you what are your next options:: @@ -142,9 +142,9 @@ Traceback (most recent call last): ... ValueError: leaving variables can be determined - for feasible dictionaries with a set entering variable - or for dual feasible dictionaries - + for feasible dictionaries with a set entering variable + or for dual feasible dictionaries + It is also possible to obtain :meth:`feasible sets ` and :meth:`final dictionaries ` of problems, work with :class:`revised dictionaries `, and use the dual @@ -156,8 +156,8 @@ # Copyright (C) 2013 Andrey Novoseltsev # # Distributed under the terms of the GNU General Public License (GPL) -# as published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** @@ -199,20 +199,20 @@ def _assemble_arrayl(lines, stretch=None): r""" Return ``lines`` assembled in a left-justified array. - + INPUT: - + - ``lines`` -- a list of strings suitable for math mode typesetting; - + - ``stretch`` -- (default: None) if given, a command setting ``\arraystretch`` to this value will be added before the array. - + OUTPUT: - + - a :class:`LatexExpr`. - + EXAMPLES:: - + sage: from sage.numerical.interactive_simplex_method \ ....: import _assemble_arrayl sage: lines = ["1 + 1", "2"] @@ -243,41 +243,41 @@ def _latex_product(coefficients, variables, drop_plus=True, allow_empty=False): r""" Generate LaTeX code for a linear function. - + This function is intended for internal use by LaTeX methods of LP problems and their dictionaries. - + INPUT: - - - ``coefficients`` -- a list of coefficients; - - - ``variables`` -- a list of variables; - - - ``separator`` -- (default: "&" with some extra space adjustment) a string - to be inserted between elements of the generated expression; - - - ``head`` -- either ``None`` (default) or a list of entries to be added to - the beginning of the output; - - - ``tail`` -- either ``None`` (default) or a list of entries to be added to - the end of the output; - + + - ``coefficients`` -- a list of coefficients + + - ``variables`` -- a list of variables + + - ``separator`` -- (default: ``"&"`` with some extra space adjustment) a + string to be inserted between elements of the generated expression + + - ``head`` -- either ``None`` (default) or a list of entries to be + added to the beginning of the output + + - ``tail`` -- either ``None`` (default) or a list of entries to be + added to the end of the output + - ``drop_plus`` -- (default: ``True``) whether to drop the leading plus - sign or not; - + sign or not + - ``allow_empty`` -- (default: ``False``) whether to allow empty output or - produce at least "0". - + produce at least "0" + OUTPUT: - - - a strings joining ``head``, each sign and coefficient-variable product, + + - A string joining ``head``, each sign and coefficient-variable product, and ``tail`` using ``separator``. Strings in ``head`` and ``tail`` are used as is except for "<=", "==", and ">=", which are replaced by LaTeX commands. Other elements in ``head`` in ``tail`` are processed by :func:`latex`. - + TESTS:: - + sage: from sage.numerical.interactive_simplex_method import \ ....: _latex_product sage: var("x, y") @@ -331,27 +331,26 @@ def _latex_product(coefficients, variables, else: separator = r" \mspace{-6mu}&\mspace{-6mu} " return separator.join(entries) - + @cached_function def variable(R, v): r""" Interpret ``v`` as a variable of ``R``. - + INPUT: - - - ``R`` -- a polynomial ring; - + + - ``R`` -- a polynomial ring + - ``v`` -- a variable of ``R`` or convertible into ``R``, a string - with the name of a variable of ``R`` or an index of a variable in - ``R``. - + with the name of a variable of ``R`` or an index of a variable in ``R`` + OUTPUT: - - - a variable of ``R``. - + + - a variable of ``R`` + EXAMPLES:: - + sage: from sage.numerical.interactive_simplex_method \ ....: import variable sage: R = PolynomialRing(QQ, "x3, y5, x5, y") @@ -406,48 +405,44 @@ def variable(R, v): class LPProblem(SageObject): r""" Construct an LP (Linear Programming) problem. - + This class supports LP problems with "variables on the left" constraints. - + INPUT: - - ``A`` -- a matrix of constraint coefficients; - - - ``b`` -- a vector of constraint constant terms; - - - ``c`` -- a vector of objective coefficients; - - - ``x`` -- (default: "x") a vector of decision variables or a string giving - the base name; - - - ``constraint_type`` -- (default: "<=") a string specifying constraint - type(s): either "<=", or ">=", or "==", or a list of them; - - - ``variable_type`` -- (default: "") a string specifying variable type(s): - either ">=", or "<=", or "" (empty string), or a list of them, - corresponding, respectively, to non-negative, non-positive, and free - variables; - - - ``problem_type`` -- (default: "max") a string specifying the problem type: - "max", "min", "-max", or "-min"; - + - ``A`` -- a matrix of constraint coefficients + + - ``b`` -- a vector of constraint constant terms + + - ``c`` -- a vector of objective coefficients + + - ``x`` -- (default: ``"x"``) a vector of decision variables or a + string giving the base name + + - ``constraint_type`` -- (default: ``"<="``) a string specifying constraint + type(s): either ``"<="``, ``">="``, ``"=="``, or a list of them + + - ``variable_type`` -- (default: ``""``) a string specifying variable + type(s): either ``">="``, ``"<="``, ``""`` (the empty string), or a + list of them, corresponding, respectively, to non-negative, + non-positive, and free variables + + - ``problem_type`` -- (default: ``"max"``) a string specifying the + problem type: ``"max"``, ``"min"``, ``"-max"``, or ``"-min"`` + - ``prefix`` -- (default: parameter ``x`` if it was given as a string, - string "x" otherwise) a string giving the base name of automatically - created variables in :meth:`standard_form`; - + string ``"x"`` otherwise) a string giving the base name of automatically + created variables in :meth:`standard_form` + - ``base_ring`` -- (default: the fraction field of a common ring for all input coefficients) a field to which all input coefficients will be - converted. - - OUTPUT: - - - an :class:`LPProblem`. + converted EXAMPLES: We will construct the following problem: - - .. math:: + + .. MATH:: \begin{array}{l} \begin{array}{lcrcrcl} @@ -456,25 +451,25 @@ class LPProblem(SageObject): \!\!\!&\!\!\! \!\!\!&\!\!\! 3 C \!\!\!&\!\!\! + \!\!\!&\!\!\! B \!\!\!&\!\!\! \leq \!\!\!&\!\!\! 1500 \\ \end{array} \\ C, B \geq 0 - \end{array} - + \end{array} + :: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") Same problem, but more explicitly:: - + sage: P = LPProblem(A, b, c, ["C", "B"], ....: constraint_type="<=", variable_type=">=") - + Even more explicitly:: - + sage: P = LPProblem(A, b, c, ["C", "B"], problem_type="max", ....: constraint_type=["<=", "<="], variable_type=[">=", ">="]) - + Using the last form you should be able to represent any LP problem, as long as all like terms are collected and in constraints variables and constants are on different sides. @@ -520,9 +515,9 @@ def __init__(self, A, b, c, x="x", if len(x) != n: raise ValueError("A and x have incompatible dimensions") R = PolynomialRing(base_ring, x, order="neglex") - x = vector(R, R.gens()) # All variables as a vector + x = vector(R, R.gens()) # All variables as a vector self._Abcx = A, b, c, x - + if constraint_type in ["<=", ">=", "=="]: constraint_type = (constraint_type, ) * m else: @@ -530,9 +525,9 @@ def __init__(self, A, b, c, x="x", if any(ct not in ["<=", ">=", "=="] for ct in constraint_type): raise ValueError("unknown constraint type") if len(constraint_type) != m: - raise ValueError("wrong number of constraint types") + raise ValueError("wrong number of constraint types") self._constraint_types = constraint_type - + if variable_type in ["<=", ">=", ""]: variable_type = (variable_type, ) * n else: @@ -540,9 +535,9 @@ def __init__(self, A, b, c, x="x", if any(vt not in ["<=", ">=", ""] for vt in variable_type): raise ValueError("unknown variable type") if len(variable_type) != n: - raise ValueError("wrong number of variable types") + raise ValueError("wrong number of variable types") self._variable_types = variable_type - + if problem_type.startswith("-"): self._is_negative = True problem_type = problem_type[1:] @@ -551,22 +546,22 @@ def __init__(self, A, b, c, x="x", if problem_type not in ["max", "min"]: raise ValueError("unknown problem type") self._problem_type = problem_type - + self._prefix = prefix def __eq__(self, other): r""" Check if two LP problems are equal. - + INPUT: - + - ``other`` -- anything. - + OUTPUT: - + - ``True`` if ``other`` is an :class:`LPProblem` with all details the same as ``self``, ``False`` otherwise. - + TESTS:: sage: A = ([1, 1], [3, 1]) @@ -580,14 +575,14 @@ def __eq__(self, other): sage: P == P3 False """ - return (isinstance(other, LPProblem) and + return (isinstance(other, LPProblem) and self.Abcx() == other.Abcx() and self._problem_type == other._problem_type and self._is_negative == other._is_negative and self._constraint_types == other._constraint_types and self._variable_types == other._variable_types and self._prefix == other._prefix) - + def _latex_(self): r""" Return a LaTeX representation of ``self``. @@ -611,7 +606,7 @@ def _latex_(self): \end{array} \\ C, B \geq 0 \end{array} - """ + """ A, b, c, x = self.Abcx() lines = [] lines.append(r"\begin{array}{l}") @@ -633,7 +628,7 @@ def _latex_(self): latex(xj), r"\geq" if vt == ">=" else r"\leq") for xj, vt in zip(x, self._variable_types) if vt)) lines.append(r"\end{array}") - return "\n".join(lines) + return "\n".join(lines) def _repr_(self): r""" @@ -658,17 +653,17 @@ def _repr_(self): def _solve(self): r""" Return an optimal solution and the optimal value of ``self``. - + OUTPUT: - + - a pair consisting of a vector and a number. If the problem is infeasible, both components are ``None``. If the problem is unbounded, the first component is ``None`` and the second is `\pm \infty`. - + This function uses "brute force" solution technique of evaluating the objective at all vertices of the feasible set and taking into account its rays and lines. - + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -703,15 +698,15 @@ def _solve(self): S = vector(R, S) S.set_immutable() return S, M - + def Abcx(self): r""" Return `A`, `b`, `c`, and `x` of ``self`` as a tuple. - + OUTPUT: - + - a tuple. - + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -720,24 +715,24 @@ def Abcx(self): sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P.Abcx() ( - [1 1] + [1 1] [3 1], (1000, 1500), (10, 5), (C, B) ) """ return self._Abcx - + def base_ring(self): r""" Return the base ring of ``self``. - + .. note:: - + The base ring of LP problems is always a field. - + OUTPUT: - + - a ring. - + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -746,22 +741,22 @@ def base_ring(self): sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P.base_ring() Rational Field - + sage: c = (10, 5.) sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P.base_ring() Real Field with 53 bits of precision """ return self._Abcx[0].base_ring() - + def constant_terms(self): r""" Return constant terms of constraints of ``self``, i.e. `b`. - + OUTPUT: - + - a vector. - + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -774,15 +769,15 @@ def constant_terms(self): (1000, 1500) """ return self._Abcx[1] - + def constraint_coefficients(self): r""" Return coefficients of constraints of ``self``, i.e. `A`. - + OUTPUT: - + - a matrix. - + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -801,11 +796,11 @@ def constraint_coefficients(self): def decision_variables(self): r""" Return decision variables of ``self``, i.e. `x`. - + OUTPUT: - + - a vector. - + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -822,19 +817,19 @@ def decision_variables(self): def dual(self, y=None): r""" Construct the dual LP problem for ``self``. - + INPUT: - + - ``y`` -- (default: "x" if the prefix of ``self`` is "y", "y" otherwise) a vector of dual decision variables or a string giving the base name. - + OUTPUT: - an :class:`LPProblem`. EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -849,7 +844,7 @@ def dual(self, y=None): A = A.transpose() if y is None: y = "x" if self._prefix == "y" else "y" - problem_type = "min" if self._problem_type == "max" else "max" + problem_type = "min" if self._problem_type == "max" else "max" constraint_type = [] for vt in self._variable_types: if (vt == ">=" and problem_type == "min" or @@ -874,16 +869,16 @@ def dual(self, y=None): problem_type = "-" + problem_type return LPProblem(A, b, c, y, constraint_type, variable_type, problem_type) - + @cached_method def feasible_set(self): r""" Return the feasible set of ``self``. - + OUTPUT: - + - a :mod:`Polyhedron `. - + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -915,15 +910,15 @@ def feasible_set(self): ieqs = [map(R, ieq) for ieq in ieqs] eqns = [map(R, eqn) for eqn in eqns] return Polyhedron(ieqs=ieqs, eqns=eqns, base_ring=R) - + def is_bounded(self): r""" Check if ``self`` is bounded. - + OUTPUT: - + - ``True`` is ``self`` is bounded, ``False`` otherwise. - + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -938,11 +933,11 @@ def is_bounded(self): def is_feasible(self): r""" Check if ``self`` is feasible. - + OUTPUT: - + - ``True`` is ``self`` is feasible, ``False`` otherwise. - + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -957,11 +952,11 @@ def is_feasible(self): def n_constraints(self): r""" Return the number of constraints of ``self``, i.e. `m`. - + OUTPUT: - + - an integer. - + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -978,11 +973,11 @@ def n_constraints(self): def n_variables(self): r""" Return the number of decision variables of ``self``, i.e. `n`. - + OUTPUT: - + - an integer. - + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -995,15 +990,15 @@ def n_variables(self): 2 """ return self._Abcx[0].ncols() - + def objective_coefficients(self): r""" Return coefficients of the objective of ``self``, i.e. `c`. - + OUTPUT: - + - a vector. - + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -1016,15 +1011,15 @@ def objective_coefficients(self): (10, 5) """ return self._Abcx[2] - + def optimal_solution(self): r""" Return **an** optimal solution of ``self``. - + OUTPUT: - + - a vector or ``None`` if there are no optimal solutions. - + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -1039,12 +1034,12 @@ def optimal_solution(self): def optimal_value(self): r""" Return the optimal value for ``self``. - + OUTPUT: - + - a number if the problem is bounded, `\pm \infty` if it is unbounded, or ``None`` if it is infeasible. - + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -1059,15 +1054,15 @@ def optimal_value(self): def plot(self, *args, **kwds): r""" Return a plot for solving ``self`` graphically. - + INPUT: - + - same as for :meth:`plot_feasible_set`. - + OUTPUT: - + - a plot. - + This only works for problems with two decision variables. On the plot the black arrow indicates the direction of growth of the objective. The lines perpendicular to it are level curves of the objective. If there @@ -1076,7 +1071,7 @@ def plot(self, *args, **kwds): are optimal solutions. Otherwise the arrow is placed in the center. If the problem is infeasible or the objective is zero, a plot of the feasible set only is returned. - + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -1085,16 +1080,16 @@ def plot(self, *args, **kwds): sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: p = P.plot() sage: p.show() - + In this case the plot works better with the following axes ranges:: - + sage: p = P.plot(0, 1000, 0, 1500) sage: p.show() - + TESTS: - + We check that zero objective can be dealt with:: - + sage: LPProblem(A, b, (0, 0), ["C", "B"], variable_type=">=").plot() """ FP = self.plot_feasible_set(*args, **kwds) @@ -1110,7 +1105,7 @@ def plot(self, *args, **kwds): start = vector(QQ, start.n() if start is not None else [xmin + (xmax-xmin)/2, ymin + (ymax-ymin)/2]) length = min(xmax - xmin, ymax - ymin) / 5 - end = start + (c * length / c.norm()).n().change_ring(QQ) + end = start + (c * length / c.norm()).n().change_ring(QQ) result = FP + point(start, color="black", size=50, zorder=10) result += arrow(start, end, color="black", zorder=10) ieqs = [(xmax, -1, 0), (- xmin, 1, 0), @@ -1130,29 +1125,29 @@ def plot(self, *args, **kwds): result.set_axes_range(xmin, xmax, ymin, ymax) result.axes_labels(FP.axes_labels()) #FIXME: should be preserved! return result - + def plot_feasible_set(self, xmin=None, xmax=None, ymin=None, ymax=None, alpha=0.2): r""" Return a plot of the feasible set of ``self``. - + INPUT: - + - ``xmin``, ``xmax``, ``ymin``, ``ymax`` -- bounds for the axes, if not given, an attempt will be made to pick reasonable values; - + - ``alpha`` -- (default: 0.2) determines how opaque are shadows. - + OUTPUT: - + - a plot. - + This only works for a problem with two decision variables. The plot shows boundaries of constraints with a shadow on one side for inequalities. If the :meth:`feasible_set` is not empty and at least part of it is in the given boundaries, it will be shaded gray and `F` will be placed in its middle. - + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -1161,9 +1156,9 @@ def plot_feasible_set(self, xmin=None, xmax=None, ymin=None, ymax=None, sage: P = LPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: p = P.plot_feasible_set() sage: p.show() - + In this case the plot works better with the following axes ranges:: - + sage: p = P.plot_feasible_set(0, 1000, 0, 1500) sage: p.show() """ @@ -1234,18 +1229,18 @@ def plot_feasible_set(self, xmin=None, xmax=None, ymin=None, ymax=None, shadow=True) result._extra_kwds["aspect_ratio"] = 1 result.set_aspect_ratio(1) - return result + return result def standard_form(self): r""" Construct the LP problem in standard form equivalent to ``self``. - + OUTPUT: - an :class:`LPProblemStandardForm`. EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -1295,7 +1290,7 @@ def standard_form(self): problem_type = "-max" if is_negative else "max" return LPProblemStandardForm(A, b, c, x, problem_type, self._prefix, self._prefix + "0") - + # Aliases for the standard notation A = constraint_coefficients b = constant_terms @@ -1308,63 +1303,60 @@ def standard_form(self): class LPProblemStandardForm(LPProblem): r""" Construct an LP (Linear Programming) problem in standard form. - + The used standard form is: - - .. math:: - + + .. MATH:: + \begin{array}{l} \pm \max cx \\ Ax \leq b \\ x \geq 0 \end{array} - + INPUT: - - ``A`` -- a matrix of constraint coefficients; + - ``A`` -- a matrix of constraint coefficients + + - ``b`` -- a vector of constraint constant terms - - ``b`` -- a vector of constraint constant terms; + - ``c`` -- a vector of objective coefficients - - ``c`` -- a vector of objective coefficients; + - ``x`` -- (default: ``"x"``) a vector of decision variables or a string + the base name giving + + - ``problem_type`` -- (default: ``"max"``) a string specifying the + problem type: either ``"max"`` or ``"-max"`` - - ``x`` -- (default: "x") a vector of decision variables or a string giving - the base name; - - - ``problem_type`` -- (default: "max") a string specifying the problem type: - either "max" or "-max"; - - ``slack_variables`` -- (default: same as ``x`` parameter, if it was given - as a string, otherwise string "x") a vector of slack variables or a sting - giving the base name; - + as a string, otherwise string ``"x"``) a vector of slack variables or + a sting giving the base name + - ``auxiliary_variable`` -- (default: same as ``x`` parameter with adjoined - "0" if it was given as a string, otherwise "x0") the auxiliary variable - name, expected to be the same as the first decision variable for auxiliary - problems; - - - ``objective`` -- (default: "z") the objective variable (used for the - initial dictionary); - + ``"0"`` if it was given as a string, otherwise ``"x0"``) the auxiliary + name, expected to be the same as the first decision variable for + auxiliary problems + + - ``objective`` -- (default: ``"z"``) the objective variable (used for the + initial dictionary) + - ``base_ring`` -- (default: the fraction field of a common ring for all input coefficients) a field to which all input coefficients will be - converted. - - OUTPUT: - - - an :class:`LPProblemStandardForm`. + converted EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) sage: P = LPProblemStandardForm(A, b, c) - Unlike :class:`LPProblem`, this class does not allow you to adjust types of - constraints (they are always "<=") and variables (they are always ">="), and - the problem type may only be "max" or "-max". You may give custom names to - slack and auxiliary variables, but in most cases defaults should work:: - + Unlike :class:`LPProblem`, this class does not allow you to adjust types of + constraints (they are always ``"<="``) and variables (they are always + ``">="``), and the problem type may only be ``"max"`` or ``"-max"``. + You may give custom names to slack and auxiliary variables, but in + most cases defaults should work:: + sage: P.decision_variables() (x1, x2) sage: P.slack_variables() @@ -1378,7 +1370,7 @@ def __init__(self, A, b, c, x="x", problem_type="max", See :class:`StandardFormLPP` for documentation. TESTS:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -1418,26 +1410,26 @@ def __init__(self, A, b, c, x="x", problem_type="max", def auxiliary_problem(self): r""" Construct the auxiliary problem for ``self``. - + OUTPUT: - - - an :class:`LP problem in standard form `. - + + - an :class:`LP problem in standard form ` + The auxiliary problem with the auxiliary variable `x_0` is - - .. math:: - + + .. MATH:: + \begin{array}{l} \max - x_0 \\ - x_0 + A_i x \leq b_i \text{ for all } i \\ x \geq 0 - \end{array} - + \end{array}\ . + Such problems are used when the :meth:`initial_dictionary` is infeasible. - + EXAMPLES:: - + sage: A = ([1, 1], [3, 1], [-1, -1]) sage: b = (1000, 1500, -400) sage: c = (10, 5) @@ -1456,18 +1448,18 @@ def auxiliary_problem(self): slack_variables=X[-m:], auxiliary_variable=X[0], objective="w") - + def auxiliary_variable(self): r""" Return the auxiliary variable of ``self``. - + Note that the auxiliary variable may or may not be among :meth:`~LPProblem.decision_variables`. - + OUTPUT: - - - a variable of the :meth:`coordinate_ring` of ``self``. - + + - a variable of the :meth:`coordinate_ring` of ``self`` + EXAMPLES:: sage: A = ([1, 1], [3, 1], [-1, -1]) @@ -1485,17 +1477,17 @@ def auxiliary_variable(self): (x0, x1, x2) """ return self._R.gen(0) - + def coordinate_ring(self): r""" Return the coordinate ring of ``self``. - + OUTPUT: - + - a polynomial ring over the :meth:`~LPProblem.base_ring` of ``self`` in the :meth:`auxiliary_variable`, :meth:`~LPProblem.decision_variables`, - and :meth:`slack_variables` with "neglex" order. - + and :meth:`slack_variables` with "neglex" order + EXAMPLES:: sage: A = ([1, 1], [3, 1], [-1, -1]) @@ -1515,26 +1507,26 @@ def coordinate_ring(self): (x3, x4, x5) """ return self._R - + def dictionary(self, *x_B): r""" Construct a dictionary for ``self`` with given basic variables. - + INPUT: - - - basic variables for the dictionary to be constructed. - + + - basic variables for the dictionary to be constructed + OUTPUT: - - - a :class:`dictionary `. - + + - a :class:`dictionary ` + .. NOTE:: - + This is a synonym for ``self.revised_dictionary(x_B).dictionary()``, but basic variables are mandatory. - + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -1546,23 +1538,23 @@ def dictionary(self, *x_B): if not x_B: raise ValueError("basic variables must be given explicitly") return self.revised_dictionary(*x_B).dictionary() - + def feasible_dictionary(self, auxiliary_dictionary): r""" Construct a feasible dictionary for ``self``. - + INPUT: - + - ``auxiliary_dictionary`` -- an optimal dictionary for the :meth:`auxiliary_problem` of ``self`` with the optimal value `0` and - a non-basic auxiliary variable. - + a non-basic auxiliary variable + OUTPUT: - - - a feasible :class:`dictionary ` for ``self``. - + + - a feasible :class:`dictionary ` for ``self`` + EXAMPLES:: - + sage: A = ([1, 1], [3, 1], [-1, -1]) sage: b = (1000, 1500, -400) sage: c = (10, 5) @@ -1617,19 +1609,19 @@ def feasible_dictionary(self, auxiliary_dictionary): B = map(self._R, B) N = map(self._R, N) return LPDictionary(A, b, c, v, B, N, self._objective) - + def final_dictionary(self): r""" Return the final dictionary of the simplex method applied to ``self``. - + See :meth:`run_simplex_method` for the description of possibilities. - + OUTPUT: - - - a :class:`dictionary `. - + + - a :class:`dictionary ` + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -1637,9 +1629,9 @@ def final_dictionary(self): sage: D = P.final_dictionary() sage: D.is_optimal() True - + TESTS:: - + sage: P.final_dictionary() is P.final_dictionary() False """ @@ -1651,20 +1643,21 @@ def final_dictionary(self): except AttributeError: self.run_simplex_method() return self.final_dictionary() - + def final_revised_dictionary(self): r""" - Return the final dictionary of the revised simplex method applied to ``self``. - + Return the final dictionary of the revised simplex method applied + to ``self``. + See :meth:`run_revised_simplex_method` for the description of possibilities. - + OUTPUT: - - - a :class:`revised dictionary `. - + + - a :class:`revised dictionary ` + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -1672,9 +1665,9 @@ def final_revised_dictionary(self): sage: D = P.final_revised_dictionary() sage: D.is_optimal() True - + TESTS:: - + sage: P.final_revised_dictionary() is P.final_revised_dictionary() False """ @@ -1686,21 +1679,21 @@ def final_revised_dictionary(self): except AttributeError: self.run_revised_simplex_method() return self.final_revised_dictionary() - + def initial_dictionary(self): r""" Construct the initial dictionary of ``self``. - - The initial dictionary "defines" :meth:`slack_variables` in terms of the - :meth:`~LPProblem.decision_variables`, i.e. it has slack variables as - basic ones. - + + The initial dictionary "defines" :meth:`slack_variables` in terms + of the :meth:`~LPProblem.decision_variables`, i.e. it has slack + variables as basic ones. + OUTPUT: - - - a :class:`dictionary `. - + + - a :class:`dictionary ` + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -1711,21 +1704,21 @@ def initial_dictionary(self): x = self._R.gens() m, n = self.m(), self.n() return LPDictionary(A, b, c, 0, x[-m:], x[-m-n:-m], self._objective) - + def inject_variables(self, scope=None, verbose=True): r""" Inject variables of ``self`` into ``scope``. INPUT: - - ``scope`` -- namespace (default: global); + - ``scope`` -- namespace (default: global) - ``verbose`` -- if ``True`` (default), names of injected variables - will be printed. + will be printed OUTPUT: - - none. + - none EXAMPLES:: @@ -1744,24 +1737,23 @@ def inject_variables(self, scope=None, verbose=True): self._R.inject_variables(scope, verbose) except AttributeError: pass - + def revised_dictionary(self, *x_B): r""" Construct a revised dictionary for ``self``. - + INPUT: - - - basic variables for the dictionary to be constructed. If not given, + + - basic variables for the dictionary to be constructed; if not given, :meth:`slack_variables` will be used, perhaps with the - :meth:`auxiliary_variable` to - give a feasible dictionary. - + :meth:`auxiliary_variable` to give a feasible dictionary + OUTPUT: - - - a :class:`revised dictionary `. - + + - a :class:`revised dictionary ` + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -1769,17 +1761,18 @@ def revised_dictionary(self, *x_B): sage: D = P.revised_dictionary("x1", "x2") sage: D.basic_variables() (x1, x2) - - If basic variables are not given the initial dictionary is constructed:: - + + If basic variables are not given the initial dictionary is + constructed:: + sage: P.revised_dictionary().basic_variables() (x3, x4) sage: P.initial_dictionary().basic_variables() (x3, x4) - + Unless it is infeasible, in which case a feasible dictionary for the auxiliary problem is constructed:: - + sage: A = ([1, 1], [3, 1], [-1,-1]) sage: b = (1000, 1500, -400) sage: P = LPProblemStandardForm(A, b, c) @@ -1794,29 +1787,29 @@ def revised_dictionary(self, *x_B): if bm < 0: x_B[self.b().list().index(bm)] = self.auxiliary_variable() return LPRevisedDictionary(self, x_B) - + def run_revised_simplex_method(self): r""" Apply the revised simplex method to solve ``self`` and show the steps. - + OUTPUT: - - - a string with `\LaTeX` code of intermediate dictionaries. - - .. note:: - + + - a string with `\LaTeX` code of intermediate dictionaries + + .. NOTE:: + You can access the :meth:`final_revised_dictionary`, which can be one of the following: - + - an optimal dictionary with the :meth:`auxiliary_variable` among :meth:`~LPRevisedDictionary.basic_variables` and a non-zero optimal value indicating that ``self`` is infeasible; - + - a non-optimal dictionary that has marked entering variable for which there is no choice of the leaving variable, indicating that ``self`` is unbounded; - + - an optimal dictionary. EXAMPLES:: @@ -1873,27 +1866,27 @@ def run_revised_simplex_method(self): latex(v), latex(d.basic_solution()))) self._final_revised_dictionary = d return _assemble_arrayl(result, 1.5) - - def run_simplex_method(self): + + def run_simplex_method(self): r""" Apply the simplex method to solve ``self`` and show the steps. - + OUTPUT: - - - a string with `\LaTeX` code of intermediate dictionaries. - - .. note:: - - You can access the :meth:`final_dictionary`, which can be one of the - following: - + + - a string with `\LaTeX` code of intermediate dictionaries + + .. NOTE:: + + You can access the :meth:`final_dictionary`, which can be one + of the following: + - an optimal dictionary for the :meth:`auxiliary_problem` with a non-zero optimal value indicating that ``self`` is infeasible; - + - a non-optimal dictionary for ``self`` that has marked entering variable for which there is no choice of the leaving variable, indicating that ``self`` is unbounded; - + - an optimal dictionary for ``self``. EXAMPLES:: @@ -1903,10 +1896,10 @@ def run_simplex_method(self): sage: c = (10, 5) sage: P = LPProblemStandardForm(A, b, c) sage: P.run_simplex_method() # not tested - + You should use the typeset mode as the command above generates long `\LaTeX` code:: - + sage: print P.run_simplex_method() \begin{gather*} ... @@ -1929,7 +1922,7 @@ def step(entering, leaving): result.append(r"\text{{Entering: ${}$. Leaving: ${}$.}}" .format(latex(entering), latex(leaving))) result.append(d.ELLUL(entering, leaving)) - + result = [] d = self.initial_dictionary() result.append(latex(d)) @@ -1943,7 +1936,7 @@ def step(entering, leaving): x0 = self.auxiliary_variable() _, leaving = min(zip(d.constant_terms(), d.basic_variables())) step(x0, leaving) - # while not d.is_optimal(): + # while not d.is_optimal(): # either optimality check should handle rounding errors or while not (d.is_optimal() or x0 in d.nonbasic_variables()): entering, leaving = min(d.possible_simplex_method_steps()) @@ -1981,23 +1974,23 @@ def step(entering, leaving): "\\displaybreak[0]\\\\\n".join(result) + "\n\\end{gather*}") return _assemble_arrayl(result, 1.5) - + def slack_variables(self): r""" Return slack variables of ``self``. - - Slack variables are differences between the constant terms and left hand - sides of the constraints. - + + Slack variables are differences between the constant terms and + left hand sides of the constraints. + If you want to give custom names to slack variables, you have to do so during construction of the problem. - + OUTPUT: - - - a tuple. - + + - a tuple + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -2015,17 +2008,17 @@ def slack_variables(self): class LPAbstractDictionary(SageObject): r""" Abstract base class for dictionaries for LP problems. - + Instantiating this class directly is meaningless, see :class:`LPDictionary` and :class:`LPRevisedDictionary` for useful extensions. """ - + def __init__(self): r""" Initialize internal fields for entering and leaving variables. - + TESTS:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -2042,7 +2035,7 @@ def _repr_(self): OUTPUT: - - a string. + - a string TESTS:: @@ -2061,12 +2054,12 @@ def _repr_(self): def base_ring(self): r""" - Return the base ring of ``self``, i.e. the ring of coefficients. - + Return the base ring of ``self``, i.e. the ring of coefficients. + OUTPUT: - - - a ring. - + + - a ring + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -2081,29 +2074,29 @@ def base_ring(self): Rational Field """ return self.coordinate_ring().base_ring() - + def basic_solution(self, include_slack_variables=False): r""" Return the basic solution of ``self``. - + The basic solution associated to a dictionary is obtained by setting to zero all :meth:`~LPDictionary.nonbasic_variables`, in which case :meth:`~LPDictionary.basic_variables` have to be equal to :meth:`~LPDictionary.constant_terms` in equations. It may refer to values of :meth:`~LPProblem.decision_variables` only or include :meth:`~LPProblemStandardForm.slack_variables` as well. - + INPUT: - + - ``include_slack_variables`` -- (default: ``False``) if ``True``, - values of slack variables will be appended at the end. - + values of slack variables will be appended at the end + OUTPUT: - - - a vector. - + + - a vector + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -2123,22 +2116,22 @@ def basic_solution(self, include_slack_variables=False): N = self.nonbasic_variables() vv += [(v, 0) for v in N] vv.sort() # We use neglex order - v = [value for _, value in vv] + v = [value for _, value in vv] return vector(self.base_ring(), v if include_slack_variables else v[:len(N)]) - + def coordinate_ring(self): r""" Return the coordinate ring of ``self``. - + OUTPUT: - + - a polynomial ring in :meth:`~LPProblemStandardForm.auxiliary_variable`, :meth:`~LPProblem.decision_variables`, and :meth:`~LPProblemStandardForm.slack_variables` of ``self`` over the - :meth:`base_ring`. - + :meth:`base_ring` + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -2155,37 +2148,37 @@ def coordinate_ring(self): over Rational Field """ return self.basic_variables()[0].parent() - + def dual_ratios(self): r""" Return ratios used to determine the entering variable based on leaving. - + OUTPUT: - - - a list of pairs `(r_j, x_j)` where `x_j` is a non-basic variable and + + - A list of pairs `(r_j, x_j)` where `x_j` is a non-basic variable and `r_j = c_j / a_{ij}` is the ratio of the objective coefficient `c_j` to the coefficient `a_{ij}` of `x_j` in the relation for the leaving variable `x_i`: - - .. math:: - - x_i = b_i - \dots - a_{ij} x_j - \dots. - + + .. MATH:: + + x_i = b_i - \cdots - a_{ij} x_j - \cdots. + The order of pairs matches the order of :meth:`~LPDictionary.nonbasic_variables`, but only `x_j` with negative `a_{ij}` are considered. - + EXAMPLES:: - + sage: A = ([1, 1], [3, 1], [-1, -1]) sage: b = (1000, 1500, -400) sage: c = (10, 5) sage: P = LPProblemStandardForm(A, b, c) - sage: D = P.dictionary(2, 3, 5) + sage: D = P.dictionary(2, 3, 5) sage: D.leave(3) sage: D.dual_ratios() [(5/2, x1), (5, x4)] - sage: D = P.revised_dictionary(2, 3, 5) + sage: D = P.revised_dictionary(2, 3, 5) sage: D.leave(3) sage: D.dual_ratios() [(5/2, x1), (5, x4)] @@ -2197,42 +2190,42 @@ def dual_ratios(self): def enter(self, v): r""" Set ``v`` as the entering variable of ``self``. - + INPUT: - + - ``v`` -- a non-basic variable of ``self``, can be given as a string, an actual variable, or an integer interpreted as the index of a - variable. - + variable + OUTPUT: - + - none, but the selected variable will be used as entering by methods that require an entering variable and the corresponding column will be - typeset in green. - + typeset in green + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) sage: P = LPProblemStandardForm(A, b, c) sage: D = P.initial_dictionary() sage: D.enter("x1") - + We can also use indices of variables:: - + sage: D.enter(1) - + Or variable names without quotes after injecting them:: - + sage: P.inject_variables() Defining x0, x1, x2, x3, x4 sage: D.enter(x1) - + The same works for revised dictionaries as well:: - + sage: D = P.revised_dictionary() - sage: D.enter(x1) + sage: D.enter(x1) """ v = variable(self.coordinate_ring(), v) if v not in self.nonbasic_variables(): @@ -2242,14 +2235,14 @@ def enter(self, v): def is_dual_feasible(self): r""" Check if ``self`` is dual feasible. - + OUTPUT: - + - ``True`` if all :meth:`~LPDictionary.objective_coefficients` are - non-positive, ``False`` otherwise. - + non-positive, ``False`` otherwise + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -2262,18 +2255,18 @@ def is_dual_feasible(self): False """ return all(ci <= 0 for ci in self.objective_coefficients()) - + def is_feasible(self): r""" Check if ``self`` is feasible. - + OUTPUT: - - - ``True`` if all :meth:`~LPDictionary.constant_terms` are non-negative, - ``False`` otherwise. - + + - ``True`` if all :meth:`~LPDictionary.constant_terms` are + non-negative,``False`` otherwise + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -2286,20 +2279,20 @@ def is_feasible(self): True """ return all(bi >= 0 for bi in self.constant_terms()) - + def is_optimal(self): r""" Check if ``self`` is optimal. - + OUTPUT: - + - ``True`` if ``self`` :meth:`is_feasible` and :meth:`is_dual_feasible` (i.e. all :meth:`~LPDictionary.constant_terms` are non-negative and all :meth:`~LPDictionary.objective_coefficients` are non-positive), - ``False`` otherwise. - + ``False`` otherwise. + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -2319,61 +2312,61 @@ def is_optimal(self): def leave(self, v): r""" Set ``v`` as the leaving variable of ``self``. - + INPUT: - + - ``v`` -- a basic variable of ``self``, can be given as a string, an - actual variable, or an integer interpreted as the index of a variable. - + actual variable, or an integer interpreted as the index of a variable + OUTPUT: - + - none, but the selected variable will be used as leaving by methods that require a leaving variable and the corresponding row will be - typeset in red. - + typeset in red + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) sage: P = LPProblemStandardForm(A, b, c) sage: D = P.initial_dictionary() sage: D.leave("x4") - + We can also use indices of variables:: - + sage: D.leave(4) - + Or variable names without quotes after injecting them:: - + sage: P.inject_variables() Defining x0, x1, x2, x3, x4 sage: D.leave(x4) - + The same works for revised dictionaries as well:: - + sage: D = P.revised_dictionary() - sage: D.leave(x4) + sage: D.leave(x4) """ v = variable(self.coordinate_ring(), v) if v not in self.basic_variables(): raise ValueError("leaving variable must be basic") self._leaving = v - + def possible_dual_simplex_method_steps(self): r""" Return possible dual simplex method steps for ``self``. - + OUTPUT: - - - a list of pairs ``(leaving, entering)``, where ``leaving`` is a + + - A list of pairs ``(leaving, entering)``, where ``leaving`` is a basic variable that may :meth:`leave` and ``entering`` is a list of non-basic variables that may :meth:`enter` when ``leaving`` leaves. Note that ``entering`` may be empty, indicating that the problem is infeasible (since the dual one is unbounded). - + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -2398,23 +2391,23 @@ def possible_dual_simplex_method_steps(self): self._entering = old_entering self._leaving = old_leaving return steps - + def possible_entering(self): r""" Return possible entering variables for ``self``. - + OUTPUT: - + - a list of non-basic variables of ``self`` that can :meth:`enter` on - the next step of the (dual) simplex method. - + the next step of the (dual) simplex method + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) sage: P = LPProblemStandardForm(A, b, c) - sage: D = P.initial_dictionary() + sage: D = P.initial_dictionary() sage: D.possible_entering() [x1, x2] sage: D = P.revised_dictionary() @@ -2433,23 +2426,23 @@ def possible_entering(self): raise ValueError("entering variables can be determined for feasible " "dictionaries or for dual feasible dictionaries " "with a set leaving variable") - + def possible_leaving(self): r""" Return possible leaving variables for ``self``. - + OUTPUT: - + - a list of basic variables of ``self`` that can :meth:`leave` on - the next step of the (dual) simplex method. - + the next step of the (dual) simplex method + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) sage: P = LPProblemStandardForm(A, b, c) - sage: D = P.initial_dictionary() + sage: D = P.initial_dictionary() sage: D.enter(1) sage: D.possible_leaving() [x4] @@ -2470,26 +2463,26 @@ def possible_leaving(self): raise ValueError("leaving variables can be determined for feasible " "dictionaries with a set entering variable " "or for dual feasible dictionaries") - + def possible_simplex_method_steps(self): r""" Return possible simplex method steps for ``self``. - + OUTPUT: - - - a list of pairs ``(entering, leaving)``, where ``entering`` is a - non-basic variable that may :meth:`enter` and ``leaving`` is a list of - basic variables that may :meth:`leave` when ``entering`` enters. Note - that ``leaving`` may be empty, indicating that the problem is + + - A list of pairs ``(entering, leaving)``, where ``entering`` is a + non-basic variable that may :meth:`enter` and ``leaving`` is a list + of basic variables that may :meth:`leave` when ``entering`` enters. + Note that ``leaving`` may be empty, indicating that the problem is unbounded. - + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) sage: P = LPProblemStandardForm(A, b, c) - sage: D = P.initial_dictionary() + sage: D = P.initial_dictionary() sage: D.possible_simplex_method_steps() [(x1, [x4]), (x2, [x3])] sage: D = P.revised_dictionary() @@ -2509,37 +2502,37 @@ def possible_simplex_method_steps(self): self._entering = old_entering self._leaving = old_leaving return steps - + def ratios(self): r""" Return ratios used to determine the leaving variable based on entering. - + OUTPUT: - - - a list of pairs `(r_i, x_i)` where `x_i` is a basic variable and + + - A list of pairs `(r_i, x_i)` where `x_i` is a basic variable and `r_i = b_i / a_{ik}` is the ratio of the constant term `b_i` to the coefficient `a_{ik}` of the entering variable `x_k` in the relation for `x_i`: - - .. math:: - - x_i = b_i - \dots - a_{ik} x_k - \dots. - + + .. MATH:: + + x_i = b_i - \cdots - a_{ik} x_k - \cdots. + The order of pairs matches the order of :meth:`~LPDictionary.basic_variables`, but only `x_i` with positive `a_{ik}` are considered. - + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) sage: P = LPProblemStandardForm(A, b, c) - sage: D = P.initial_dictionary() + sage: D = P.initial_dictionary() sage: D.enter(1) sage: D.ratios() [(1000, x3), (500, x4)] - sage: D = P.revised_dictionary() + sage: D = P.revised_dictionary() sage: D.enter(1) sage: D.ratios() [(1000, x3), (500, x4)] @@ -2552,48 +2545,48 @@ def ratios(self): class LPDictionary(LPAbstractDictionary): r""" Construct a dictionary for an LP problem. - + A dictionary consists of the following data: - - .. math:: - + + .. MATH:: + \begin{array}{|l|} \hline x_B = b - A x_N\\ \hline z = z^* + c x_N\\ \hline - \end{array} - + \end{array} + INPUT: - - ``A`` -- a matrix of relation coefficients; + - ``A`` -- a matrix of relation coefficients + + - ``b`` -- a vector of relation constant terms - - ``b`` -- a vector of relation constant terms; - - - ``c`` -- a vector of objective coefficients; - - - ``objective_value`` -- current value of the objective `z^*`; + - ``c`` -- a vector of objective coefficients - - ``basic_variables`` -- a list of basic variables `x_B`; + - ``objective_value`` -- current value of the objective `z^*` - - ``nonbasic_variables`` -- a list of non-basic variables `x_N`; - - - ``objective_variable`` -- an objective variable `z`. + - ``basic_variables`` -- a list of basic variables `x_B` + + - ``nonbasic_variables`` -- a list of non-basic variables `x_N` + + - ``objective_variable`` -- an objective variable `z` OUTPUT: - - a :class:`dictionary for an LP problem `. - - .. note:: - - This constructor does not check correctness of input, as it is intended - to be used internally by :class:`LPProblemStandardForm`. + - a :class:`dictionary for an LP problem ` + + .. NOTE:: + + This constructor does not check correctness of input, as it is + intended to be used internally by :class:`LPProblemStandardForm`. EXAMPLES: - + The intended way to use this class is indirect:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -2601,10 +2594,10 @@ class LPDictionary(LPAbstractDictionary): sage: D = P.initial_dictionary() sage: D LP problem dictionary (use typeset mode to see details) - + But if you want you can create a dictionary without starting with an LP problem, here is construction of the same dictionary as above:: - + sage: A = matrix(QQ, ([1, 1], [3, 1])) sage: b = vector(QQ, (1000, 1500)) sage: c = vector(QQ, (10, 5)) @@ -2622,7 +2615,7 @@ def __init__(self, A, b, c, objective_value, See :class:`LPDictionary` for documentation. TESTS::: - + sage: A = matrix(QQ, ([1, 1], [3, 1])) sage: b = vector(QQ, (1000, 1500)) sage: c = vector(QQ, (10, 5)) @@ -2644,16 +2637,16 @@ def __init__(self, A, b, c, objective_value, def __eq__(self, other): r""" Check if two LP problem dictionaries are equal. - + INPUT: - - - ``other`` -- anything. - + + - ``other`` -- anything + OUTPUT: - - - ``True`` if ``other`` is an :class:`LPDictionary` with all details the - same as ``self``, ``False`` otherwise. - + + - ``True`` if ``other`` is an :class:`LPDictionary` with all + details the same as ``self``, ``False`` otherwise. + TESTS:: sage: A = ([1, 1], [3, 1]) @@ -2661,7 +2654,7 @@ def __eq__(self, other): sage: c = (10, 5) sage: P = LPProblemStandardForm(A, b, c) sage: D = P.initial_dictionary() - + sage: A = matrix(QQ, ([1, 1], [3, 1])) sage: b = vector(QQ, (1000, 1500)) sage: c = vector(QQ, (10, 5)) @@ -2671,21 +2664,21 @@ def __eq__(self, other): sage: D2 = LPDictionary(A, b, c, 0, R.gens()[2:], R.gens()[:2], "z") sage: D2 == D True - + sage: D3 = LPDictionary(A, b, c, 0, R.gens()[2:], R.gens()[:2], "w") sage: D2 == D3 False """ - return (isinstance(other, LPDictionary) and + return (isinstance(other, LPDictionary) and self._AbcvBNz == other._AbcvBNz) - + def _latex_(self): r""" Return a LaTeX representation of ``self``. OUTPUT: - - a string. + - a string TESTS:: @@ -2704,7 +2697,7 @@ def _latex_(self): z & = & 0 & + & 10 x_{1} & + & 5 x_{2}\\ \hline \end{array} - """ + """ A, b, c, v, B, N, z = self._AbcvBNz lines = [] lines.append(r"\renewcommand{\arraystretch}{1.5}") @@ -2720,7 +2713,7 @@ def _latex_(self): lines.append(r"\hline") lines.append(_latex_product(c, N, head=[z, "=", v], drop_plus=False, allow_empty=True) + r"\\") - lines.append(r"\hline") + lines.append(r"\hline") lines.append(r"\end{array}") latex.add_package_to_preamble_if_available("color") if self._entering is not None: @@ -2732,7 +2725,7 @@ def _latex_(self): if not generate_real_LaTeX: line[e] = ("{" + line[e] + "}").replace(r"\\}", r"}\\") line[e] = r"\color{green}" + line[e] - lines[i] = "&".join(line) + lines[i] = "&".join(line) if self._leaving is not None: # Highlight the leaving variable row l = tuple(B).index(self._leaving) + 3 @@ -2754,19 +2747,19 @@ def _latex_(self): def ELLUL(self, entering, leaving): r""" Perform the Enter-Leave-LaTeX-Update-LaTeX step sequence on ``self``. - + INPUT: - - - ``entering`` -- the entering variable; - - - ``leaving`` -- the leaving variable. - + + - ``entering`` -- the entering variable + + - ``leaving`` -- the leaving variable + OUTPUT: - - - a string with LaTeX code for ``self`` before and after update. - + + - a string with LaTeX code for ``self`` before and after update + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -2789,11 +2782,11 @@ def ELLUL(self, entering, leaving): z & = & 5000 & - & \frac{10}{3} x_{4} & + & \frac{5}{3} x_{2}\\ \hline \end{array} - + This is how the above output looks when rendered: - - .. math:: - + + .. MATH:: + \renewcommand{\arraystretch}{1.5} \begin{array}{|rcrcrcr|} \hline @@ -2810,33 +2803,33 @@ def ELLUL(self, entering, leaving): z \!\!\!&\!\!\! = \!\!\!&\!\!\! 5000 \!\!\!&\!\!\! - \!\!\!&\!\!\! \frac{10}{3} x_{4} \!\!\!&\!\!\! + \!\!\!&\!\!\! \frac{5}{3} x_{2}\\ \hline \end{array} - + The column of the entering variable is green, while the row of the - leaving variable is red in the original dictionary state on the top. The - new state after the update step is shown on the bottom. + leaving variable is red in the original dictionary state on the top. + The new state after the update step is shown on the bottom. """ self.enter(entering) - self.leave(leaving) + self.leave(leaving) result = latex(self).rsplit("\n", 1)[0] # Remove \end{array} # Make an empty line in the array if generate_real_LaTeX: - result += "\n" r"\multicolumn{2}{c}{}\\[-3ex]" "\n" + result += "\n" r"\multicolumn{2}{c}{}\\[-3ex]" "\n" else: result += "\n\\\\\n" - self.update() + self.update() result += latex(self).split("\n", 2)[2] # Remove array header return LatexExpr(result) def basic_variables(self): r""" Return the basic variables of ``self``. - + OUTPUT: - - - a vector. - + + - a vector + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -2846,17 +2839,17 @@ def basic_variables(self): (x3, x4) """ return self._AbcvBNz[4] - + def constant_terms(self): r""" Return the constant terms of relations of ``self``. - + OUTPUT: - + - a vector. - + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -2866,17 +2859,17 @@ def constant_terms(self): (1000, 1500) """ return self._AbcvBNz[1] - + def entering_coefficients(self): r""" Return coefficients of the entering variable. - + OUTPUT: - - - a vector. - + + - a vector + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -2891,17 +2884,17 @@ def entering_coefficients(self): "its coefficients") k = tuple(self.nonbasic_variables()).index(self._entering) return self._AbcvBNz[0].column(k) - + def leaving_coefficients(self): r""" Return coefficients of the leaving variable. - + OUTPUT: - - - a vector. - + + - a vector + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -2916,17 +2909,17 @@ def leaving_coefficients(self): "its coefficients") i = tuple(self.basic_variables()).index(self._leaving) return self._AbcvBNz[0][i] - + def nonbasic_variables(self): r""" Return non-basic variables of ``self``. - + OUTPUT: - - - a vector. - + + - a vector + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -2936,17 +2929,17 @@ def nonbasic_variables(self): (x1, x2) """ return self._AbcvBNz[5] - + def objective_coefficients(self): r""" Return coefficients of the objective of ``self``. - + OUTPUT: - - - a vector. - + + - a vector + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -2956,18 +2949,18 @@ def objective_coefficients(self): (10, 5) """ return self._AbcvBNz[2] - + def objective_value(self): r""" Return the value of the objective at the :meth:`~LPAbstractDictionary.basic_solution` of ``self``. - + OUTPUT: - - - a number. - + + - a number + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -2977,18 +2970,18 @@ def objective_value(self): 0 """ return self._AbcvBNz[3] - + def update(self): r""" Update ``self`` using previously set entering and leaving variables. - + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) sage: P = LPProblemStandardForm(A, b, c) - sage: D = P.initial_dictionary() + sage: D = P.initial_dictionary() sage: D.objective_value() 0 sage: D.enter("x1") @@ -3010,7 +3003,7 @@ def update(self): if Ale == 0: raise ValueError("incompatible choice of entering and leaving " "variables") - # Variables + # Variables B[l] = entering N[e] = leaving # "The Changing Relation" @@ -3026,34 +3019,34 @@ def update(self): A[i] -= Aie * A[l] # Objective ce = c[e] - c[e] = 0 + c[e] = 0 self._AbcvBNz[2] = c - ce * A[l] self._AbcvBNz[3] += ce * b[l] self._entering = None self._leaving = None - + def random_dictionary(m, n, bound=5, special_probability=0.2): r""" Construct a random dictionary. - + INPUT: - - - ``m`` -- the number of constraints/basic variables; - - - ``n`` -- the number of decision/non-basic variables; - - - ``bound`` -- (default: 5) a bound on dictionary entries; - + + - ``m`` -- the number of constraints/basic variables + + - ``n`` -- the number of decision/non-basic variables + + - ``bound`` -- (default: 5) a bound on dictionary entries + - ``special_probability`` -- (default: 0.2) probability of constructing a - potentially infeasible or potentially optimal dictionary. - + potentially infeasible or potentially optimal dictionary + OUTPUT: - - - an :class:`LP problem dictionary `. - + + - an :class:`LP problem dictionary ` + EXAMPLES:: - + sage: from sage.numerical.interactive_simplex_method \ ....: import random_dictionary sage: random_dictionary(3, 4) @@ -3063,7 +3056,7 @@ def random_dictionary(m, n, bound=5, special_probability=0.2): if special_probability < random(): b = random_vector(ZZ, m, x=0, y=bound).change_ring(QQ) else: # Allow infeasible dictionary - b = random_vector(ZZ, m, x=-bound, y=bound).change_ring(QQ) + b = random_vector(ZZ, m, x=-bound, y=bound).change_ring(QQ) if special_probability < random(): c = random_vector(ZZ, n, x=-bound, y=bound).change_ring(QQ) else: # Make dual feasible dictionary @@ -3079,32 +3072,32 @@ def random_dictionary(m, n, bound=5, special_probability=0.2): class LPRevisedDictionary(LPAbstractDictionary): r""" Construct a revised dictionary for an LP problem. - + INPUT: - ``problem`` -- an :class:`LP problem in standard form - `; + ` - - ``basic_variables`` -- a list of basic variables or their indices. + - ``basic_variables`` -- a list of basic variables or their indices OUTPUT: - - a :class:`revised dictionary for an LP problem `. - + - a :class:`revised dictionary for an LP problem ` + A revised dictionary encodes the same relations as a :class:`regular dictionary `, but stores only what is "necessary to efficiently compute data for the simplex method". - + Let the original problem be - - .. math:: - + + .. MATH:: + \begin{array}{l} \pm \max cx \\ Ax \leq b \\ x \geq 0 \end{array} - + Let `\bar{x}` be the vector of :meth:`~LPProblem.decision_variables` `x` followed by the :meth:`~LPProblemStandardForm.slack_variables`. Let `\bar{c}` be the vector of :meth:`~LPProblem.objective_coefficients` `c` @@ -3112,16 +3105,16 @@ class LPRevisedDictionary(LPAbstractDictionary): Let `\bar{A} = (A | I)` be the matrix of :meth:`~LPProblem.constraint_coefficients` `A` augmented by the identity matrix as columns corresponding to the slack variables. Then the problem - above can be written as - - .. math:: - + above can be written as + + .. MATH:: + \begin{array}{l} \pm \max \bar{c} \bar{x} \\ \bar{A} \bar{x} = b \\ \bar{x} \geq 0 \end{array} - + and any dictionary is a system of equations equivalent to `\bar{A} \bar{x} = b`, but resolved for :meth:`basic_variables` `x_B` in terms of :meth:`nonbasic_variables` `x_N` together with the expression for @@ -3129,24 +3122,24 @@ class LPRevisedDictionary(LPAbstractDictionary): "splitting `\bar{c}` into basic and non-basic parts". Let :meth:`B` and :meth:`A_N` be the splitting of `\bar{A}`. Then the corresponding dictionary is - - .. math:: - + + .. MATH:: + \begin{array}{|l|} \hline x_B = B^{-1} b - B^{-1} A_N x_N\\ \hline z = y b + \left(c_N - y^T A_N\right) x_N\\ \hline - \end{array} - + \end{array} + where `y = c_B^T B^{-1}`. To proceed with the simplex method, it is not necessary to compute all entries of this dictionary. On the other hand, any entry is easy to compute, if you know `B^{-1}`, so we keep track of it through the update steps. - + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3158,16 +3151,16 @@ class LPRevisedDictionary(LPAbstractDictionary): (x1, x2) sage: D LP problem dictionary (use typeset mode to see details) - + The same dictionary can be constructed through the problem:: - + sage: P.revised_dictionary(1, 2) == D True - + When this dictionary is typeset, you will see two tables like these ones: - - .. math:: - + + .. MATH:: + \renewcommand{\arraystretch}{1.500000} \begin{array}{l} \begin{array}{l|r|rr||r||r} @@ -3186,7 +3179,7 @@ class LPRevisedDictionary(LPAbstractDictionary): c_N^T - y^T A_N & -\frac{5}{2} & -\frac{5}{2} \\ \end{array} \end{array} - + More details will be shown if entering and leaving variables are set, but in any case the top table shows `B^{-1}` and a few extra columns, while the bottom one shows several rows: these are related to columns and rows of @@ -3198,7 +3191,7 @@ def __init__(self, problem, basic_variables): See :class:`LPRevisedDictionary` for documentation. TESTS::: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3219,17 +3212,17 @@ def __init__(self, problem, basic_variables): def __eq__(self, other): r""" Check if two revised LP problem dictionaries are equal. - + INPUT: - - - ``other`` -- anything. - + + - ``other`` -- anything + OUTPUT: - + - ``True`` if ``other`` is an :class:`LPRevisedDictionary` for the same :class:`LPProblemStandardForm` with the same :meth:`basic_variables`, - ``False`` otherwise. - + ``False`` otherwise + TESTS:: sage: A = ([1, 1], [3, 1]) @@ -3248,17 +3241,17 @@ def __eq__(self, other): sage: D1 == D3 False """ - return (isinstance(other, LPRevisedDictionary) and + return (isinstance(other, LPRevisedDictionary) and self._problem == other._problem and self._x_B == other._x_B) - + def _latex_(self): r""" Return a LaTeX representation of ``self``. OUTPUT: - - a string. + - a string TESTS:: @@ -3282,14 +3275,14 @@ def _latex_(self): \begin{array}{r|rr} x_N & \color{green} x_{1} & x_{2} \\ \hline - c_N^T & \color{green} 10 & 5 \\ + c_N^T & \color{green} 10 & 5 \\ \hline y^T A_N & \color{green} 0 & 0 \\ \hline c_N^T - y^T A_N & \color{green} 10 & 5 \\ \end{array} \end{array} - """ + """ latex.add_package_to_preamble_if_available("color") x_B = self._x_B m = len(x_B) @@ -3317,7 +3310,7 @@ def _latex_(self): lines.append(r"\hline") Bi = self.B_inverse() c_B = self.c_B() - y = self.y() + y = self.y() Bib = self.constant_terms() if entering is not None: Biae = self.entering_coefficients() @@ -3343,16 +3336,16 @@ def _latex_(self): lines.append(" & ".join(terms) + r" \\") lines.append(r"\end{array}") top = "\n".join(lines) - + def make_line(header, terms): terms = map(latex, terms) if entering is not None: t = terms[k] if not generate_real_LaTeX: t = "{" + t + "}" - terms[k] = r"\color{green} " + t + terms[k] = r"\color{green} " + t lines.append(" & ".join([header] + terms) + r" \\") - + lines = [] x_N = self.x_N() if entering is not None: @@ -3381,17 +3374,17 @@ def make_line(header, terms): def A(self, v): r""" Return the column of constraint coefficients corresponding to ``v``. - + INPUT: - - - ``v`` -- a variable, its name, or its index. - + + - ``v`` -- a variable, its name, or its index + OUTPUT: - - - a vector. - + + - a vector + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3419,14 +3412,15 @@ def A(self, v): def A_N(self): r""" - Return the `A_N` matrix, constraint coefficients of non-basic variables. - + Return the `A_N` matrix, constraint coefficients of + non-basic variables. + OUTPUT: - - - a matrix. - + + - a matrix + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3438,17 +3432,18 @@ def A_N(self): """ return column_matrix(self.problem().base_ring(), [self.A(x) for x in self.x_N()]) - + def B(self): r""" - Return the `B` matrix, i.e. constraint coefficients of basic variables. - + Return the `B` matrix, i.e. constraint coefficients of + basic variables. + OUTPUT: - - - a matrix. - + + - a matrix + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3464,16 +3459,16 @@ def B(self): def B_inverse(self): r""" Return the inverse of the :meth:`B` matrix. - + This inverse matrix is stored and computed during dictionary update in a more efficient way than generic inversion. - + OUTPUT: - - - a matrix. - + + - a matrix + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3492,17 +3487,17 @@ def B_inverse(self): def E(self): r""" Return the eta matrix between ``self`` and the next dictionary. - + OUTPUT: - - - a matrix. - - If `B_\mathrm{old}` is the current matrix `B` and `B_\mathrm{new}` is - the `B` matrix of the next dictionary (after the update step), then - `B_\mathrm{new} = B_\mathrm{old} E`. - + + - a matrix + + If `B_{\mathrm{old}}` is the current matrix `B` and `B_{\mathrm{new}}` + is the `B` matrix of the next dictionary (after the update step), then + `B_{\mathrm{new}} = B_{\mathrm{old}} E`. + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3525,20 +3520,20 @@ def E(self): E = identity_matrix(self.base_ring(), self.problem().m()) E.set_column(l, self.entering_coefficients()) return E - + def E_inverse(self): r""" Return the inverse of the matrix :meth:`E`. - + This inverse matrix is computed in a more efficient way than generic inversion. - + OUTPUT: - - - a matrix. - + + - a matrix + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3563,21 +3558,21 @@ def E_inverse(self): def basic_indices(self): r""" Return the basic indices of ``self``. - - .. note:: - + + .. NOTE:: + Basic indices are indices of :meth:`basic_variables` in the list of generators of the :meth:`~LPProblemStandardForm.coordinate_ring` of the :meth:`problem` of ``self``, they may not coincide with the indices of variables which are parts of their names. (They will for the default indexed names.) - + OUTPUT: - + - a list. - + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3586,19 +3581,19 @@ def basic_indices(self): sage: D.basic_indices() [3, 4] """ - gens = self.coordinate_ring().gens() + gens = self.coordinate_ring().gens() return [gens.index(x) for x in self._x_B] def basic_variables(self): r""" Return the basic variables of ``self``. - + OUTPUT: - - - a vector. - + + - a vector + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3612,13 +3607,13 @@ def basic_variables(self): def c_B(self): r""" Return the `c_B` vector, objective coefficients of basic variables. - + OUTPUT: - - - a vector. - + + - a vector + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3638,17 +3633,17 @@ def c_B(self): c_D = P.c() n = P.n() return vector(R, [c_D[k - 1] if k <= n else 0 for k in BB]) - + def c_N(self): r""" Return the `c_N` vector, objective coefficients of non-basic variables. - + OUTPUT: - - - a vector. - + + - a vector + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3666,17 +3661,17 @@ def c_N(self): c_D = P.c() return vector(R, (c_D[k - 1] if k <= n else 0 for k in self.nonbasic_indices())) - + def constant_terms(self): r""" Return constant terms in the relations of ``self``. - + OUTPUT: - - - a vector. - + + - a vector + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3686,17 +3681,17 @@ def constant_terms(self): (1000, 1500) """ return self.B_inverse() * self.problem().b() - + def dictionary(self): r""" Return a regular LP dictionary matching ``self``. - + OUTPUT: - - - an :class:`LP dictionary `. - + + - an :class:`LP dictionary ` + EXAMPLES:: - + sage: A = ([1, 1], [3, 1], [-1, -1]) sage: b = (1000, 1500, -400) sage: c = (10, 5) @@ -3715,17 +3710,17 @@ def dictionary(self): D._entering = self._entering D._leaving = self._leaving return D - + def entering_coefficients(self): r""" Return coefficients of the entering variable. - + OUTPUT: - - - a vector. - + + - a vector + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3739,17 +3734,17 @@ def entering_coefficients(self): raise ValueError("entering variable must be chosen to compute " "its coefficients") return self.B_inverse() * self.A(self._entering) - + def leaving_coefficients(self): r""" Return coefficients of the leaving variable. - + OUTPUT: - - - a vector. - + + - a vector + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3764,26 +3759,26 @@ def leaving_coefficients(self): "its coefficients") i = self.basic_variables().list().index(self._leaving) return self.B_inverse()[i] * self.A_N() - + def nonbasic_indices(self): r""" Return the non-basic indices of ``self``. - - .. note:: - + + .. NOTE:: + Non-basic indices are indices of :meth:`nonbasic_variables` in the list of generators of the :meth:`~LPProblemStandardForm.coordinate_ring` of the :meth:`problem` of ``self``, they may not coincide with the indices of variables which are parts of their names. (They will for the default indexed names.) - + OUTPUT: - - - a list. - + + - a list + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3792,19 +3787,19 @@ def nonbasic_indices(self): sage: D.nonbasic_indices() [1, 2] """ - gens = self.coordinate_ring().gens() + gens = self.coordinate_ring().gens() return [gens.index(x) for x in self.x_N()] def nonbasic_variables(self): r""" Return non-basic variables of ``self``. - + OUTPUT: - - - a vector. - + + - a vector + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3813,22 +3808,22 @@ def nonbasic_variables(self): sage: D.nonbasic_variables() (x1, x2) """ - R = self.coordinate_ring() + R = self.coordinate_ring() return vector(R, [xi for xi in R.gens()[1:] if xi not in self._x_B]) - + def objective_coefficients(self): r""" Return coefficients of the objective of ``self``. - + OUTPUT: - - - a vector. - + + - a vector + These are coefficients of non-basic variables when basic variables are eliminated. - + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3838,17 +3833,17 @@ def objective_coefficients(self): (10, 5) """ return self.c_N() - self.y() * self.A_N() - + def objective_value(self): r""" Return the value of the objective at the basic solution of ``self``. - + OUTPUT: - - - a number. - + + - a number + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3858,15 +3853,15 @@ def objective_value(self): 0 """ return self.y() * self.problem().b() - + def problem(self): r""" Return the original problem. - + OUTPUT: - - - an :class:`LP problem in standard form `. - + + - an :class:`LP problem in standard form ` + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -3878,13 +3873,13 @@ def problem(self): True """ return self._problem - + def update(self): r""" Update ``self`` using previously set entering and leaving variables. - + EXAMPLES:: - + sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) @@ -3904,15 +3899,16 @@ def update(self): self._x_B[self._x_B.list().index(self._leaving)] = self._entering self._entering = None self._leaving = None - + def y(self): r""" - Return the `y` vector, the product of :meth:`c_B` and :meth:`B_inverse`. - + Return the `y` vector, the product of :meth:`c_B` and + :meth:`B_inverse`. + OUTPUT: - - - a vector. - + + - a vector + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -3924,7 +3920,8 @@ def y(self): (0, 0) """ return self.c_B() * self.B_inverse() - + # Aliases for the standard notation x_B = basic_variables x_N = nonbasic_variables + From 21ce795c0be4ea88298f32f64056322d2c18ffd1 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 20 Jun 2014 13:14:22 -0700 Subject: [PATCH 295/546] Some more formatting and minor tweaks. --- .../numerical/interactive_simplex_method.py | 67 ++++++++++--------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/src/sage/numerical/interactive_simplex_method.py b/src/sage/numerical/interactive_simplex_method.py index 338b3dfeeac..b74324711db 100644 --- a/src/sage/numerical/interactive_simplex_method.py +++ b/src/sage/numerical/interactive_simplex_method.py @@ -589,7 +589,7 @@ def _latex_(self): OUTPUT: - - a string. + - a string TESTS:: @@ -705,7 +705,7 @@ def Abcx(self): OUTPUT: - - a tuple. + - a tuple EXAMPLES:: @@ -725,13 +725,13 @@ def base_ring(self): r""" Return the base ring of ``self``. - .. note:: + .. NOTE:: The base ring of LP problems is always a field. OUTPUT: - - a ring. + - a ring EXAMPLES:: @@ -755,7 +755,7 @@ def constant_terms(self): OUTPUT: - - a vector. + - a vector EXAMPLES:: @@ -776,7 +776,7 @@ def constraint_coefficients(self): OUTPUT: - - a matrix. + - a matrix EXAMPLES:: @@ -799,7 +799,7 @@ def decision_variables(self): OUTPUT: - - a vector. + - a vector EXAMPLES:: @@ -820,13 +820,13 @@ def dual(self, y=None): INPUT: - - ``y`` -- (default: "x" if the prefix of ``self`` is "y", "y" - otherwise) a vector of dual decision variables or a string giving the - base name. + - ``y`` -- (default: ``"x"`` if the prefix of ``self`` is ``"y"``, + ``"y"`` otherwise) a vector of dual decision variables or a string + giving the base name OUTPUT: - - an :class:`LPProblem`. + - an :class:`LPProblem` EXAMPLES:: @@ -877,7 +877,7 @@ def feasible_set(self): OUTPUT: - - a :mod:`Polyhedron `. + - a :mod:`Polyhedron ` EXAMPLES:: @@ -917,7 +917,7 @@ def is_bounded(self): OUTPUT: - - ``True`` is ``self`` is bounded, ``False`` otherwise. + - ``True`` is ``self`` is bounded, ``False`` otherwise EXAMPLES:: @@ -936,7 +936,7 @@ def is_feasible(self): OUTPUT: - - ``True`` is ``self`` is feasible, ``False`` otherwise. + - ``True`` is ``self`` is feasible, ``False`` otherwise EXAMPLES:: @@ -955,7 +955,7 @@ def n_constraints(self): OUTPUT: - - an integer. + - an integer EXAMPLES:: @@ -976,7 +976,7 @@ def n_variables(self): OUTPUT: - - an integer. + - an integer EXAMPLES:: @@ -997,7 +997,7 @@ def objective_coefficients(self): OUTPUT: - - a vector. + - a vector EXAMPLES:: @@ -1018,7 +1018,7 @@ def optimal_solution(self): OUTPUT: - - a vector or ``None`` if there are no optimal solutions. + - a vector or ``None`` if there are no optimal solutions EXAMPLES:: @@ -1038,7 +1038,7 @@ def optimal_value(self): OUTPUT: - a number if the problem is bounded, `\pm \infty` if it is unbounded, - or ``None`` if it is infeasible. + or ``None`` if it is infeasible EXAMPLES:: @@ -1057,20 +1057,23 @@ def plot(self, *args, **kwds): INPUT: - - same as for :meth:`plot_feasible_set`. + - ``xmin``, ``xmax``, ``ymin``, ``ymax`` -- bounds for the axes, if + not given, an attempt will be made to pick reasonable values + + - ``alpha`` -- (default: 0.2) determines how opaque are shadows OUTPUT: - - a plot. + - a plot This only works for problems with two decision variables. On the plot the black arrow indicates the direction of growth of the objective. The lines perpendicular to it are level curves of the objective. If there are optimal solutions, the arrow originates in one of them and the - corresponding level curve is solid: all points of the feasible set on it - are optimal solutions. Otherwise the arrow is placed in the center. If - the problem is infeasible or the objective is zero, a plot of the - feasible set only is returned. + corresponding level curve is solid: all points of the feasible set + on it are optimal solutions. Otherwise the arrow is placed in the + center. If the problem is infeasible or the objective is zero, a plot + of the feasible set only is returned. EXAMPLES:: @@ -1134,19 +1137,19 @@ def plot_feasible_set(self, xmin=None, xmax=None, ymin=None, ymax=None, INPUT: - ``xmin``, ``xmax``, ``ymin``, ``ymax`` -- bounds for the axes, if - not given, an attempt will be made to pick reasonable values; + not given, an attempt will be made to pick reasonable values - - ``alpha`` -- (default: 0.2) determines how opaque are shadows. + - ``alpha`` -- (default: 0.2) determines how opaque are shadows OUTPUT: - - a plot. + - a plot This only works for a problem with two decision variables. The plot shows boundaries of constraints with a shadow on one side for - inequalities. If the :meth:`feasible_set` is not empty and at least part - of it is in the given boundaries, it will be shaded gray and `F` will be - placed in its middle. + inequalities. If the :meth:`feasible_set` is not empty and at least + part of it is in the given boundaries, it will be shaded gray and `F` + will be placed in its middle. EXAMPLES:: @@ -1237,7 +1240,7 @@ def standard_form(self): OUTPUT: - - an :class:`LPProblemStandardForm`. + - an :class:`LPProblemStandardForm` EXAMPLES:: From b541f0e0b737e378ebd92f167aed38242ce6a8ed Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 20 Jun 2014 13:38:50 -0700 Subject: [PATCH 296/546] Last review tweaks. --- .../numerical/interactive_simplex_method.py | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/sage/numerical/interactive_simplex_method.py b/src/sage/numerical/interactive_simplex_method.py index b74324711db..2522ffc4fb1 100644 --- a/src/sage/numerical/interactive_simplex_method.py +++ b/src/sage/numerical/interactive_simplex_method.py @@ -145,10 +145,14 @@ for feasible dictionaries with a set entering variable or for dual feasible dictionaries -It is also possible to obtain :meth:`feasible sets ` and -:meth:`final dictionaries ` of problems, -work with :class:`revised dictionaries `, and use the dual -simplex method! +It is also possible to obtain :meth:`feasible sets ` +and :meth:`final dictionaries ` of +problems, work with :class:`revised dictionaries `, +and use the dual simplex method! + +.. NOTE:: + + Currently this does not have a display format for the terminal. """ @@ -209,7 +213,7 @@ def _assemble_arrayl(lines, stretch=None): OUTPUT: - - a :class:`LatexExpr`. + - a :class:`LatexExpr` EXAMPLES:: @@ -1921,14 +1925,15 @@ def run_simplex_method(self): \text{The optimal value: $6250$. An optimal solution: $\left(250,\,750\right)$.} \end{gather*} """ + result = [] + d = self.initial_dictionary() + result.append(latex(d)) + def step(entering, leaving): result.append(r"\text{{Entering: ${}$. Leaving: ${}$.}}" .format(latex(entering), latex(leaving))) result.append(d.ELLUL(entering, leaving)) - result = [] - d = self.initial_dictionary() - result.append(latex(d)) if d.is_feasible(): is_feasible = True else: From d4fe805fa2a815b902d8aa3809d4be9d362af7c6 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 20 Jun 2014 16:26:29 -0700 Subject: [PATCH 297/546] Fixed documentation links. --- src/sage/monoids/indexed_free_monoid.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/monoids/indexed_free_monoid.py b/src/sage/monoids/indexed_free_monoid.py index 9cc4193e721..eb0e4f277b2 100644 --- a/src/sage/monoids/indexed_free_monoid.py +++ b/src/sage/monoids/indexed_free_monoid.py @@ -653,7 +653,7 @@ class IndexedMonoid(Parent, IndexedGenerators, UniqueRepresentation): - ``indices`` -- the indices for the generators For the optional arguments that control the printing, see - :class:`~sage.misc.indexed_generators.IndexedGenerators`. + :class:`~sage.structure.indexed_generators.IndexedGenerators`. """ @staticmethod def __classcall__(cls, indices, prefix="F", **kwds): @@ -837,7 +837,7 @@ class IndexedFreeMonoid(IndexedMonoid): - ``indices`` -- the indices for the generators For the optional arguments that control the printing, see - :class:`~sage.misc.indexed_generators.IndexedGenerators`. + :class:`~sage.structure.indexed_generators.IndexedGenerators`. EXAMPLES:: @@ -907,7 +907,7 @@ class IndexedFreeAbelianMonoid(IndexedMonoid): - ``indices`` -- the indices for the generators For the optional arguments that control the printing, see - :class:`~sage.misc.indexed_generators.IndexedGenerators`. + :class:`~sage.structure.indexed_generators.IndexedGenerators`. EXAMPLES:: From 3355773583dad6fd88a8c7019a9fa19e305b3350 Mon Sep 17 00:00:00 2001 From: Punarbasu Purkayastha Date: Sat, 21 Jun 2014 08:21:50 +0800 Subject: [PATCH 298/546] I hope I got the correct rel tol value :-/ --- src/sage/functions/trig.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/functions/trig.py b/src/sage/functions/trig.py index 757d0d5bb3f..0c3ed86343f 100644 --- a/src/sage/functions/trig.py +++ b/src/sage/functions/trig.py @@ -36,7 +36,7 @@ def __init__(self): sage: conjugate(sin(x)) sin(conjugate(x)) - sage: sin(complex(1,1)) # rel tol + sage: sin(complex(1,1)) # rel tol 1e-12 (1.2984575814159773+0.6349639147847361j) """ @@ -76,7 +76,7 @@ def __init__(self): sage: conjugate(cos(x)) cos(conjugate(x)) - sage: cos(complex(1,1)) # rel tol + sage: cos(complex(1,1)) # rel tol 1e-12 (0.8337300251311491-0.9888977057628651j) """ @@ -120,7 +120,7 @@ def __init__(self): sage: conjugate(tan(x)) tan(conjugate(x)) - sage: tan(complex(1,1)) # rel tol + sage: tan(complex(1,1)) # rel tol 1e-12 (0.2717525853195118+1.0839233273386946j) """ @@ -176,7 +176,7 @@ def _evalf_(self, x, parent=None, algorithm=None): Test complex input:: - sage: sec(complex(1,1)) # rel tol + sage: sec(complex(1,1)) # rel tol 1e-12 (0.49833703055518686+0.5910838417210451j) """ @@ -275,7 +275,7 @@ def _evalf_(self, x, parent=None, algorithm=None): Test complex input:: - sage: csc(complex(1,1)) # rel tol + sage: csc(complex(1,1)) # rel tol 1e-12 (0.6215180171704284-0.30393100162842646j) """ @@ -403,7 +403,7 @@ def _evalf_(self, x, parent=None, algorithm=None): Test complex input:: - sage: cot(complex(1,1)) # rel tol + sage: cot(complex(1,1)) # rel tol 1e-12 (0.21762156185440273-0.8680141428959249j) """ @@ -636,7 +636,7 @@ def _evalf_(self, x, parent=None, algorithm=None): Test complex input:: - sage: arccot(complex(1,1)) # rel tol + sage: arccot(complex(1,1)) # rel tol 1e-12 (0.5535743588970452-0.4023594781085251j) """ @@ -718,7 +718,7 @@ def _evalf_(self, x, parent=None, algorithm=None): Test complex input:: - sage: arccsc(complex(1,1)) # rel tol + sage: arccsc(complex(1,1)) # rel tol 1e-12 (0.45227844715119064-0.5306375309525178j) """ @@ -794,7 +794,7 @@ def _evalf_(self, x, parent=None, algorithm=None): Test complex input:: - sage: arcsec(complex(1,1)) # rel tol + sage: arcsec(complex(1,1)) # rel tol 1e-12 (1.118517879643706+0.5306375309525178j) """ From 5e525eb287b5baf52b966183f3704cbd41964e64 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sat, 21 Jun 2014 11:52:11 +0200 Subject: [PATCH 299/546] trac #15390: fix the case of polynomial over algebraic closure --- .../rings/algebraic_closure_finite_field.py | 42 +++++++++++++++---- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/algebraic_closure_finite_field.py b/src/sage/rings/algebraic_closure_finite_field.py index 91fbcaf6456..ad082eeb0e8 100644 --- a/src/sage/rings/algebraic_closure_finite_field.py +++ b/src/sage/rings/algebraic_closure_finite_field.py @@ -851,33 +851,57 @@ def _roots_univariate_polynomial(self, p, ring=None, multiplicities=None, algori True sage: prod((x - c) for c,_ in r) == P True + + sage: K = GF(5).algebraic_closure() + sage: R. = PolynomialRing(K, 'T') + sage: P = K.gen(4) * T^4 + K.gen(3) * T^3 + K.gen(2) * T^2 + K.gen(1) * T + 1 + sage: r = P.roots() # indirect doctest + sage: sorted(r) + [(2*z36^34 + z36^33 + z36^30 + ... + 2*z36^2 + 4*z36, 1), + (z36^34 + z36^33 + z36^32 + ... + 3*z36^3 + z36^2 + 3, 1), + (3*z12^11 + 4*z12^10 + 2*z12^9 + ... + 4*z12^2 + 2*z12, 1), + (z36^35 + z36^34 + z36^33 + ... + 3*z36^2 + z36 + 4, 1)] + sage: all(P(c) == 0 for c,_ in r) + True + sage: P[4] * prod((T-c) for c,_ in r) == P + True """ from sage.rings.arith import lcm from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing # first build a polynomial over some finite field + # TODO: ideally we would set the option minimal=True in the following + # but it is currently broken coeffs = [v.as_finite_field_element() for v in p.list()] levels = [c[0].degree() for c in coeffs] l = lcm(levels) F,phi = self.subfield(l) - new_coeffs = [F(c[1]) for c in coeffs] + new_coeffs = [self.inclusion(c[0].degree(),l)(c[1]) for c in coeffs] # then factor it and for each factor add some root by going to a larger # finite field if needed P = PolynomialRing(F, 'x') roots = {} # root -> multiplicity - polys = [(g,m,F,phi) for g,m in P(new_coeffs).factor()] - while polys: - g,m,F,phi = polys.pop() - if g.degree() == 1: # got a root ! - c = phi(-g.constant_coefficient()) + for g,m in P(new_coeffs).factor(): + if g.degree() == 1: + r = [phi(-g.constant_coefficient())] + else: + # TODO: once the coercion between the finite subfields are fixed + # this can be done in a more natural way using .change_ring + ll = l * g.degree() + psi = self.inclusion(l, ll) + FF, pphi = self.subfield(ll) + gg = PolynomialRing(FF, 'x')(map(psi, g)) + r = [] + for ggg,_ in gg.factor(): + r.append(pphi(-ggg.constant_coefficient())) + + for c in r: if c not in roots: roots[c] = 0 roots[c] += m - else: # build an extension where the polynomial splits - FF,pphi = self.subfield(F.degree() * g.degree()) - polys.extend([(gg,mm,FF,pphi) for gg,mm in g.change_ring(FF).factor()]) + if multiplicities: return list(roots.iteritems()) return roots.keys() From 5572837f991d5ed6a1674e009de8c38e0c3bbc7c Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Sat, 21 Jun 2014 12:10:39 +0100 Subject: [PATCH 300/546] Trac 16509: fix conversion between finite subfields in AlgebraicClosureFiniteField.as_finite_field_element() --- .../rings/algebraic_closure_finite_field.py | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/algebraic_closure_finite_field.py b/src/sage/rings/algebraic_closure_finite_field.py index f2ba912bc92..2109e6b2e5d 100644 --- a/src/sage/rings/algebraic_closure_finite_field.py +++ b/src/sage/rings/algebraic_closure_finite_field.py @@ -425,14 +425,32 @@ def as_finite_field_element(self, minimal=False): sage: s.as_finite_field_element(minimal=True)[0] Finite Field of size 3 + This also works when the element has to be converted between + two non-trivial finite subfields (see :trac:16509):: + + sage: K = GF(5).algebraic_closure() + sage: z = K.gen(5) - K.gen(5) + K.gen(2) + sage: z.as_finite_field_element(minimal=True) + (Finite Field in z2 of size 5^2, z2, Ring morphism: + From: Finite Field in z2 of size 5^2 + To: Algebraic closure of Finite Field of size 5 + Defn: z2 |--> z2) + """ - if not minimal: - l = self._level - else: - l = self._value.minpoly().degree() + Fbar = self.parent() + x = self._value + l = self._level + + if minimal: + m = x.minpoly().degree() + if m == 1: + x = Fbar.base_ring()(x) + else: + x = Fbar.inclusion(m, l).section()(x) + l = m - F, phi = self.parent().subfield(l) - return (F, F(self._value), phi) + F, phi = Fbar.subfield(l) + return (F, x, phi) class AlgebraicClosureFiniteField_generic(Field): From 8c141d6c37eb4d76e29308a3079cff0b5b4d087f Mon Sep 17 00:00:00 2001 From: vince Date: Sat, 21 Jun 2014 16:13:20 +0100 Subject: [PATCH 301/546] Added one word --- src/sage/game_theory/cooperative_game.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 015d3650417..7474136cd6d 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -43,7 +43,8 @@ class CooperativeGame(SageObject): The type of game that is currently implemented is referred to as a Characteristic function game. This is a game on a set of players `\Omega` that is defined by a value function `v : C \to \RR` where - `C = 2^{\Omega}` is set of all coalitions of players. Let `N := |\Omega|`. + `C = 2^{\Omega}` is the set of all coalitions of players. + Let `N := |\Omega|`. An example of such a game is shown below: .. MATH:: From e989e841387eba59b132e21ce694442af5ef26db Mon Sep 17 00:00:00 2001 From: vince Date: Sat, 21 Jun 2014 17:00:20 +0100 Subject: [PATCH 302/546] Formatted reference slightly better --- src/sage/game_theory/cooperative_game.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 7474136cd6d..0bd111def48 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -245,7 +245,6 @@ class CooperativeGame(SageObject): ISBN 9781608456529, :doi:`10.2200/S00355ED1V01Y201107AIM016`. .. [MSZ2013] Michael Maschler, Solan Eilon, and Zamir Shmuel. - *Game Theory*. Cambridge: Cambridge University Press, (2013). ISBN 9781107005488. From 7c4c72596fe2a6de32e61ca578f2e671a51d7962 Mon Sep 17 00:00:00 2001 From: Punarbasu Purkayastha Date: Sun, 22 Jun 2014 05:07:18 +0800 Subject: [PATCH 303/546] set it to 1e-15 as per vbraun advice --- src/sage/functions/trig.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/functions/trig.py b/src/sage/functions/trig.py index 0c3ed86343f..e73a310f237 100644 --- a/src/sage/functions/trig.py +++ b/src/sage/functions/trig.py @@ -36,7 +36,7 @@ def __init__(self): sage: conjugate(sin(x)) sin(conjugate(x)) - sage: sin(complex(1,1)) # rel tol 1e-12 + sage: sin(complex(1,1)) # rel tol 1e-15 (1.2984575814159773+0.6349639147847361j) """ @@ -76,7 +76,7 @@ def __init__(self): sage: conjugate(cos(x)) cos(conjugate(x)) - sage: cos(complex(1,1)) # rel tol 1e-12 + sage: cos(complex(1,1)) # rel tol 1e-15 (0.8337300251311491-0.9888977057628651j) """ @@ -120,7 +120,7 @@ def __init__(self): sage: conjugate(tan(x)) tan(conjugate(x)) - sage: tan(complex(1,1)) # rel tol 1e-12 + sage: tan(complex(1,1)) # rel tol 1e-15 (0.2717525853195118+1.0839233273386946j) """ @@ -176,7 +176,7 @@ def _evalf_(self, x, parent=None, algorithm=None): Test complex input:: - sage: sec(complex(1,1)) # rel tol 1e-12 + sage: sec(complex(1,1)) # rel tol 1e-15 (0.49833703055518686+0.5910838417210451j) """ @@ -275,7 +275,7 @@ def _evalf_(self, x, parent=None, algorithm=None): Test complex input:: - sage: csc(complex(1,1)) # rel tol 1e-12 + sage: csc(complex(1,1)) # rel tol 1e-15 (0.6215180171704284-0.30393100162842646j) """ @@ -403,7 +403,7 @@ def _evalf_(self, x, parent=None, algorithm=None): Test complex input:: - sage: cot(complex(1,1)) # rel tol 1e-12 + sage: cot(complex(1,1)) # rel tol 1e-15 (0.21762156185440273-0.8680141428959249j) """ @@ -636,7 +636,7 @@ def _evalf_(self, x, parent=None, algorithm=None): Test complex input:: - sage: arccot(complex(1,1)) # rel tol 1e-12 + sage: arccot(complex(1,1)) # rel tol 1e-15 (0.5535743588970452-0.4023594781085251j) """ @@ -718,7 +718,7 @@ def _evalf_(self, x, parent=None, algorithm=None): Test complex input:: - sage: arccsc(complex(1,1)) # rel tol 1e-12 + sage: arccsc(complex(1,1)) # rel tol 1e-15 (0.45227844715119064-0.5306375309525178j) """ @@ -794,7 +794,7 @@ def _evalf_(self, x, parent=None, algorithm=None): Test complex input:: - sage: arcsec(complex(1,1)) # rel tol 1e-12 + sage: arcsec(complex(1,1)) # rel tol 1e-15 (1.118517879643706+0.5306375309525178j) """ From 65cb842d67549494f3a6b1c71f193a4dca328fae Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 21 Jun 2014 15:22:33 -0700 Subject: [PATCH 304/546] Added space where it was not suppose to be. Fixed this. --- .../numerical/interactive_simplex_method.py | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/sage/numerical/interactive_simplex_method.py b/src/sage/numerical/interactive_simplex_method.py index 2522ffc4fb1..3b9baaf68c4 100644 --- a/src/sage/numerical/interactive_simplex_method.py +++ b/src/sage/numerical/interactive_simplex_method.py @@ -67,7 +67,7 @@ sage: LPProblemStandardForm(A, b, c, ["C", "B"]) LP problem (use typeset mode to see details) -or from an already constructed problem of "general type" :: +or from an already constructed problem of "general type":: sage: P = P.standard_form() @@ -75,7 +75,7 @@ standard form, but this step is still necessary to enable methods related to the simplex method. -The simplest way to use the simplex method is :: +The simplest way to use the simplex method is:: sage: P.run_simplex_method() '...' @@ -134,7 +134,8 @@ 5000 If you are unsure about picking entering and leaving variables, you can use -helper methods that will try their best to tell you what are your next options:: +helper methods that will try their best to tell you what are your next +options:: sage: D.possible_entering() [B] @@ -206,10 +207,10 @@ def _assemble_arrayl(lines, stretch=None): INPUT: - - ``lines`` -- a list of strings suitable for math mode typesetting; + - ``lines`` -- a list of strings suitable for math mode typesetting - ``stretch`` -- (default: None) if given, a command setting - ``\arraystretch`` to this value will be added before the array. + ``\arraystretch`` to this value will be added before the array OUTPUT: @@ -559,7 +560,7 @@ def __eq__(self, other): INPUT: - - ``other`` -- anything. + - ``other`` -- anything OUTPUT: @@ -640,7 +641,7 @@ def _repr_(self): OUTPUT: - - a string. + - a string TESTS:: @@ -660,9 +661,10 @@ def _solve(self): OUTPUT: - - a pair consisting of a vector and a number. If the problem is - infeasible, both components are ``None``. If the problem is unbounded, - the first component is ``None`` and the second is `\pm \infty`. + - A pair consisting of a vector and a number. If the problem is + infeasible, both components are ``None``. If the problem is + unbounded, the first component is ``None`` and the second is + `\pm \infty`. This function uses "brute force" solution technique of evaluating the objective at all vertices of the feasible set and taking into account @@ -1341,7 +1343,7 @@ class LPProblemStandardForm(LPProblem): - ``auxiliary_variable`` -- (default: same as ``x`` parameter with adjoined ``"0"`` if it was given as a string, otherwise ``"x0"``) the auxiliary - name, expected to be the same as the first decision variable for + name, expected to be the same as the first decision variable for auxiliary problems - ``objective`` -- (default: ``"z"``) the objective variable (used for the @@ -2170,7 +2172,7 @@ def dual_ratios(self): .. MATH:: - x_i = b_i - \cdots - a_{ij} x_j - \cdots. + x_i = b_i - \cdots - a_{ij} x_j - \cdots. The order of pairs matches the order of :meth:`~LPDictionary.nonbasic_variables`, @@ -2208,8 +2210,8 @@ def enter(self, v): OUTPUT: - none, but the selected variable will be used as entering by methods - that require an entering variable and the corresponding column will be - typeset in green + that require an entering variable and the corresponding column + will be typeset in green EXAMPLES:: @@ -2524,11 +2526,11 @@ def ratios(self): .. MATH:: - x_i = b_i - \cdots - a_{ik} x_k - \cdots. + x_i = b_i - \cdots - a_{ik} x_k - \cdots. - The order of pairs matches the order of - :meth:`~LPDictionary.basic_variables`, - but only `x_i` with positive `a_{ik}` are considered. + The order of pairs matches the order of + :meth:`~LPDictionary.basic_variables`, + but only `x_i` with positive `a_{ik}` are considered. EXAMPLES:: From 384b5a23011b43380be2c434e2487121aaa7dea8 Mon Sep 17 00:00:00 2001 From: Punarbasu Purkayastha Date: Sun, 22 Jun 2014 15:45:05 +0800 Subject: [PATCH 305/546] catch TypeError after #16439 is applied --- src/sage/plot/plot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index 18bda40ebd5..57f9f1a753d 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -1381,7 +1381,7 @@ def _plot(funcs, xrange, parametric=False, for x,fdata in data: try: newdata.append((fdata, g(x))) - except ValueError: + except (ValueError, TypeError): newdata.append((fdata, 0)) # append a dummy value 0 excluded_points.append(x) data = newdata From 49ce5587517a69cc32215cd4763ab10de55a76b2 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 22 Jun 2014 12:28:36 +0200 Subject: [PATCH 306/546] Trac 16509: typo + more doc --- .../rings/algebraic_closure_finite_field.py | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/algebraic_closure_finite_field.py b/src/sage/rings/algebraic_closure_finite_field.py index 2109e6b2e5d..278eef1375a 100644 --- a/src/sage/rings/algebraic_closure_finite_field.py +++ b/src/sage/rings/algebraic_closure_finite_field.py @@ -426,7 +426,7 @@ def as_finite_field_element(self, minimal=False): Finite Field of size 3 This also works when the element has to be converted between - two non-trivial finite subfields (see :trac:16509):: + two non-trivial finite subfields (see :trac:`16509`):: sage: K = GF(5).algebraic_closure() sage: z = K.gen(5) - K.gen(5) + K.gen(2) @@ -436,6 +436,29 @@ def as_finite_field_element(self, minimal=False): To: Algebraic closure of Finite Field of size 5 Defn: z2 |--> z2) + There is currently no automatic conversion between the various + subfields:: + + sage: a = K.gen(2) + 1 + sage: _,b,_ = a.as_finite_field_element() + sage: K4 = K.subfield(4)[0] + sage: K4(b) + Traceback (most recent call last): + ... + TypeError: unable to coerce from a finite field other than the prime + subfield + + Nevertheless it is possible to use the inclusions that are implemented at + the level of the algebraic closure:: + + sage: f = K.inclusion(2,4); f + Ring morphism: + From: Finite Field in z2 of size 5^2 + To: Finite Field in z4 of size 5^4 + Defn: z2 |--> z4^3 + z4^2 + z4 + 3 + sage: f(b) + z4^3 + z4^2 + z4 + 4 + """ Fbar = self.parent() x = self._value From 42c5808ea1a7961a9dd22de18a36f587aacc5e03 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 22 Jun 2014 15:47:43 +0200 Subject: [PATCH 307/546] trac #15390: make factorization works as well --- src/sage/matrix/matrix2.pyx | 12 +- .../rings/algebraic_closure_finite_field.py | 106 +++++++++--------- 2 files changed, 57 insertions(+), 61 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 44e42b8d821..b13e4e82b81 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -5348,7 +5348,7 @@ cdef class Matrix(matrix1.Matrix): sage: M = matrix(GF(3), [[0,1,1],[1,2,0],[2,0,1]]) sage: ev = M.eigenvalues(); ev - [2*z3 + 2, 2*z3 + 1, 2*z3] + [2*z3, 2*z3 + 2, 2*z3 + 1] Similarly as in the case of QQbar, the eigenvalues belong to some algebraic closure but they can be converted to elements of a finite @@ -5358,12 +5358,10 @@ cdef class Matrix(matrix1.Matrix): sage: e.parent() Algebraic closure of Finite Field of size 3 sage: e.as_finite_field_element() - (Finite Field in z3 of size 3^3, - 2*z3 + 2, - Ring morphism: - From: Finite Field in z3 of size 3^3 - To: Algebraic closure of Finite Field of size 3 - Defn: z3 |--> z3) + (Finite Field in z3 of size 3^3, 2*z3, Ring morphism: + From: Finite Field in z3 of size 3^3 + To: Algebraic closure of Finite Field of size 3 + Defn: z3 |--> z3) """ x = self.fetch('eigenvalues') if x is not None: diff --git a/src/sage/rings/algebraic_closure_finite_field.py b/src/sage/rings/algebraic_closure_finite_field.py index ad082eeb0e8..0618c8b4d0f 100644 --- a/src/sage/rings/algebraic_closure_finite_field.py +++ b/src/sage/rings/algebraic_closure_finite_field.py @@ -828,84 +828,82 @@ def some_elements(self): """ return (self(1), self.gen(2), 1+self.gen(3)) - def _roots_univariate_polynomial(self, p, ring=None, multiplicities=None, algorithm=None): r""" - Return the roots of the polynomial ``p``. + Return a list of pairs ``(root,multiplicity)`` of roots of the polynomial ``p``. + + If the argument ``multiplicities`` is set to ``False`` then return the + list of roots. + + .. SEEALSO:: + + :meth:`_factor_univariate_polynomial` EXAMPLES:: - sage: R. = PolynomialRing(GF(3),'x') - sage: P = x^7 + 2*x^6 + x^5 + x^4 + 2*x^3 + 2*x^2 + x + 1 - sage: K = GF(3).algebraic_closure('t') - sage: r = P.roots(K) # indirect doctest - sage: sorted(r) - [(t7^5 + t7 + 2, 1), - (t7^5 + 2*t7 + 2, 1), - (t7^5 + 2*t7^3 + t7 + 2, 1), - (t7^5 + 2*t7^4 + 2*t7^3 + t7^2 + t7 + 2, 1), - (t7^6 + t7^3 + t7 + 1, 1), - (t7^6 + 2*t7^4 + 2*t7^3 + 2*t7^2 + 2*t7 + 1, 1), - (t7^6 + 2*t7^5 + 2*t7^4 + 2*t7^3 + t7, 1)] - sage: all(P(c) == 0 for c,_ in r) - True - sage: prod((x - c) for c,_ in r) == P - True + sage: R. = PolynomialRing(GF(5),'x') + sage: K = GF(5).algebraic_closure('t') + + sage: sorted((x^6 - 1).roots(K,multiplicities=False)) + [1, 4, 2*t2 + 1, 2*t2 + 2, 3*t2 + 3, 3*t2 + 4] + sage: ((K.gen(2)*x - K.gen(3))**2).roots(K) + [(3*t6^5 + 2*t6^4 + 2*t6^2 + 3, 2)] + + sage: for _ in xrange(10): + ....: p = R.random_element(degree=randint(2,8)) + ....: for r in p.roots(K, multiplicities=False): + ....: assert p(r).is_zero() - sage: K = GF(5).algebraic_closure() - sage: R. = PolynomialRing(K, 'T') - sage: P = K.gen(4) * T^4 + K.gen(3) * T^3 + K.gen(2) * T^2 + K.gen(1) * T + 1 - sage: r = P.roots() # indirect doctest - sage: sorted(r) - [(2*z36^34 + z36^33 + z36^30 + ... + 2*z36^2 + 4*z36, 1), - (z36^34 + z36^33 + z36^32 + ... + 3*z36^3 + z36^2 + 3, 1), - (3*z12^11 + 4*z12^10 + 2*z12^9 + ... + 4*z12^2 + 2*z12, 1), - (z36^35 + z36^34 + z36^33 + ... + 3*z36^2 + z36 + 4, 1)] - sage: all(P(c) == 0 for c,_ in r) - True - sage: P[4] * prod((T-c) for c,_ in r) == P - True """ from sage.rings.arith import lcm from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing # first build a polynomial over some finite field - # TODO: ideally we would set the option minimal=True in the following - # but it is currently broken - coeffs = [v.as_finite_field_element() for v in p.list()] - levels = [c[0].degree() for c in coeffs] - l = lcm(levels) + coeffs = [v.as_finite_field_element(minimal=True) for v in p.list()] + l = lcm([c[0].degree() for c in coeffs]) F,phi = self.subfield(l) - new_coeffs = [self.inclusion(c[0].degree(),l)(c[1]) for c in coeffs] + P = p.parent().change_ring(F) - # then factor it and for each factor add some root by going to a larger - # finite field if needed - P = PolynomialRing(F, 'x') + new_coeffs = [self.inclusion(c[0].degree(),l)(c[1]) for c in coeffs] - roots = {} # root -> multiplicity + roots = [] # a list of pair (root,multiplicity) for g,m in P(new_coeffs).factor(): if g.degree() == 1: - r = [phi(-g.constant_coefficient())] + r = phi(-g.constant_coefficient()) + roots.append((r,m)) else: - # TODO: once the coercion between the finite subfields are fixed - # this can be done in a more natural way using .change_ring ll = l * g.degree() psi = self.inclusion(l, ll) FF, pphi = self.subfield(ll) gg = PolynomialRing(FF, 'x')(map(psi, g)) - r = [] - for ggg,_ in gg.factor(): - r.append(pphi(-ggg.constant_coefficient())) - - for c in r: - if c not in roots: - roots[c] = 0 - roots[c] += m + for r,_ in gg.roots(): # note: we know that multiplicity is 1 + roots.append((pphi(r),m)) if multiplicities: - return list(roots.iteritems()) - return roots.keys() + return roots + else: + return [r[0] for r in roots] + + def _factor_univariate_polynomial(self, p, **kwds): + r""" + Factorization of univariate polynomials. + EXAMPLES:: + + sage: K = GF(3).algebraic_closure() + sage: R = PolynomialRing(K, 'T') + sage: T = R.gen() + sage: (K.gen(2) * T^2 - 1).factor() + (z2) * (T + z4^3 + z4^2 + z4) * (T + 2*z4^3 + 2*z4^2 + 2*z4) + + sage: for d in xrange(10): + ....: p = R.random_element(degree=randint(2,8)) + ....: assert p.factor().prod() == p + + """ + from sage.structure.factorization import Factorization + R = p.parent() + return Factorization([(R([-root,self.one()]),m) for root,m in p.roots()], unit=p[p.degree()]) class AlgebraicClosureFiniteField_pseudo_conway(AlgebraicClosureFiniteField_generic, WithEqualityById): """ From 1eee50b9282d2b007b7435af9a866d996369dd54 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 22 Jun 2014 19:08:59 +0200 Subject: [PATCH 308/546] trac 15390: put space after comma --- src/sage/matrix/matrix2.pyx | 2 +- src/sage/rings/algebraic_closure_finite_field.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index b13e4e82b81..0c064f9e76f 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -5394,7 +5394,7 @@ cdef class Matrix(matrix1.Matrix): raise NotImplementedError("algebraic closure is not implemented for %s"%K) res = [] - for f,e in self.charpoly().change_ring(K).factor(): + for f, e in self.charpoly().change_ring(K).factor(): if f.degree() == 1: res.extend([-f.constant_coefficient()]*e) else: diff --git a/src/sage/rings/algebraic_closure_finite_field.py b/src/sage/rings/algebraic_closure_finite_field.py index 0618c8b4d0f..63e476f3536 100644 --- a/src/sage/rings/algebraic_closure_finite_field.py +++ b/src/sage/rings/algebraic_closure_finite_field.py @@ -861,13 +861,13 @@ def _roots_univariate_polynomial(self, p, ring=None, multiplicities=None, algori # first build a polynomial over some finite field coeffs = [v.as_finite_field_element(minimal=True) for v in p.list()] l = lcm([c[0].degree() for c in coeffs]) - F,phi = self.subfield(l) + F, phi = self.subfield(l) P = p.parent().change_ring(F) - new_coeffs = [self.inclusion(c[0].degree(),l)(c[1]) for c in coeffs] + new_coeffs = [self.inclusion(c[0].degree(), l)(c[1]) for c in coeffs] roots = [] # a list of pair (root,multiplicity) - for g,m in P(new_coeffs).factor(): + for g, m in P(new_coeffs).factor(): if g.degree() == 1: r = phi(-g.constant_coefficient()) roots.append((r,m)) @@ -876,8 +876,8 @@ def _roots_univariate_polynomial(self, p, ring=None, multiplicities=None, algori psi = self.inclusion(l, ll) FF, pphi = self.subfield(ll) gg = PolynomialRing(FF, 'x')(map(psi, g)) - for r,_ in gg.roots(): # note: we know that multiplicity is 1 - roots.append((pphi(r),m)) + for r, _ in gg.roots(): # note: we know that multiplicity is 1 + roots.append((pphi(r), m)) if multiplicities: return roots @@ -903,7 +903,7 @@ def _factor_univariate_polynomial(self, p, **kwds): """ from sage.structure.factorization import Factorization R = p.parent() - return Factorization([(R([-root,self.one()]),m) for root,m in p.roots()], unit=p[p.degree()]) + return Factorization([(R([-root, self.one()]), m) for root, m in p.roots()], unit=p[p.degree()]) class AlgebraicClosureFiniteField_pseudo_conway(AlgebraicClosureFiniteField_generic, WithEqualityById): """ From 07ab433665c12167d61c688be615c95a6adfcf74 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 22 Jun 2014 18:09:38 -0700 Subject: [PATCH 309/546] Implemented fallback univariate factor using singular. --- .../rings/polynomial/polynomial_element.pyx | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index cda897efafa..b2b852841e8 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -3322,6 +3322,23 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: factor(f) (x - a1) * (x^2 + a1*x + a1^2) + We check that :trac:`7554` is fixed:: + + sage: L. = LaurentPolynomialRing(QQ) + sage: F = L.fraction_field() + sage: R. = PolynomialRing(F) + sage: factor(x) + x + sage: factor(x^2 - q^2) + (-1) * (-x + q) * (x + q) + sage: factor(x^2 - q^-2) + (1/q^2) * (q*x - 1) * (q*x + 1) + + sage: P. = PolynomialRing(ZZ) + sage: R. = PolynomialRing(FractionField(P)) + sage: p = (x - a)*(b*x + c)*(a*b*x + a*c) / (a + 2) + sage: factor(p) + (a/(a + 2)) * (x - a) * (b*x + c)^2 """ # PERFORMANCE NOTE: # In many tests with SMALL degree PARI is substantially @@ -3455,7 +3472,32 @@ cdef class Polynomial(CommutativeAlgebraElement): G = self._pari_with_name().factor() if G is None: - raise NotImplementedError + # See if we can do this as a singular polynomial as a fallback + # This was copied from the general multivariate implementation + try: + if R.is_finite(): + if R.characteristic() > 1<<29: + raise NotImplementedError("Factorization of multivariate polynomials over prime fields with characteristic > 2^29 is not implemented.") + + P = self.parent() + P._singular_().set_ring() + S = self._singular_().factorize() + factors = S[1] + exponents = S[2] + v = sorted([( P(factors[i+1]), + sage.rings.integer.Integer(exponents[i+1]) ) + for i in range(len(factors))]) + unit = P.one() + for i in range(len(v)): + if v[i][0].is_unit(): + unit = unit * v[i][0] + del v[i] + break + F = Factorization(v, unit=unit) + F.sort() + return F + except (TypeError, AttributeError): + raise NotImplementedError return self._factor_pari_helper(G, n) From 17dd13462c4191748f76a2ce91bb671556f0ff24 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 22 Jun 2014 18:13:09 -0700 Subject: [PATCH 310/546] Added doctest to multivariate factor on original issue. --- src/sage/rings/polynomial/multi_polynomial_element.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/rings/polynomial/multi_polynomial_element.py b/src/sage/rings/polynomial/multi_polynomial_element.py index cc847adee77..b0aae126113 100644 --- a/src/sage/rings/polynomial/multi_polynomial_element.py +++ b/src/sage/rings/polynomial/multi_polynomial_element.py @@ -1603,6 +1603,13 @@ def factor(self, proof=True): Traceback (most recent call last): ... NotImplementedError: Factorization of multivariate polynomials over prime fields with characteristic > 2^29 is not implemented. + + We check that the original issue in :trac:`7554` is fixed:: + + sage: K. = PolynomialRing(QQ) + sage: R. = PolynomialRing(FractionField(K)) + sage: factor(x) + x """ R = self.parent() From 3e01acb6cc2f189d606f6877264f7d635a9a2036 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Mon, 23 Jun 2014 08:04:10 +0200 Subject: [PATCH 311/546] trac #16430: micro improvements --- src/sage/combinat/designs/bibd.py | 15 ++++++++++----- src/sage/combinat/designs/orthogonal_arrays.py | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index ffbc5beb147..8da24bdb141 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -655,7 +655,10 @@ def BIBD_from_PBD(PBD,v,k,check=True,base_cases={}): def _check_pbd(B,v,S): r""" - Checks that ``B`` is a PBD on `v` points with given block sizes. + Checks that ``B`` is a PBD on ``v`` points with given block sizes ``S``. + + The points of the balanced incomplete block design are implicitely assumed + to be `\{0, ..., v-1\}`. INPUT: @@ -683,7 +686,7 @@ def _check_pbd(B,v,S): sage: _check_pbd([[1,2]],2,[2]) Traceback (most recent call last): ... - RuntimeError: The PBD covers a point 2 which is not in [0,...,1] + RuntimeError: The PBD covers a point 2 which is not in {0, 1} sage: _check_pbd([[1,2]]*2,2,[2]) Traceback (most recent call last): ... @@ -723,14 +726,16 @@ def _check_pbd(B,v,S): m = m_tmp if g.vertices() != range(v): - p = list(set(g.vertices())-set(range(v)))[0] - raise RuntimeError("The PBD covers a point {} which is not in [0,...,{}]".format(p,v-1)) + from sage.sets.integer_range import IntegerRange + p = (set(g.vertices())-set(range(v))).pop() + raise RuntimeError("The PBD covers a point {} which is not in {}".format(p,IntegerRange(v))) + if not g.is_clique(): for p1 in g: if g.degree(p1) != v-1: break neighbors = g.neighbors(p1)+[p1] - p2 = list(set(g.vertices())-set(neighbors))[0] + p2 = (set(g.vertices())-set(neighbors)).pop() raise RuntimeError("The pair ({},{}) is not covered".format(p1,p2)) return B diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index f6187dd1a73..d30cef9c37e 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -95,7 +95,6 @@ def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): .. SEEALSO:: - :func:`orthogonal_array` -- a tranversal design `TD(k,n)` is equivalent to an orthogonal array `OA(k,n,2)`. EXAMPLES:: @@ -829,6 +828,7 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): sage: t = 3 sage: designs.orthogonal_array(None,5,t=t,existence=True) == t True + sage: _ = designs.orthogonal_array(t,5,t) """ from latin_squares import mutually_orthogonal_latin_squares @@ -1464,7 +1464,7 @@ def OA_from_PBD(k,n,PBD, check=True): sage: _ = OA_from_PBD(3,6,pbd) Traceback (most recent call last): ... - RuntimeError: The PBD covers a point 8 which is not in [0,...,5] + RuntimeError: The PBD covers a point 8 which is not in {0, .., 5} """ # Size of the sets of the PBD K = set(map(len,PBD)) From 29264c639511608c3b14e7003fdb0e13b1a32875 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Mon, 23 Jun 2014 09:15:00 +0200 Subject: [PATCH 312/546] Refactor squarefree_decomposition of polynomials The implemented algorithms only work over fields of characteristic zero or finite fields. For other base rings a NotImplementedError is now raised. Introduced _squarefree_decomposition_univariate_polynomial to allow base fields to implement specialized algorithms. --- src/sage/categories/fields.py | 69 ++++++++++ .../rings/finite_rings/finite_field_base.pyx | 102 +++++++++++++++ .../rings/polynomial/polynomial_element.pyx | 120 +----------------- 3 files changed, 178 insertions(+), 113 deletions(-) diff --git a/src/sage/categories/fields.py b/src/sage/categories/fields.py index 0a1de703129..5d24a83f674 100644 --- a/src/sage/categories/fields.py +++ b/src/sage/categories/fields.py @@ -241,6 +241,75 @@ def fraction_field(self): """ return self + def _squarefree_decomposition_univariate_polynomial(self, f): + r""" + Return the square-free decomposition of ``f`` over this field. + + This is a helper method for + :meth:`sage.rings.polynomial.squarefree_decomposition`. + + INPUT: + + - ``f`` -- a univariate non-zero polynomial over this field + + ALGORITHM: For rings of characteristic zero, we use the algorithm + descriped in [Yun]_. Other fields may provide their own + implementation by overriding this method. + + EXAMPLES:: + + sage: x = polygen(QQ) + sage: p = 37 * (x-1)^3 * (x-2)^3 * (x-1/3)^7 * (x-3/7) + sage: p.squarefree_decomposition() + (37*x - 111/7) * (x^2 - 3*x + 2)^3 * (x - 1/3)^7 + sage: p = 37 * (x-2/3)^2 + sage: p.squarefree_decomposition() + (37) * (x - 2/3)^2 + sage: x = polygen(GF(3)) + sage: x.squarefree_decomposition() + x + sage: f = QQbar['x'](1) + sage: f.squarefree_decomposition() + 1 + + REFERENCES: + + .. [Yun] Yun, David YY. On square-free decomposition algorithms. + In Proceedings of the third ACM symposium on Symbolic and algebraic + computation, pp. 26-35. ACM, 1976. + + """ + from sage.structure.factorization import Factorization + if f.degree() == 0: + return Factorization([], unit=f[0]) + if self.characteristic() != 0: + raise NotImplementedError("square-free decomposition not implemented for this polynomial.") + + factors = [] + cur = f + f = [f] + while cur.degree() > 0: + cur = cur.gcd(cur.derivative()) + f.append(cur) + + g = [] + for i in range(len(f) - 1): + g.append(f[i] // f[i+1]) + + a = [] + for i in range(len(g) - 1): + a.append(g[i] // g[i+1]) + a.append(g[-1]) + + unit = f[-1] + for i in range(len(a)): + if a[i].degree() > 0: + factors.append((a[i], i+1)) + else: + unit = unit * a[i].constant_coefficient() ** (i + 1) + + return Factorization(factors, unit=unit, sort=False) + def __pow__(self, n): r""" Returns the vector space of dimension `n` over ``self``. diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index 37d0e5f41d0..c693bbf7400 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -351,6 +351,108 @@ cdef class FiniteField(Field): else: return RingHomset(self, codomain, category) + def _squarefree_decomposition_univariate_polynomial(self, f): + """ + Return the square-free decomposition of this polynomial. This is a + partial factorization into square-free, coprime polynomials. + + This is a helper method for + :meth:`sage.rings.polynomial.squarefree_decomposition`. + + INPUT: + + - ``f`` -- a univariate non-zero polynomial over this field + + ALGORITHM; [Coh]_, algorithm 3.4.2 which is basically the algorithm in + [Yun]_ with special treatment for powers divisible by `p`. + + EXAMPLES:: + + sage: K. = GF(3^2) + sage: R. = K[] + sage: f = x^243+2*x^81+x^9+1 + sage: f.squarefree_decomposition() + (x^27 + 2*x^9 + x + 1)^9 + sage: f = x^243+a*x^27+1 + sage: f.squarefree_decomposition() + (x^9 + (2*a + 1)*x + 1)^27 + + TESTS:: + + sage: for K in [GF(2^18,'a'), GF(3^2,'a'), GF(47^3,'a')]: + ....: R. = K[] + ....: if K.characteristic() < 5: m = 4 + ....: else: m = 1 + ....: for _ in range(m): + ....: f = (R.random_element(4)^3*R.random_element(m)^(m+1))(x^6) + ....: F = f.squarefree_decomposition() + ....: assert F.prod() == f + ....: for i in range(len(F)): + ....: assert gcd(F[i][0], F[i][0].derivative()) == 1 + ....: for j in range(len(F)): + ....: if i == j: continue + ....: assert gcd(F[i][0], F[j][0]) == 1 + ....: + + REFERENCES: + + .. [Coh] H. Cohen, A Course in Computational Algebraic Number + Theory. Springer-Verlag, 1993. + + .. [Yun] Yun, David YY. On square-free decomposition algorithms. + In Proceedings of the third ACM symposium on Symbolic and algebraic + computation, pp. 26-35. ACM, 1976. + + """ + from sage.structure.factorization import Factorization + if f.degree() == 0: + return Factorization([], unit=f[0]) + + factors = [] + p = self.characteristic() + unit = f.leading_coefficient() + T0 = f.monic() + e = 1 + if T0.degree() > 0: + der = T0.derivative() + while der.is_zero(): + T0 = T0.parent()([T0[p*i].pth_root() for i in range(T0.degree()//p + 1)]) + if T0 == 1: + raise RuntimeError + der = T0.derivative() + e = e*p + T = T0.gcd(der) + V = T0 // T + k = 0 + while T0.degree() > 0: + k += 1 + if p.divides(k): + T = T // V + k += 1 + W = V.gcd(T) + if W.degree() < V.degree(): + factors.append((V // W, e*k)) + V = W + T = T // V + if V.degree() == 0: + if T.degree() == 0: + break + # T is of the form sum_{i=0}^n t_i X^{pi} + T0 = T0.parent()([T[p*i].pth_root() for i in range(T.degree()//p + 1)]) + der = T0.derivative() + e = p*e + while der.is_zero(): + T0 = T0.parent()([T0[p*i].pth_root() for i in range(T0.degree()//p + 1)]) + der = T0.derivative() + e = p*e + T = T0.gcd(der) + V = T0 // T + k = 0 + else: + T = T//V + + return Factorization(factors, unit=unit, sort=False) + def gen(self): r""" Return a generator of this field (over its prime field). As this is an diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index cda897efafa..920b86c4473 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -1212,15 +1212,8 @@ cdef class Polynomial(CommutativeAlgebraElement): def squarefree_decomposition(self): """ - Return the square-free decomposition of self. This is a - partial factorization of self into square-free, coprime - polynomials. - - ALGORITHM: In characteristic 0, we use Yun's algorithm, - which works for arbitrary rings of characteristic 0. - If the characteristic is a prime number `p > 0`, we use - [Coh]_, algorithm 3.4.2. This is basically Yun's algorithm - with special treatment for powers divisible by `p`. + Return the square-free decomposition of this polynomial. This is a + partial factorization into square-free, coprime polynomials. EXAMPLES:: @@ -1238,111 +1231,12 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: f.squarefree_decomposition() 1 - TESTS:: - - sage: K. = GF(3^2) - sage: R. = K[] - sage: f = x^243+2*x^81+x^9+1 - sage: f.squarefree_decomposition() - (x^27 + 2*x^9 + x + 1)^9 - sage: f = x^243+a*x^27+1 - sage: f.squarefree_decomposition() - (x^9 + (2*a + 1)*x + 1)^27 - - sage: for K in [GF(2^18,'a'), GF(3^2,'a'), GF(47^3,'a')]: - ....: R. = K[] - ....: if K.characteristic() < 5: m = 4 - ....: else: m = 1 - ....: for _ in range(m): - ....: f = (R.random_element(4)^3*R.random_element(m)^(m+1))(x^6) - ....: F = f.squarefree_decomposition() - ....: assert F.prod() == f - ....: for i in range(len(F)): - ....: assert gcd(F[i][0], F[i][0].derivative()) == 1 - ....: for j in range(len(F)): - ....: if i == j: continue - ....: assert gcd(F[i][0], F[j][0]) == 1 - ....: - - REFERENCES: - - .. [Coh] H. Cohen, A Course in Computational Algebraic Number - Theory. Springer-Verlag, 1993. """ - if not self.base_ring().is_unique_factorization_domain(): - raise NotImplementedError, "Squarefree decomposition not implemented for " + str(self.parent()) - - if self.degree() == 0: - return Factorization([], unit=self[0]) - - p = self.base_ring().characteristic() - factors = [] - if p == 0: - f = [self] - cur = self - while cur.degree() > 0: - cur = cur.gcd(cur.derivative()) - f.append(cur) - - g = [] - for i in range(len(f) - 1): - g.append(f[i] // f[i+1]) - - a = [] - for i in range(len(g) - 1): - a.append(g[i] // g[i+1]) - a.append(g[-1]) - - unit = f[-1] - for i in range(len(a)): - if a[i].degree() > 0: - factors.append((a[i], i+1)) - else: - unit = unit * a[i].constant_coefficient() ** (i + 1) - else: - # Beware that `p`-th roots might not exist. - unit = self.leading_coefficient() - T0 = self.monic() - e = 1 - if T0.degree() > 0: - der = T0.derivative() - while der.is_zero(): - T0 = T0.parent()([T0[p*i].pth_root() for i in range(T0.degree()//p + 1)]) - if T0 == 1: - raise RuntimeError - der = T0.derivative() - e = e*p - T = T0.gcd(der) - V = T0 // T - k = 0 - while T0.degree() > 0: - k += 1 - if p.divides(k): - T = T // V - k += 1 - W = V.gcd(T) - if W.degree() < V.degree(): - factors.append((V // W, e*k)) - V = W - T = T // V - if V.degree() == 0: - if T.degree() == 0: - break - # T is of the form sum_{i=0}^n t_i X^{pi} - T0 = T0.parent()([T[p*i].pth_root() for i in range(T.degree()//p + 1)]) - der = T0.derivative() - e = p*e - while der.is_zero(): - T0 = T0.parent()([T0[p*i].pth_root() for i in range(T0.degree()//p + 1)]) - der = T0.derivative() - e = p*e - T = T0.gcd(der) - V = T0 // T - k = 0 - else: - T = T//V - - return Factorization(factors, unit=unit, sort=False) + if self.degree() < 0: + raise ValueError("square-free decomposition not defined for zero polynomial") + if hasattr(self.base_ring(),'_squarefree_decomposition_univariate_polynomial'): + return self.base_ring()._squarefree_decomposition_univariate_polynomial(self) + raise NotImplementedError("square-free decomposition not implemented for this polynomial") def is_square(self, root=False): """ From b1f43af754d4ab7e136015519c7fc0cdbe32dd36 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Mon, 23 Jun 2014 12:47:59 +0200 Subject: [PATCH 313/546] trac #16504: A mandatory 'nonnegative' argument for MixedIntegerLinearProgram.new_variable() until the standard changes --- src/sage/graphs/digraph.py | 2 +- src/sage/graphs/generic_graph.py | 2 +- .../vertex_separation.pyx | 2 +- src/sage/numerical/backends/glpk_backend.pyx | 2 +- src/sage/numerical/linear_functions.pyx | 2 +- src/sage/numerical/mip.pxd | 2 + src/sage/numerical/mip.pyx | 373 +++++++++++------- 7 files changed, 248 insertions(+), 137 deletions(-) diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index e53944d8ef5..a8af4b03c65 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -1784,7 +1784,7 @@ def feedback_edge_set(self, constraint_generation= True, value_only=False, solve p=MixedIntegerLinearProgram(maximization=False, solver=solver) b=p.new_variable(binary = True) - d=p.new_variable(integer = True) + d=p.new_variable(integer = True, nonnegative=True) n=self.order() diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 08df666afef..38f51aa88e0 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -6962,7 +6962,7 @@ def feedback_vertex_set(self, value_only=False, solver=None, verbose=0, constrai p = MixedIntegerLinearProgram(maximization = False, solver = solver) b = p.new_variable(binary = True) - d = p.new_variable(integer = True) + d = p.new_variable(integer = True, nonnegative=True) n = self.order() # The removed vertices cover all the back arcs ( third condition ) diff --git a/src/sage/graphs/graph_decompositions/vertex_separation.pyx b/src/sage/graphs/graph_decompositions/vertex_separation.pyx index 2af7bf5ef60..9550c72c668 100644 --- a/src/sage/graphs/graph_decompositions/vertex_separation.pyx +++ b/src/sage/graphs/graph_decompositions/vertex_separation.pyx @@ -819,7 +819,7 @@ def vertex_separation_MILP(G, integrality = False, solver = None, verbosity = 0) x = p.new_variable(binary = integrality, nonnegative=bool(not integrality)) # at least one has to be set (#15221) u = p.new_variable(binary = integrality, nonnegative=bool(not integrality)) y = p.new_variable(binary = True) - z = p.new_variable(integer = True) + z = p.new_variable(integer = True, nonnegative=True) N = G.num_verts() V = G.vertices() diff --git a/src/sage/numerical/backends/glpk_backend.pyx b/src/sage/numerical/backends/glpk_backend.pyx index 49db772e41d..8743f5c902c 100644 --- a/src/sage/numerical/backends/glpk_backend.pyx +++ b/src/sage/numerical/backends/glpk_backend.pyx @@ -386,7 +386,7 @@ cdef class GLPKBackend(GenericBackend): sage: p = MixedIntegerLinearProgram(solver='GLPK') sage: x,y = p[0], p[1] - doctest:839: DeprecationWarning: The default behaviour of new_variable() will soon change ! It will return 'real' variables instead of nonnegative ones. Please be explicit and call new_variable(nonnegative=True) instead. + doctest:...: DeprecationWarning: The default value of 'nonnegative' will change, to False instead of True. You should add the explicit 'nonnegative=True'. See http://trac.sagemath.org/15521 for details. sage: p.add_constraint(2*x + 3*y, max = 6) sage: p.add_constraint(3*x + 2*y, max = 6) diff --git a/src/sage/numerical/linear_functions.pyx b/src/sage/numerical/linear_functions.pyx index 6c531146704..0c691d320b0 100644 --- a/src/sage/numerical/linear_functions.pyx +++ b/src/sage/numerical/linear_functions.pyx @@ -8,7 +8,7 @@ either equalities or less-or-equal. For example:: sage: p = MixedIntegerLinearProgram() sage: x = p.new_variable() - doctest:839: DeprecationWarning: The default behaviour of new_variable() will soon change ! It will return 'real' variables instead of nonnegative ones. Please be explicit and call new_variable(nonnegative=True) instead. + doctest:...: DeprecationWarning: The default value of 'nonnegative' will change, to False instead of True. You should add the explicit 'nonnegative=True'. See http://trac.sagemath.org/15521 for details. sage: f = 1 + x[1] + 2*x[2]; f # a linear function 1 + x_0 + 2*x_1 diff --git a/src/sage/numerical/mip.pxd b/src/sage/numerical/mip.pxd index 1e9b35915f1..6296d4e4624 100644 --- a/src/sage/numerical/mip.pxd +++ b/src/sage/numerical/mip.pxd @@ -32,4 +32,6 @@ cdef class MIPVariable(SageObject): cdef int _vtype cdef char * _name cdef bint _hasname + cdef object _lower_bound + cdef object _upper_bound diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index e2d8150de99..6e2afd824bf 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -53,7 +53,7 @@ A mixed integer linear program can give you an answer: The following example shows all these steps:: sage: p = MixedIntegerLinearProgram(maximization=False, solver = "GLPK") - sage: w = p.new_variable(integer=True) # all variables are non-negative by default + sage: w = p.new_variable(integer=True, nonnegative=True) sage: p.add_constraint(w[0] + w[1] + w[2] - 14*w[3] == 0) sage: p.add_constraint(w[1] + 2*w[2] - 8*w[3] == 0) sage: p.add_constraint(2*w[2] - 3*w[3] == 0) @@ -89,16 +89,14 @@ Different backends compute with different base fields, for example:: sage: p = MixedIntegerLinearProgram(solver = 'GLPK') sage: p.base_ring() Real Double Field - sage: x = p.new_variable() - doctest:839: DeprecationWarning: The default behaviour of new_variable() will soon change ! It will return 'real' variables instead of nonnegative ones. Please be explicit and call new_variable(nonnegative=True) instead. - See http://trac.sagemath.org/15521 for details. + sage: x = p.new_variable(real=True, nonnegative=True) sage: 0.5 + 3/2*x[1] 0.5 + 1.5*x_0 sage: p = MixedIntegerLinearProgram(solver = 'ppl') sage: p.base_ring() Rational Field - sage: x = p.new_variable() + sage: x = p.new_variable(nonnegative=True) sage: 0.5 + 3/2*x[1] 1/2 + 3/2*x_0 @@ -241,11 +239,10 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: g = graphs.PetersenGraph() sage: p = MixedIntegerLinearProgram(maximization=True) - sage: b = p.new_variable() + sage: b = p.new_variable(binary=True) sage: p.set_objective(sum([b[v] for v in g])) sage: for (u,v) in g.edges(labels=None): ... p.add_constraint(b[u] + b[v], max=1) - sage: p.set_binary(b) sage: p.solve(objective_only=True) 4.0 """ @@ -287,7 +284,7 @@ cdef class MixedIntegerLinearProgram(SageObject): - ``constraint_generation`` -- Only used when ``solver=None``. - - When set to ``True``, after solving the + - When set to ``True``, after solving the ``MixedIntegerLinearProgram``, it is possible to add a constraint, and then solve it again. The effect is that solvers that do not support this feature will not be used. @@ -337,6 +334,17 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: sum([1 for x in gc.get_objects() if isinstance(x,C)]) 0 sage: gc.enable() + + Right now the ``nonnegative`` argument is mandatory when a variable is + created, but later the default will change from nonnegative variables to + unbounded variables:: + + sage: p = MixedIntegerLinearProgram() + sage: v = p.new_variable(real=True) + doctest:839: DeprecationWarning: The default value of 'nonnegative' will change, to False instead of True. You should add the explicit 'nonnegative=True'. + See http://trac.sagemath.org/15521 for details. + sage: p.get_min(v[0]) + 0.0 """ self.__BINARY = 0 self.__REAL = -1 @@ -418,7 +426,7 @@ cdef class MixedIntegerLinearProgram(SageObject): EXAMPLE:: sage: p = MixedIntegerLinearProgram() - sage: v = p.new_variable() + sage: v = p.new_variable(nonnegative=True) sage: p.add_constraint(v[1] + v[2], max=2) sage: print p Mixed Integer Program ( maximization, 2 variables, 1 constraints ) @@ -441,11 +449,12 @@ cdef class MixedIntegerLinearProgram(SageObject): EXAMPLE:: - sage: p = MixedIntegerLinearProgram() - sage: p.add_constraint(p[0] + p[1], max = 10) - sage: q = copy(p) - sage: q.number_of_constraints() - 1 + sage: p = MixedIntegerLinearProgram() + sage: v = p.new_variable(nonnegative=True) + sage: p.add_constraint(v[0] + v[1], max = 10) + sage: q = copy(p) + sage: q.number_of_constraints() + 1 """ cdef MixedIntegerLinearProgram p = \ MixedIntegerLinearProgram(solver="GLPK") @@ -531,7 +540,7 @@ cdef class MixedIntegerLinearProgram(SageObject): """ self._backend.problem_name(name) - def new_variable(self, real=False, nonnegative=False, binary=False, integer=False, dim=1,name=""): + def new_variable(self, real=False, binary=False, integer=False, nonnegative=None, dim=1,name=""): r""" Returns an instance of ``MIPVariable`` associated to the current instance of ``MixedIntegerLinearProgram``. @@ -539,27 +548,24 @@ cdef class MixedIntegerLinearProgram(SageObject): A new variable ``x`` is defined by:: sage: p = MixedIntegerLinearProgram() - sage: x = p.new_variable() + sage: x = p.new_variable(nonnegative=True) It behaves exactly as an usual dictionary would. It can use any key argument you may like, as ``x[5]`` or ``x["b"]``, and has methods ``items()`` and ``keys()``. - .. WARNING:: - - By default, all ``x[i]`` are assumed to be non-negative. See - :meth:`set_min` to set a different lower bound. - INPUT: - ``dim`` (integer) -- Defines the dimension of the dictionary. If ``x`` has dimension `2`, its fields will be of the form ``x[key1][key2]``. Deprecated. - - ``binary, integer, nonnegative`` (boolean) -- Set one of these + - ``binary, integer, real`` (boolean) -- Set one of these arguments to ``True`` to ensure that the variable gets the - corresponding type. The default type is ``nonnegative``, which - represents nonnegative real variables. + corresponding type. + + - ``nonnegative`` (boolean) -- whether the variable should be assumed to + be nonnegative. Rather useless for the binary type. - ``name`` (string) -- Associates a name to the variable. This is only useful when exporting the linear program to a file using @@ -580,10 +586,8 @@ cdef class MixedIntegerLinearProgram(SageObject): To define two dictionaries of variables, the first being of real type, and the second of integer type :: - sage: x = p.new_variable(real=True) - doctest:839: DeprecationWarning: The meaning of 'real' will change, to represent real variables instead of nonnegative ones. Please use the new 'nonnegative' variable type. - See http://trac.sagemath.org/15521 for details. - sage: y = p.new_variable(integer=True) + sage: x = p.new_variable(real=True, nonnegative=True) + sage: y = p.new_variable(integer=True, nonnegative=True) sage: p.add_constraint(x[2] + y[3,5], max=2) sage: p.is_integer(x[2]) False @@ -601,32 +605,33 @@ cdef class MixedIntegerLinearProgram(SageObject): Default behaviour (:trac:`15521`):: - sage: x = p.new_variable() + sage: x = p.new_variable(nonnegative=True) sage: p.get_min(x[0]) 0.0 """ - if sum([real, binary, integer]) >= 2: + if sum([real, binary, integer]) > 1: raise ValueError("Exactly one of the available types has to be True") + if nonnegative is None and not binary: + deprecation(15521, "The default value of 'nonnegative' will change, to "+ + "False instead of True. You should add the explicit "+ + "'nonnegative=True'.") + nonnegative=True + if binary: vtype = self.__BINARY elif integer: vtype = self.__INTEGER - elif real: - deprecation(15521, "The meaning of 'real' will change, to "+ - "represent real variables instead of nonnegative "+ - "ones. Please use the new 'nonnegative' variable type.") - vtype = self.__REAL - elif nonnegative: - vtype = self.__REAL else: - deprecation(15521, "The default behaviour of new_variable() will "+ - "soon change ! It will return 'real' variables instead "+ - "of nonnegative ones. Please be explicit and call "+ - "new_variable(nonnegative=True) instead.") vtype = self.__REAL - v=MIPVariable(self, vtype, dim=dim,name=name) + v=MIPVariable(self, + vtype, + dim=dim, + name=name, + lower_bound=0 if (nonnegative or binary) else None, + upper_bound=1 if binary else None) + return v cpdef int number_of_constraints(self): @@ -942,7 +947,7 @@ cdef class MixedIntegerLinearProgram(SageObject): Without any names :: sage: p = MixedIntegerLinearProgram(solver = "GLPK") - sage: x = p.new_variable() + sage: x = p.new_variable(nonnegative=True) sage: p.set_objective(x[1] + x[2]) sage: p.add_constraint(-3*x[1] + 2*x[2], max=2) sage: p.show() @@ -957,7 +962,7 @@ cdef class MixedIntegerLinearProgram(SageObject): With `\QQ` coefficients:: sage: p = MixedIntegerLinearProgram(solver= 'ppl') - sage: x = p.new_variable() + sage: x = p.new_variable(nonnegative=True) sage: p.set_objective(x[1] + 1/2*x[2]) sage: p.add_constraint(-3/5*x[1] + 2/7*x[2], max=2/5) sage: p.show() @@ -1081,7 +1086,7 @@ cdef class MixedIntegerLinearProgram(SageObject): EXAMPLE:: sage: p = MixedIntegerLinearProgram(solver="GLPK") - sage: x = p.new_variable() + sage: x = p.new_variable(nonnegative=True) sage: p.set_objective(x[1] + x[2]) sage: p.add_constraint(-3*x[1] + 2*x[2], max=2,name="OneConstraint") sage: p.write_mps(os.path.join(SAGE_TMP, "lp_problem.mps")) @@ -1107,7 +1112,7 @@ cdef class MixedIntegerLinearProgram(SageObject): EXAMPLE:: sage: p = MixedIntegerLinearProgram(solver="GLPK") - sage: x = p.new_variable() + sage: x = p.new_variable(nonnegative=True) sage: p.set_objective(x[1] + x[2]) sage: p.add_constraint(-3*x[1] + 2*x[2], max=2) sage: p.write_lp(os.path.join(SAGE_TMP, "lp_problem.lp")) @@ -1145,8 +1150,8 @@ cdef class MixedIntegerLinearProgram(SageObject): EXAMPLE:: sage: p = MixedIntegerLinearProgram() - sage: x = p.new_variable() - sage: y = p.new_variable() + sage: x = p.new_variable(nonnegative=True) + sage: y = p.new_variable(nonnegative=True) sage: p.set_objective(x[3] + 3*y[2,9] + x[5]) sage: p.add_constraint(x[3] + y[2,9] + 2*x[5], max=2) sage: p.solve() @@ -1245,7 +1250,7 @@ cdef class MixedIntegerLinearProgram(SageObject): This linear program can be solved as follows:: sage: p = MixedIntegerLinearProgram(maximization=True) - sage: x = p.new_variable() + sage: x = p.new_variable(nonnegative=True) sage: p.set_objective(x[1] + 5*x[2]) sage: p.add_constraint(x[1] + 2/10*x[2], max=4) sage: p.add_constraint(1.5*x[1]+3*x[2], max=4) @@ -1312,7 +1317,7 @@ cdef class MixedIntegerLinearProgram(SageObject): It can be solved as follows:: sage: p = MixedIntegerLinearProgram(maximization=True) - sage: x = p.new_variable() + sage: x = p.new_variable(nonnegative=True) sage: p.set_objective(x[1] + 5*x[2]) sage: p.add_constraint(x[1] + 0.2*x[2], max=4) sage: p.add_constraint(1.5*x[1] + 3*x[2], max=4) @@ -1342,7 +1347,7 @@ cdef class MixedIntegerLinearProgram(SageObject): The previous program can be rewritten:: sage: p = MixedIntegerLinearProgram(maximization=True) - sage: x = p.new_variable() + sage: x = p.new_variable(nonnegative=True) sage: p.set_objective(x[1] + 5*x[2]) sage: p.add_constraint(x[1] + 0.2*x[2] <= 4) sage: p.add_constraint(1.5*x[1] + 3*x[2] <= 4) @@ -1354,7 +1359,7 @@ cdef class MixedIntegerLinearProgram(SageObject): Complex constraints:: sage: p = MixedIntegerLinearProgram(solver = "GLPK") - sage: b = p.new_variable() + sage: b = p.new_variable(nonnegative=True) sage: p.add_constraint( b[8] - b[15] <= 3*b[8] + 9) sage: p.show() Maximization: @@ -1372,7 +1377,7 @@ cdef class MixedIntegerLinearProgram(SageObject): Min/Max are numerical :: - sage: v = p.new_variable() + sage: v = p.new_variable(nonnegative=True) sage: p.add_constraint(v[3] + v[5], min = v[6]) Traceback (most recent call last): ... @@ -1425,7 +1430,7 @@ cdef class MixedIntegerLinearProgram(SageObject): Catch ``True`` / ``False`` as INPUT (:trac:`13646`):: sage: p = MixedIntegerLinearProgram() - sage: x = p.new_variable() + sage: x = p.new_variable(nonnegative=True) sage: p.add_constraint(True) Traceback (most recent call last): ... @@ -1600,7 +1605,7 @@ cdef class MixedIntegerLinearProgram(SageObject): EXAMPLE:: sage: p = MixedIntegerLinearProgram() - sage: x = p.new_variable() + sage: x = p.new_variable(nonnegative=True) With the following instruction, all the variables from x will be binary:: @@ -1610,9 +1615,9 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p.add_constraint(-3*x[0] + 2*x[1], max=2) It is still possible, though, to set one of these - variables as nonnegative while keeping the others as they are:: + variables as integer while keeping the others as they are:: - sage: p.set_nonnegative(x[3]) + sage: p.set_integer(x[3]) TESTS: @@ -1656,7 +1661,7 @@ cdef class MixedIntegerLinearProgram(SageObject): EXAMPLE:: sage: p = MixedIntegerLinearProgram() - sage: v = p.new_variable() + sage: v = p.new_variable(nonnegative=True) sage: p.set_objective(v[1]) sage: p.is_binary(v[1]) False @@ -1679,7 +1684,7 @@ cdef class MixedIntegerLinearProgram(SageObject): EXAMPLE:: sage: p = MixedIntegerLinearProgram() - sage: x = p.new_variable() + sage: x = p.new_variable(nonnegative=True) With the following instruction, all the variables from x will be integers:: @@ -1689,9 +1694,9 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p.add_constraint(-3*x[0] + 2*x[1], max=2) It is still possible, though, to set one of these - variables as nonnegative while keeping the others as they are:: + variables as binary while keeping the others as they are:: - sage: p.set_nonnegative(x[3]) + sage: p.set_binary(x[3]) """ cdef MIPVariable e e = ee @@ -1725,7 +1730,7 @@ cdef class MixedIntegerLinearProgram(SageObject): EXAMPLE:: sage: p = MixedIntegerLinearProgram() - sage: v = p.new_variable() + sage: v = p.new_variable(nonnegative=True) sage: p.set_objective(v[1]) sage: p.is_integer(v[1]) False @@ -1735,10 +1740,9 @@ cdef class MixedIntegerLinearProgram(SageObject): """ return self._backend.is_variable_integer(self._variables[e]) - set_real = deprecated_function_alias(15521, set_nonnegative) - def set_nonnegative(self,ee): + def set_real(self,ee): r""" - Sets a variable or a ``MIPVariable`` as nonnegative. + Sets a variable or a ``MIPVariable`` as real. INPUT: @@ -1748,12 +1752,12 @@ cdef class MixedIntegerLinearProgram(SageObject): EXAMPLE:: sage: p = MixedIntegerLinearProgram() - sage: x = p.new_variable() + sage: x = p.new_variable(nonnegative=True) With the following instruction, all the variables - from x will be nonnegative:: + from x will be real:: - sage: p.set_nonnegative(x) + sage: p.set_real(x) sage: p.set_objective(x[0] + x[1]) sage: p.add_constraint(-3*x[0] + 2*x[1], max=2) @@ -1762,15 +1766,7 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p.set_binary(x[3]) - TESTS: - - :trac:`15521`:: - - sage: p.set_real(x[3]) - doctest:1: DeprecationWarning: set_real is deprecated. Please use set_nonnegative instead. - See http://trac.sagemath.org/15521 for details. """ - cdef MIPVariable e e = ee @@ -1789,10 +1785,9 @@ cdef class MixedIntegerLinearProgram(SageObject): else: raise ValueError("e must be an instance of MIPVariable or one of its elements.") - is_real = deprecated_function_alias(15521,is_nonnegative) - def is_nonnegative(self, e): + def is_real(self, e): r""" - Tests whether the variable is nonnegative. + Tests whether the variable is real. INPUT: @@ -1800,24 +1795,23 @@ cdef class MixedIntegerLinearProgram(SageObject): OUTPUT: - ``True`` if the variable is nonnegative; ``False`` otherwise. + ``True`` if the variable is real; ``False`` otherwise. EXAMPLE:: sage: p = MixedIntegerLinearProgram() - sage: v = p.new_variable() + sage: v = p.new_variable(nonnegative=True) sage: p.set_objective(v[1]) - sage: p.is_nonnegative(v[1]) + sage: p.is_real(v[1]) True sage: p.set_binary(v[1]) - sage: p.is_nonnegative(v[1]) + sage: p.is_real(v[1]) False - sage: p.set_nonnegative(v[1]) - sage: p.is_nonnegative(v[1]) + sage: p.set_real(v[1]) + sage: p.is_real(v[1]) True """ - return (self._backend.is_variable_continuous(self._variables[e]) and - self._backend.variable_lower_bound(self._variables[e]) == 0) + return self._backend.is_variable_continuous(self._variables[e]) def solve(self, log=None, objective_only=False): r""" @@ -1860,7 +1854,7 @@ cdef class MixedIntegerLinearProgram(SageObject): This linear program can be solved as follows:: sage: p = MixedIntegerLinearProgram(maximization=True) - sage: x = p.new_variable() + sage: x = p.new_variable(nonnegative=True) sage: p.set_objective(x[1] + 5*x[2]) sage: p.add_constraint(x[1] + 0.2*x[2], max=4) sage: p.add_constraint(1.5*x[1] + 3*x[2], max=4) @@ -1876,7 +1870,7 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: g = graphs.PetersenGraph() sage: p = MixedIntegerLinearProgram(maximization=True) - sage: b = p.new_variable() + sage: b = p.new_variable(nonnegative=True) sage: p.set_objective(sum([b[v] for v in g])) sage: for (u,v) in g.edges(labels=None): ... p.add_constraint(b[u] + b[v], max=1) @@ -1905,16 +1899,12 @@ cdef class MixedIntegerLinearProgram(SageObject): r""" Sets the minimum value of a variable. - .. WARNING:: - - By default, all variables are defined to be non-negative. - INPUT: - - ``v`` -- a variable (not a ``MIPVariable``, but one of its - elements). - - ``min`` -- the minimum value the variable can take. - When ``min=None``, the variable has no lower bound. + - ``v`` -- a variable. + + - ``min`` -- the minimum value the variable can take. When + ``min=None``, the variable has no lower bound. .. SEEALSO:: @@ -1923,7 +1913,7 @@ cdef class MixedIntegerLinearProgram(SageObject): EXAMPLE:: sage: p = MixedIntegerLinearProgram() - sage: v = p.new_variable() + sage: v = p.new_variable(nonnegative=True) sage: p.set_objective(v[1]) sage: p.get_min(v[1]) 0.0 @@ -1933,8 +1923,21 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p.set_min(v[1], None) sage: p.get_min(v[1]) + With a :class:`MIPVariable` as an argument:: + + sage: vv = p.new_variable(real=True,nonnegative=False) + sage: p.get_min(vv) + sage: p.get_min(vv[0]) + sage: p.set_min(vv,5) + sage: p.get_min(vv[0]) + 5.0 + sage: p.get_min(vv[9]) + 5.0 """ - self._backend.variable_lower_bound(self._variables[v], min) + if isinstance(v, MIPVariable): + v.set_min(min) + else: + self._backend.variable_lower_bound(self._variables[v], min) def set_max(self, v, max): r""" @@ -1942,22 +1945,36 @@ cdef class MixedIntegerLinearProgram(SageObject): INPUT - - ``v`` -- a variable (not a ``MIPVariable``, but one of its - elements). - - ``max`` -- the maximum value the variable can take. - When ``max=None``, the variable has no upper bound. + - ``v`` -- a variable. + + - ``max`` -- the maximum value the variable can take. When + ``max=None``, the variable has no upper bound. EXAMPLE:: sage: p = MixedIntegerLinearProgram() - sage: v = p.new_variable() + sage: v = p.new_variable(nonnegative=True) sage: p.set_objective(v[1]) sage: p.get_max(v[1]) sage: p.set_max(v[1],6) sage: p.get_max(v[1]) 6.0 + + With a :class:`MIPVariable` as an argument:: + + sage: vv = p.new_variable(real=True,nonnegative=False) + sage: p.get_max(vv) + sage: p.get_max(vv[0]) + sage: p.set_max(vv,5) + sage: p.get_max(vv[0]) + 5.0 + sage: p.get_max(vv[9]) + 5.0 """ - self._backend.variable_upper_bound(self._variables[v], max) + if isinstance(v, MIPVariable): + v.set_max(max) + else: + self._backend.variable_upper_bound(self._variables[v], max) def get_min(self, v): r""" @@ -1965,17 +1982,17 @@ cdef class MixedIntegerLinearProgram(SageObject): INPUT: - - ``v`` -- a variable (not a ``MIPVariable``, but one of its elements). + - ``v`` -- a variable OUTPUT: - Minimum value of the variable, or ``None`` if - the variable has no lower bound. + Minimum value of the variable, or ``None`` if the variable has no lower + bound. EXAMPLE:: sage: p = MixedIntegerLinearProgram() - sage: v = p.new_variable() + sage: v = p.new_variable(nonnegative=True) sage: p.set_objective(v[1]) sage: p.get_min(v[1]) 0.0 @@ -1985,7 +2002,10 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p.set_min(v[1], None) sage: p.get_min(v[1]) """ - return self._backend.variable_lower_bound(self._variables[v]) + if isinstance(v, MIPVariable): + return ( v)._lower_bound + else: + return self._backend.variable_lower_bound(self._variables[v]) def get_max(self, v): r""" @@ -1993,24 +2013,27 @@ cdef class MixedIntegerLinearProgram(SageObject): INPUT: - - ``v`` -- a variable (not a ``MIPVariable``, but one of its elements). + - ``v`` -- a variable. OUTPUT: - Maximum value of the variable, or ``None`` if - the variable has no upper bound. + Maximum value of the variable, or ``None`` if the variable has no upper + bound. EXAMPLE:: sage: p = MixedIntegerLinearProgram() - sage: v = p.new_variable() + sage: v = p.new_variable(nonnegative=True) sage: p.set_objective(v[1]) sage: p.get_max(v[1]) sage: p.set_max(v[1],6) sage: p.get_max(v[1]) 6.0 """ - return self._backend.variable_upper_bound(self._variables[v]) + if isinstance(v, MIPVariable): + return ( v)._upper_bound + else: + return self._backend.variable_upper_bound(self._variables[v]) def solver_parameter(self, name, value = None): """ @@ -2098,7 +2121,7 @@ cdef class MixedIntegerLinearProgram(SageObject): EXAMPLES:: sage: p = MixedIntegerLinearProgram() - sage: v = p.new_variable() + sage: v = p.new_variable(nonnegative=True) The following command:: @@ -2165,7 +2188,7 @@ class MIPSolverException(RuntimeError): No continuous solution:: sage: p=MixedIntegerLinearProgram(solver="GLPK") - sage: v=p.new_variable() + sage: v=p.new_variable(nonnegative=True) sage: p.add_constraint(v[0],max=5.5) sage: p.add_constraint(v[0],min=7.6) sage: p.set_objective(v[0]) @@ -2180,7 +2203,7 @@ class MIPSolverException(RuntimeError): No integer solution:: sage: p=MixedIntegerLinearProgram(solver="GLPK") - sage: v=p.new_variable() + sage: v=p.new_variable(nonnegative=True) sage: p.add_constraint(v[0],max=5.6) sage: p.add_constraint(v[0],min=5.2) sage: p.set_objective(v[0]) @@ -2214,7 +2237,7 @@ cdef class MIPVariable(SageObject): ``MixedIntegerLinearProgram``. """ - def __cinit__(self, p, vtype, dim=1, name=""): + def __cinit__(self, p, vtype, dim=1, name="", lower_bound=0, upper_bound=None): r""" Constructor for ``MIPVariable``. @@ -2230,18 +2253,23 @@ cdef class MIPVariable(SageObject): - ``name`` -- A name for the ``MIPVariable``. + - ``lower_bound, upper_bound`` -- lower bound and upper bound on the + variable. Set to ``None`` to indicate that the variable is unbounded. + For more informations, see the method ``MixedIntegerLinearProgram.new_variable``. EXAMPLE:: sage: p=MixedIntegerLinearProgram() - sage: v=p.new_variable() + sage: v=p.new_variable(nonnegative=True) """ self._dim = dim self._dict = {} self._p = p self._vtype = vtype + self._lower_bound = lower_bound + self._upper_bound = upper_bound self._hasname = (len(name) >0) @@ -2271,10 +2299,17 @@ cdef class MIPVariable(SageObject): EXAMPLE:: sage: p = MixedIntegerLinearProgram() - sage: v = p.new_variable() + sage: v = p.new_variable(nonnegative=True) sage: p.set_objective(v[0] + v[1]) sage: v[0] x_0 + + TESTS: + + This function contains "dim" code that will have to be removed:: + + sage: p = MixedIntegerLinearProgram() + sage: b = p.new_variable(binary=True, dim=2) """ cdef MIPVariable s = self @@ -2284,9 +2319,14 @@ cdef class MIPVariable(SageObject): return self._dict[i] elif self._dim == 1: zero = self._p._backend.zero() - j = self._p._backend.add_variable(zero , None, False, True, False, zero, - (str(self._name) + "[" + str(i) + "]") - if self._hasname else None) + j = self._p._backend.add_variable(lower_bound = self._lower_bound, + upper_bound = self._upper_bound, + binary = False, + continuous = True, + integer = False, + obj = zero, + name = ((str(self._name) + "[" + str(i) + "]") + if self._hasname else None)) v = self._p.linear_function({j : 1}) self._p._variables[v] = j @@ -2299,12 +2339,81 @@ cdef class MIPVariable(SageObject): self._dict[i] = MIPVariable( self._p, self._vtype, - dim=self._dim-1, - name = ("" if not self._hasname - else (str(self._name) + "[" + str(i) + "]"))) + dim = self._dim-1, + name = ("" if not self._hasname + else (str(self._name) + "[" + str(i) + "]")), + lower_bound = self._lower_bound, + upper_bound = self._upper_bound) return self._dict[i] + def set_min(self, min): + r""" + Sets a lower bound on the variable. + + INPUT: + + - ``min`` -- a lower bound, or ``None`` to mean that the variable is + unbounded. + + EXAMPLES:: + + sage: p = MixedIntegerLinearProgram() + sage: v = p.new_variable(real=True,nonnegative=True,dim=2) + sage: p.get_min(v) + 0 + sage: p.get_min(v[0]) + 0 + sage: p.get_min(v[0][0]) + 0.0 + sage: p.set_min(v,4) + sage: p.get_min(v) + 4 + sage: p.get_min(v[0]) + 4 + sage: p.get_min(v[0][0]) + 4.0 + """ + self._lower_bound = min + if self._dim == 1: + for v in self._p._variables: + self._p.set_min(v,min) + else: + for v in self._dict.itervalues(): + v.set_min(min) + + def set_max(self, max): + r""" + Sets an upper bound on the variable. + + INPUT: + + - ``max`` -- an upper bound, or ``None`` to mean that the variable is + unbounded. + + EXAMPLES:: + + sage: p = MixedIntegerLinearProgram() + sage: v = p.new_variable(real=True,nonnegative=True,dim=2) + sage: p.get_max(v) + sage: p.get_max(v[0]) + sage: p.get_max(v[0][0]) + sage: p.set_max(v,4) + sage: p.get_max(v) + 4 + sage: p.get_max(v[0]) + 4 + sage: p.get_max(v[0][0]) + 4.0 + """ + self._upper_bound = max + if self._dim == 1: + for v in self._p._variables: + self._p.set_max(v,max) + else: + for v in self._dict.itervalues(): + v.set_max(max) + def _repr_(self): r""" Returns a representation of self. @@ -2329,7 +2438,7 @@ cdef class MIPVariable(SageObject): EXAMPLE:: sage: p = MixedIntegerLinearProgram() - sage: v = p.new_variable() + sage: v = p.new_variable(nonnegative=True) sage: p.set_objective(v[0] + v[1]) sage: v.keys() [0, 1] @@ -2343,7 +2452,7 @@ cdef class MIPVariable(SageObject): EXAMPLE:: sage: p = MixedIntegerLinearProgram() - sage: v = p.new_variable() + sage: v = p.new_variable(nonnegative=True) sage: p.set_objective(v[0] + v[1]) sage: v.items() [(0, x_0), (1, x_1)] @@ -2357,7 +2466,7 @@ cdef class MIPVariable(SageObject): EXAMPLE:: sage: p = MixedIntegerLinearProgram() - sage: v = p.new_variable() + sage: v = p.new_variable(nonnegative=True) sage: p.set_objective(v[0] + v[1]) sage: v.depth() 1 @@ -2371,7 +2480,7 @@ cdef class MIPVariable(SageObject): EXAMPLE:: sage: p = MixedIntegerLinearProgram() - sage: v = p.new_variable() + sage: v = p.new_variable(nonnegative=True) sage: p.set_objective(v[0] + v[1]) sage: v.values() [x_0, x_1] @@ -2390,7 +2499,7 @@ def Sum(x): See http://trac.sagemath.org/13646 for details. sage: p = MixedIntegerLinearProgram() - sage: x = p.new_variable() + sage: x = p.new_variable(nonnegative=True) sage: Sum([ x[0]+x[1], x[1]+x[2], x[2]+x[3] ]) # deprecation is only shown once x_0 + 2*x_1 + 2*x_2 + x_3 """ From e9ac71d0d8bd2334b7f13ddf7d974deed35906b1 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Mon, 23 Jun 2014 16:23:59 +0200 Subject: [PATCH 314/546] trac #16504: Tastes and colors ................ --- src/sage/numerical/mip.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 6e2afd824bf..b2833a2e20b 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -1934,9 +1934,9 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p.get_min(vv[9]) 5.0 """ - if isinstance(v, MIPVariable): + try: v.set_min(min) - else: + except AttributeError: self._backend.variable_lower_bound(self._variables[v], min) def set_max(self, v, max): @@ -1971,9 +1971,9 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p.get_max(vv[9]) 5.0 """ - if isinstance(v, MIPVariable): + try: v.set_max(max) - else: + except AttributeError: self._backend.variable_upper_bound(self._variables[v], max) def get_min(self, v): From d437be4a258a648e86f8cc1845ade75467609601 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Mon, 23 Jun 2014 16:28:26 +0200 Subject: [PATCH 315/546] trac #16504: Broken doctest --- src/sage/geometry/cone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 60586481f2b..a7b3ae7322a 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -3853,7 +3853,7 @@ def Hilbert_coefficients(self, point): from sage.numerical.mip import MixedIntegerLinearProgram p = MixedIntegerLinearProgram(maximization=False) p.set_objective(None) - x = p.new_variable(integer=True) + x = p.new_variable(integer=True, nonnegative=True) x = [ x[i] for i in range(0,len(basis)) ] for i in range(0,self.lattice_dim()): p.add_constraint(sum(b[i]*x[j] for j,b in enumerate(basis)) == point[i]) From 81b9448a730769e28af52c3ea36faa5b8156232a Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Mon, 23 Jun 2014 16:59:09 +0100 Subject: [PATCH 316/546] trac #16430: put back the seealso --- src/sage/combinat/designs/orthogonal_arrays.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index d30cef9c37e..0dbfd9e3860 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -95,6 +95,7 @@ def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): .. SEEALSO:: + :func:`orthogonal_array` -- a tranversal design `TD(k,n)` is equivalent to an orthogonal array `OA(k,n,2)`. EXAMPLES:: From b437f3539dbda9bdb16c400d406925f3f2a20293 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Mon, 23 Jun 2014 18:51:47 +0100 Subject: [PATCH 317/546] Trac 10665: reset mwrank_EllipticCurve.__saturate when creating new 2-descent data --- src/sage/libs/mwrank/interface.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/sage/libs/mwrank/interface.py b/src/sage/libs/mwrank/interface.py index 56895acbd34..8fc1be05518 100644 --- a/src/sage/libs/mwrank/interface.py +++ b/src/sage/libs/mwrank/interface.py @@ -370,7 +370,9 @@ def two_descent(self, Nothing -- nothing is returned. - TESTS (see #7992):: + TESTS: + + See :trac:`7992`:: sage: EllipticCurve([0, prod(prime_range(10))]).mwrank_curve().two_descent() Basic pair: I=0, J=-5670 @@ -405,6 +407,14 @@ def two_descent(self, ... RuntimeError: Aborted + Calling this method twice does not cause a segmentation fault + (see :trac:`10665`):: + + sage: E = EllipticCurve([1, 1, 0, 0, 528]) + sage: E.two_descent(verbose=False) + True + sage: E.two_descent(verbose=False) + True """ from sage.libs.mwrank.mwrank import _two_descent # import here to save time @@ -422,8 +432,9 @@ def two_descent(self, second_limit, n_aux, second_descent) - if not self.__two_descent_data().ok(): + if not self.__descent.ok(): raise RuntimeError("A 2-descent did not complete successfully.") + self.__saturate = -2 # not yet saturated def __two_descent_data(self): r""" From 8d29348caeb62bc330638494de537c61fa044edd Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Mon, 23 Jun 2014 21:16:08 +0200 Subject: [PATCH 318/546] Correct TeX errors (raw string vs. string) in docstring (Gray Code) --- src/sage/combinat/finite_state_machine.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index ccbfb9299cf..a60ef4971e8 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -268,7 +268,7 @@ where two successive values differ in only one bit, cf. the :wikipedia:`Gray_code`. The Gray code of an integer `n` is obtained by a bitwise xor between the binary expansion of `n` and the binary -expansion of `\\lfloor n/2\\rfloor`; the latter corresponds to a +expansion of `\lfloor n/2\rfloor`; the latter corresponds to a shift by one position in binary. The purpose of this example is to construct a transducer converting the @@ -279,7 +279,7 @@ the left-most position. Note that it is easier to shift everything to the right first, i.e., multiply by `2` instead of building -`\\lfloor n/2\\rfloor`. Then, we take the input xor with the right +`\lfloor n/2\rfloor`. Then, we take the input xor with the right shift of the input and forget the first letter. We first construct a transducer shifting the binary expansion to the From ae8ec6b18e2a26484393c752b30a117db2b858c6 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Mon, 23 Jun 2014 21:17:25 +0200 Subject: [PATCH 319/546] Modified a few docstrings to reflect use of final output word --- src/sage/combinat/finite_state_machine.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index a60ef4971e8..4dd81e3d81a 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -374,6 +374,13 @@ sage: product_transducer = shift_right_transducer.cartesian_product(transducers.Identity([0, 1])) sage: sage.combinat.finite_state_machine.FSMOldCodeTransducerCartesianProduct = True sage: Gray_transducer = xor_transducer(product_transducer) + +We use :meth:`~FiniteStateMachine.construct_final_word_out` to make sure that all output +is written; otherwise, we would have to make sure that a sufficient number of trailing +zeros is read. + +:: + sage: Gray_transducer.construct_final_word_out([0]) sage: Gray_transducer.transitions() [Transition from (('I', 0), 0) to ((0, 0), 0): 0|-, @@ -402,9 +409,7 @@ True Finally, we check that this indeed computes the Gray code of the first -10 non-negative integers. Note that we add a trailing zero at the most -significant position of the input in order to flush all output digits. -This is due to the left shift which delays its output. +10 non-negative integers. :: @@ -6955,7 +6960,9 @@ def asymptotic_moments(self, variable=SR.symbol('n')): following agrees with the results in [HP2007]_. We first use the transducer to convert the standard binary - expansion to the NAF given in [HP2007]_:: + expansion to the NAF given in [HP2007]_. We use the parameter + ``with_final_word_out`` such that we do not have to add + sufficiently many trailing zeros:: sage: NAF = Transducer([(0, 0, 0, 0), ....: (0, '.1', 1, None), @@ -6968,16 +6975,14 @@ def asymptotic_moments(self, variable=SR.symbol('n')): ....: with_final_word_out=[0]) As an example, we compute the NAF of `27` by this - transducer. Note that we have to add two trailing (at the - most significant positions) digits `0` in order to be sure - to reach the final state. + transducer. :: sage: binary_27 = 27.bits() sage: binary_27 [1, 1, 0, 1, 1] - sage: NAF_27 = NAF(binary_27+[0, 0]) + sage: NAF_27 = NAF(binary_27) sage: NAF_27 [-1, 0, -1, 0, 0, 1, 0] sage: ZZ(NAF_27, base=2) From 492754259b2f7000b45da9d3ab8f610763421181 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Mon, 23 Jun 2014 21:18:23 +0200 Subject: [PATCH 320/546] removed determine_alphabets=True from docstrings because it is the default anyway --- src/sage/combinat/finite_state_machine.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 4dd81e3d81a..85870590763 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -8090,15 +8090,13 @@ def intersection(self, other, only_accessible_components=True): ....: ('2', '2', 1, 0), ....: ('2', '2', 0, 1)], ....: initial_states=['1'], - ....: final_states=['2'], - ....: determine_alphabets=True) + ....: final_states=['2']) sage: transducer2 = Transducer([('A', 'A', 1, 0), ....: ('A', 'B', 0, 0), ....: ('B', 'B', 0, 1), ....: ('B', 'A', 1, 1)], ....: initial_states=['A'], - ....: final_states=['B'], - ....: determine_alphabets=True) + ....: final_states=['B']) sage: res = transducer1.intersection(transducer2) sage: res.transitions() [Transition from ('1', 'A') to ('2', 'A'): 1|0, @@ -8118,14 +8116,12 @@ def intersection(self, other, only_accessible_components=True): ....: (0, 1, None, 'c'), ....: (1, 1, None, 'c')], ....: initial_states=[0], - ....: final_states=[0, 1], - ....: determine_alphabets=True) + ....: final_states=[0, 1]) sage: t2 = Transducer([('A', 'A', None, 'b'), ....: ('A', 'B', 'a', 'c'), ....: ('B', 'B', 'a', 'c')], ....: initial_states=['A'], - ....: final_states=['A', 'B'], - ....: determine_alphabets=True) + ....: final_states=['A', 'B']) sage: t2.intersection(t1) Traceback (most recent call last): ... @@ -8136,12 +8132,10 @@ def intersection(self, other, only_accessible_components=True): sage: transducer1 = Transducer([('1', '2', 1, 0)], ....: initial_states=['1'], - ....: final_states=['2'], - ....: determine_alphabets=True) + ....: final_states=['2']) sage: transducer2 = Transducer([('A', 'B', 1, 0)], ....: initial_states=['A'], - ....: final_states=['B'], - ....: determine_alphabets=True) + ....: final_states=['B']) sage: res = transducer1.intersection(transducer2) sage: res.final_states() [('2', 'B')] From ba07d56515eecea78d1d09f7f4f11e04146e1494 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Mon, 23 Jun 2014 21:18:50 +0200 Subject: [PATCH 321/546] Use iterators instead of lists --- src/sage/combinat/finite_state_machine.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 85870590763..9ea1af2c5e7 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -5769,25 +5769,25 @@ def transposition(self): """ transposition = self.empty_copy() - for state in self.states(): + for state in self.iter_states(): transposition.add_state(deepcopy(state)) - for transition in self.transitions(): + for transition in self.iter_transitions(): transposition.add_transition( transition.to_state.label(), transition.from_state.label(), transition.word_in, transition.word_out) - for initial in self.initial_states(): + for initial in self.iter_initial_states(): state = transposition.state(initial.label()) if not initial.is_final: state.is_final = True state.is_initial = False - for final in self.final_states(): + for final in self.iter_final_states(): state = transposition.state(final.label()) if final.final_word_out: - raise NotImplementedError("Transposition for transducers " \ - "with final output words is not " \ + raise NotImplementedError("Transposition for transducers " + "with final output words is not " "implemented.") if not final.is_initial: state.is_final = False @@ -7288,7 +7288,7 @@ def asymptotic_moments(self, variable=SR.symbol('n')): if len(self.initial_states()) != 1: raise ValueError("A unique initial state is required.") - if len(self.final_states()) != len(self.states()): + if not all(state.is_final for state in self.iter_states()): raise ValueError("Not all states are final.") if not self.is_complete(): From 0be171e624bb02e87f203dec9026a02903987644 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Mon, 23 Jun 2014 21:28:23 +0200 Subject: [PATCH 322/546] Added examples for FiniteStateMachine.equivalence_classes --- src/sage/combinat/finite_state_machine.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 9ea1af2c5e7..6e4251f2787 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -6109,13 +6109,20 @@ def equivalence_classes(self): ....: ("B", "C", 0, 0), ("B", "C", 1, 1), ....: ("C", "D", 0, 1), ("C", "D", 1, 0), ....: ("D", "A", 0, 0), ("D", "A", 1, 1)]) - sage: fsm.equivalence_classes() + sage: sorted(fsm.equivalence_classes()) [['A', 'C'], ['B', 'D']] sage: fsm.state("A").is_final = True + sage: sorted(fsm.equivalence_classes()) + [['A'], ['B'], ['C'], ['D']] + sage: fsm.state("C").is_final = True + sage: sorted(fsm.equivalence_classes()) + [['A', 'C'], ['B', 'D']] sage: fsm.state("A").final_word_out = 1 - sage: fsm.equivalence_classes() - [['A'], ['B'], ['D'], ['C']] - + sage: sorted(fsm.equivalence_classes()) + [['A'], ['B'], ['C'], ['D']] + sage: fsm.state("C").final_word_out = 1 + sage: sorted(fsm.equivalence_classes()) + [['A', 'C'], ['B', 'D']] """ # Two states `a` and `b` are j-equivalent if and only if there From 9ce789a01c220e9c7370ffb7cd7297eef8b7c559 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Mon, 23 Jun 2014 21:33:10 +0200 Subject: [PATCH 323/546] is_perfect() for fields --- src/sage/categories/fields.py | 22 +++++++++++++++++-- .../rings/finite_rings/finite_field_base.pyx | 13 +++++++++++ .../rings/function_field/function_field.py | 15 +++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/fields.py b/src/sage/categories/fields.py index 0a1de703129..2a4263a3b42 100644 --- a/src/sage/categories/fields.py +++ b/src/sage/categories/fields.py @@ -6,7 +6,7 @@ # William Stein # 2008 Teresa Gomez-Diaz (CNRS) # 2008-2009 Nicolas M. Thiery -# 2012 Julian Rueth +# 2012-2014 Julian Rueth # # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ @@ -182,7 +182,6 @@ def is_field( self, proof=True ): def is_integrally_closed(self): r""" - Return ``True``, as per :meth:`IntegralDomain.is_integrally_closed`: for every field `F`, `F` is its own field of fractions, hence every element of `F` is integral over `F`. @@ -200,6 +199,25 @@ def is_integrally_closed(self): """ return True + def is_perfect(self): + r""" + Return whether this field is perfect, i.e., its characteristic is + `p=0` or every element has a `p`-th root. + + EXAMPLES:: + + sage: QQ.is_perfect() + True + sage: GF(2).is_perfect() + True + sage: FunctionField(GF(2), 'x').is_perfect() + False + + """ + if self.characteristic() == 0: + return True + else: raise NotImplementedError + def _test_characteristic_fields(self, **options): """ Run generic tests on the method :meth:`.characteristic`. diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index 37d0e5f41d0..4264b693b10 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -101,6 +101,19 @@ cdef class FiniteField(Field): category = FiniteFields() Field.__init__(self, base, names, normalize, category) + def is_perfect(self): + r""" + Return whether this field is perfect, i.e., every element has a `p`-th + root. Always returns ``True`` since finite fields are perfect. + + EXAMPLES:: + + sage: GF(2).is_perfect() + True + + """ + return True + def __repr__(self): """ String representation of this finite field. diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 16abb0df13f..eab50e12f52 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -113,6 +113,21 @@ class FunctionField(Field): sage: isinstance(K, sage.rings.function_field.function_field.FunctionField) True """ + def is_perfect(self): + r""" + Return whether this field is perfect, i.e., its characteristic is `p=0` + or every element has a `p`-th root. + + EXAMPLES:: + + sage: FunctionField(QQ, 'x').is_perfect() + True + sage: FunctionField(GF(2), 'x').is_perfect() + False + + """ + return self.characteristic() == 0 + def some_elements(self): """ Return a list of elements in the function field. From a081f2f5827bcb378b3fa0895d074a25a74454e8 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Mon, 23 Jun 2014 22:20:29 +0200 Subject: [PATCH 324/546] Added copyright header to finite_field_base.pyx The contents of that header were created automatically from the contents of git log (every contributor with >10 lines of contribution). --- .../rings/finite_rings/finite_field_base.pyx | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index 4264b693b10..75a9a331f8f 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -7,7 +7,28 @@ TESTS:: sage: F = K.factor(3)[0][0].residue_field() sage: loads(dumps(F)) == F True + +AUTHORS: + +- Adrien Brochard, David Roe, Jeroen Demeyer, Julian Rueth, Niles Johnson, + Peter Bruin, Travis Scrimshaw, Xavier Caruso: initial version + """ +#***************************************************************************** +# Copyright (C) 2009 David Roe +# Copyright (C) 2010 Niles Johnson +# Copyright (C) 2011 Jeroen Demeyer +# Copyright (C) 2012 Adrien Brochard +# Copyright (C) 2012 Travis Scrimshaw +# Copyright (C) 2012 Xavier Caruso +# Copyright (C) 2013 Peter Bruin +# Copyright (C) 2014 Julian Rueth +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** include "sage/ext/stdsage.pxi" from sage.categories.finite_fields import FiniteFields From 4fe52421d7fdda632a496c81ae9105a2df709a36 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 23 Jun 2014 18:29:00 -0400 Subject: [PATCH 325/546] Added doctest for #12410. --- src/sage/interfaces/maxima_lib.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 7d90915ca29..af367aceb2f 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -838,6 +838,13 @@ def sr_sum(self,*args): ... RuntimeError: ECL says: Error executing code in Maxima: Zero to negative power computed. + Similar situation for :trac:`12410`:: + + sage: x = var('x') + sage: sum(1/x*(-1)^x, x, 0, oo) + Traceback (most recent call last): + ... + RuntimeError: ECL says: Error executing code in Maxima: Zero to negative power computed. """ try: return max_to_sr(maxima_eval([[max_ratsimp],[[max_simplify_sum],([max_sum],[sr_to_max(SR(a)) for a in args])]])); From dc81be8fafb2fdbbda5b50a2e264d97264db8253 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Mon, 23 Jun 2014 20:32:59 +0200 Subject: [PATCH 326/546] Derivations for rational function fields --- .../rings/function_field/function_field.py | 51 ++++++- src/sage/rings/function_field/maps.py | 141 +++++++++++++++++- 2 files changed, 186 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 16abb0df13f..be01de1cd5f 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -7,12 +7,11 @@ - Robert Bradshaw (2010-05-30): added is_finite() -- Julian Rueth (2011-06-08): fixed hom(), extension() +- Julian Rueth (2011-06-08, 2011-09-14, 2014-06-23): fixed hom(), extension(); +use @cached_method; added derivation() - Maarten Derickx (2011-09-11): added doctests -- Julian Rueth (2011-09-14): use @cached_method - - Syed Ahmad Lavasani (2011-12-16): added genus(), is_RationalFunctionField() EXAMPLES: @@ -67,7 +66,7 @@ #***************************************************************************** # Copyright (C) 2010 William Stein # Copyright (C) 2010 Robert Bradshaw -# Copyright (C) 2011 Julian Rueth +# Copyright (C) 2011-2014 Julian Rueth # Copyright (C) 2011 Maarten Derickx # # Distributed under the terms of the GNU General Public License (GPL) @@ -1441,3 +1440,47 @@ def genus(self): """ return 0 + @cached_method + def derivation(self): + """ + Return a generator of the space of derivations over the constant base + field of this function field. + + A derivation on `R` is map `R\to R` with + `D(\alpha+\beta)=D(\alpha)+D(\beta)` and `D(\alpha\beta)=\beta + D(\alpha)+\alpha D(\beta)` for all `\alpha,\beta\in R`. For a function + field `K(x)` with `K` perfect, the derivations form a one-dimensional + `K`-vector space generated by the extension of the usual derivation on + `K[x]` (cf. Proposition 10 in [GT1996]_.) + + OUTPUT: + + An endofunction on this function field. + + REFERENCES:: + + .. [GT1996] Gianni, P., & Trager, B. (1996). Square-free algorithms in + positive characteristic. Applicable Algebra in Engineering, + Communication and Computing, 7(1), 1-14. + + EXAMPLES:: + + sage: K. = FunctionField(GF(3)) + sage: K.derivation() + Derivation map: + From: Rational function field in x over Finite Field of size 3 + To: Rational function field in x over Finite Field of size 3 + + TESTS:: + + sage: L. = FunctionField(K) + sage: L.derivation() + Traceback (most recent call last): + ... + NotImplementedError: not implemented for non-perfect base fields + + """ + from maps import FunctionFieldDerivation_rational + if not self.constant_base_field().is_perfect(): + raise NotImplementedError("not implemented for non-perfect base fields") + return FunctionFieldDerivation_rational(self, self.one()) diff --git a/src/sage/rings/function_field/maps.py b/src/sage/rings/function_field/maps.py index 4763d1d3501..d66f3363a2c 100644 --- a/src/sage/rings/function_field/maps.py +++ b/src/sage/rings/function_field/maps.py @@ -5,7 +5,8 @@ - William Stein (2010): initial version -- Julian Rueth (2011-09-14): refactored class hierarchy +- Julian Rueth (2011-09-14, 2014-06-23): refactored class hierarchy; added +derivation classes EXAMPLES:: @@ -24,7 +25,7 @@ """ #***************************************************************************** # Copyright (C) 2010 William Stein -# Copyright (C) 2011 Julian Rueth +# Copyright (C) 2011-2014 Julian Rueth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -33,8 +34,144 @@ #***************************************************************************** from sage.categories.morphism import Morphism +from sage.categories.map import Map from sage.rings.morphism import RingHomomorphism +class FunctionFieldDerivation(Map): + r""" + A base class for derivations on function fields. + + A derivation on `R` is map `R\to R` with + `D(\alpha+\beta)=D(\alpha)+D(\beta)` and `D(\alpha\beta)=\beta + D(\alpha)+\alpha D(\beta)` for all `\alpha,\beta\in R`. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: d = K.derivation() + sage: isinstance(d, sage.rings.function_field.maps.FunctionFieldDerivation) + True + + """ + def __init__(self, K): + r""" + Initialize a derivation from ``K`` to ``K``. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: d = K.derivation() # indirect doctest + + """ + from function_field import is_FunctionField + if not is_FunctionField(K): + raise ValueError("K must be a function field") + self.__field = K + from sage.categories.homset import Hom + from sage.categories.sets_cat import Sets + Map.__init__(self, Hom(K,K,Sets())) + + def _repr_type(self): + r""" + Return the type of this map (a derivation), for the purposes of printing out self. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: d = K.derivation() + sage: d._repr_type() + 'Derivation' + + """ + return "Derivation" + + def is_injective(self): + r""" + Return whether this derivation is injective. + + OUTPUT: + + Returns ``False`` since derivations are never injective. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: d = K.derivation() + sage: d.is_injective() + False + + """ + return False + +class FunctionFieldDerivation_rational(FunctionFieldDerivation): + r""" + A derivation on a rational function field. + + INPUT: + + - ``K`` -- a rational function field + + - ``u`` -- an element of ``K``, the image of the generator of ``K`` under + the derivation. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: d = K.derivation() + sage: isinstance(d, sage.rings.function_field.maps.FunctionFieldDerivation_rational) + True + + """ + def __init__(self, K, u): + r""" + Initialize a derivation of ``K`` which sends the generator of ``K`` to + ``u``. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: d = K.derivation() # indirect doctest + + """ + from function_field import is_RationalFunctionField + if not is_RationalFunctionField(K): + raise ValueError("K must be a rational function field") + if u.parent() is not K: + raise ValueError("u must be an element in K") + FunctionFieldDerivation.__init__(self, K) + self._u = u + + def _call_(self, x): + r""" + Compute the derivation of ``x``. + + INPUT: + + - ``x`` -- an element of the rational function field + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: d = K.derivation() + sage: d(x) # indirect doctest + 1 + sage: d(x^3) + 3*x^2 + sage: d(1/x) + -1/x^2 + + """ + f,g = x.numerator(),x.denominator() + + if not f.gcd(g).is_one(): + raise NotImplementedError("derivations only implemented for rational functions with coprime numerator and denominator.") + + numerator = f.derivative()*g - f*g.derivative() + if numerator.is_zero(): + return self.codomain().zero() + else: + return self._u * self.codomain()( numerator / g**2 ) + class FunctionFieldIsomorphism(Morphism): r""" A base class for isomorphisms between function fields and From 547f8daebf7a3be2d92d366fa6c19a2ff8730439 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Mon, 23 Jun 2014 21:33:10 +0200 Subject: [PATCH 327/546] is_perfect() for fields --- src/sage/categories/fields.py | 22 +++++++++++++++++-- .../rings/finite_rings/finite_field_base.pyx | 13 +++++++++++ .../rings/function_field/function_field.py | 15 +++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/fields.py b/src/sage/categories/fields.py index 0a1de703129..2a4263a3b42 100644 --- a/src/sage/categories/fields.py +++ b/src/sage/categories/fields.py @@ -6,7 +6,7 @@ # William Stein # 2008 Teresa Gomez-Diaz (CNRS) # 2008-2009 Nicolas M. Thiery -# 2012 Julian Rueth +# 2012-2014 Julian Rueth # # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ @@ -182,7 +182,6 @@ def is_field( self, proof=True ): def is_integrally_closed(self): r""" - Return ``True``, as per :meth:`IntegralDomain.is_integrally_closed`: for every field `F`, `F` is its own field of fractions, hence every element of `F` is integral over `F`. @@ -200,6 +199,25 @@ def is_integrally_closed(self): """ return True + def is_perfect(self): + r""" + Return whether this field is perfect, i.e., its characteristic is + `p=0` or every element has a `p`-th root. + + EXAMPLES:: + + sage: QQ.is_perfect() + True + sage: GF(2).is_perfect() + True + sage: FunctionField(GF(2), 'x').is_perfect() + False + + """ + if self.characteristic() == 0: + return True + else: raise NotImplementedError + def _test_characteristic_fields(self, **options): """ Run generic tests on the method :meth:`.characteristic`. diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index 37d0e5f41d0..4264b693b10 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -101,6 +101,19 @@ cdef class FiniteField(Field): category = FiniteFields() Field.__init__(self, base, names, normalize, category) + def is_perfect(self): + r""" + Return whether this field is perfect, i.e., every element has a `p`-th + root. Always returns ``True`` since finite fields are perfect. + + EXAMPLES:: + + sage: GF(2).is_perfect() + True + + """ + return True + def __repr__(self): """ String representation of this finite field. diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index be01de1cd5f..ac2706fc46b 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -112,6 +112,21 @@ class FunctionField(Field): sage: isinstance(K, sage.rings.function_field.function_field.FunctionField) True """ + def is_perfect(self): + r""" + Return whether this field is perfect, i.e., its characteristic is `p=0` + or every element has a `p`-th root. + + EXAMPLES:: + + sage: FunctionField(QQ, 'x').is_perfect() + True + sage: FunctionField(GF(2), 'x').is_perfect() + False + + """ + return self.characteristic() == 0 + def some_elements(self): """ Return a list of elements in the function field. From be532e523806aa1618c2d2171dda4c5c2b6b64eb Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Mon, 23 Jun 2014 22:20:29 +0200 Subject: [PATCH 328/546] Added copyright header to finite_field_base.pyx The contents of that header were created automatically from the contents of git log (every contributor with >10 lines of contribution). --- .../rings/finite_rings/finite_field_base.pyx | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index 4264b693b10..75a9a331f8f 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -7,7 +7,28 @@ TESTS:: sage: F = K.factor(3)[0][0].residue_field() sage: loads(dumps(F)) == F True + +AUTHORS: + +- Adrien Brochard, David Roe, Jeroen Demeyer, Julian Rueth, Niles Johnson, + Peter Bruin, Travis Scrimshaw, Xavier Caruso: initial version + """ +#***************************************************************************** +# Copyright (C) 2009 David Roe +# Copyright (C) 2010 Niles Johnson +# Copyright (C) 2011 Jeroen Demeyer +# Copyright (C) 2012 Adrien Brochard +# Copyright (C) 2012 Travis Scrimshaw +# Copyright (C) 2012 Xavier Caruso +# Copyright (C) 2013 Peter Bruin +# Copyright (C) 2014 Julian Rueth +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** include "sage/ext/stdsage.pxi" from sage.categories.finite_fields import FiniteFields From 87cf9da2416278dfea890daf37bf696c93a1d972 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Tue, 24 Jun 2014 02:14:55 +0200 Subject: [PATCH 329/546] Include an element's parent in _cache_key() See http://trac.sagemath.org/ticket/16316#comment:8 for a discussion of this. --- src/sage/misc/cachefunc.pyx | 14 +++---- .../rings/padics/padic_ZZ_pX_CR_element.pyx | 17 ++++---- .../rings/polynomial/polynomial_element.pyx | 9 ++--- src/sage/structure/sage_object.pyx | 40 +++++++------------ 4 files changed, 34 insertions(+), 46 deletions(-) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index ff91898a65e..7e9e5879f97 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -483,21 +483,21 @@ def _cache_key(o): sage: a = K(1); a 1 + O(3^20) sage: _cache_key(a) - (((1,),), 0, 20) + (..., ((1,),), 0, 20) This function works if ``o`` is a tuple. In this case it unpacks its entries recursively:: sage: o = (1, 2, (3, a)) sage: _cache_key(o) - (1, 2, (3, (((1,),), 0, 20))) + (1, 2, (3, (..., ((1,),), 0, 20))) Note that tuples are only partially unpacked if some of its entries are hashable:: - sage: o = (matrix([1, 2, 3]), a) + sage: o = (1/2, a) sage: _cache_key(o) - ([1 2 3], (((1,),), 0, 20)) + (1/2, (..., ((1,),), 0, 20)) .. SEEALSO:: @@ -844,7 +844,7 @@ cdef class CachedFunction(object): sage: f(y) 1 + O(2^2) sage: f.cache - {(((((1,),), 0, 1),), ()): 1 + O(2), (((((1,),), 0, 2),), ()): 1 + O(2^2)} + {(((..., ((1,),), 0, 2),), ()): 1 + O(2^2), (((..., ((1,),), 0, 1),), ()): 1 + O(2)} """ # We shortcut a common case of no arguments @@ -1192,7 +1192,7 @@ cdef class WeakCachedFunction(CachedFunction): sage: f(y) (1 + O(2^20))*t + 1 + O(2^2) sage: list(f.cache.keys()) - [((((((1,),), 0, 2), (((1,),), 0, 20)),), ()), ((((((1,),), 0, 1), (((1,),), 0, 20)),), ())] + [(((..., ((..., ((1,),), 0, 1), (..., ((1,),), 0, 20))),), ()), (((..., ((..., ((1,),), 0, 2), (..., ((1,),), 0, 20))),), ())] """ # We shortcut a common case of no arguments @@ -1744,7 +1744,7 @@ cdef class CachedMethodCaller(CachedFunction): sage: a.f(y) 1 + O(2^2) sage: a.f.cache - {(((((1,),), 0, 1),), ()): 1 + O(2), (((((1,),), 0, 2),), ()): 1 + O(2^2)} + {(((..., ((1,),), 0, 2),), ()): 1 + O(2^2), (((..., ((1,),), 0, 1),), ()): 1 + O(2)} """ if self._instance is None: diff --git a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx index 9580daff99e..e2f8ea6534e 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx @@ -474,8 +474,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): def _cache_key(self): r""" - Return a hashable key which uniquely identifies this element among all - elements in the parent. + Return a hashable key which identifies this element. This enables caching for `p`-adic numbers which are not hashable. @@ -502,26 +501,26 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): ``_cache_key`` which is hashable and uniquely identifies them:: sage: a._cache_key() - (((0, 1),), 0, 20) + (..., ((0, 1),), 0, 20) sage: b._cache_key() - (((0, 1),), 0, 1) + (..., ((0, 1),), 0, 1) TESTS: Check that zero values are handled correctly:: sage: K.zero()._cache_key() - 0 + (..., 0) sage: K(0,1)._cache_key() - (0, 1) + (..., 0, 1) """ if self._is_exact_zero(): - return 0 + return (id(self.parent()), 0) elif self._is_inexact_zero(): - return 0, self.valuation() + return (id(self.parent()), 0, self.valuation()) else: - return tuple(tuple(c) if isinstance(c,list) else c for c in self.unit_part().list()), self.valuation(), self.precision_relative() + return id(self.parent()), tuple(tuple(c) if isinstance(c,list) else c for c in self.unit_part().list()), self.valuation(), self.precision_relative() cdef int _set_inexact_zero(self, long absprec) except -1: """ diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 20aaa063ca7..59ed73e8bd3 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -14,7 +14,7 @@ AUTHORS: - Simon King: Use a faster way of conversion from the base ring. - Julian Rueth (2012-05-25,2014-05-09): Fixed is_squarefree() for imperfect - fields, fixed division without remainder over QQbar, added ``_cache_key`` + fields, fixed division without remainder over QQbar; added ``_cache_key`` for polynomials with unhashable coefficients - Simon King (2013-10): Implement copying of :class:`PolynomialBaseringInjection`. @@ -832,8 +832,7 @@ cdef class Polynomial(CommutativeAlgebraElement): def _cache_key(self): """ - Return a key which uniquely identifies this element among all elements - in the parent. + Return a key which identifies this element. .. SEEALSO:: @@ -852,7 +851,7 @@ cdef class Polynomial(CommutativeAlgebraElement): ... TypeError: unhashable type: 'sage.rings.padics.padic_ZZ_pX_CR_element.pAdicZZpXCRElement' sage: f._cache_key() - (0, 1 + O(2^20)) + (..., (0, 1 + O(2^20))) sage: @cached_function ....: def foo(t): return t @@ -861,7 +860,7 @@ cdef class Polynomial(CommutativeAlgebraElement): (1 + O(2^20))*x """ - return tuple(self) + return id(self.parent()), tuple(self) # you may have to replicate this boilerplate code in derived classes if you override # __richcmp__. The python documentation at http://docs.python.org/api/type-structs.html diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index 5050996f605..606f5645365 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -223,14 +223,13 @@ cdef class SageObject: def _cache_key(self): r""" - Return a key which uniquely identifies this objects for caching. The - output must be hashable itself or a tuple of objects which are hashable - or define a ``_cache_key``. + Return a key which identifies this objects for caching. The output must + be hashable itself or a tuple of objects which are hashable or define a + ``_cache_key``. This method will only be called if the object itself is not hashable. - For most objects this will just be the object itself. However, some - immutable objects (such as `p`-adic numbers) can not implement a + Some immutable objects (such as `p`-adic numbers) can not implement a reasonable hash function because their ``==`` operator has been modified to return ``True`` for objects which might behave differently in some computations:: @@ -266,33 +265,24 @@ cdef class SageObject: False sage: b._cache_key() - (((0, 1),), 0, 1) + (..., ((0, 1),), 0, 1) sage: c._cache_key() - (((0, 1), (1,)), 0, 20) + (..., ((0, 1), (1,)), 0, 20) - Note that such ``_cache_key`` often does not uniquely identify an - object in a strict sense:: + An implementation must make sure that for elements ``a`` and ``b``, if ``a != b``, then also ``a._cache_key() != b._cache_key()``. In pratice this means that the ``_cache_key`` should always include the ``id`` of the parent. sage: S. = Qq(4) sage: d = a + O(2) - sage: b._cache_key() == d._cache_key() - True - - However, this kind of behaviour is common for many elements with - different parents. Special care has to be taken when mixing such - elements in caches:: - - sage: A = matrix([[1]],base_ring=GF(2)) - sage: A.set_immutable() - sage: B = matrix([[1]],base_ring=GF(3)) - sage: B.set_immutable() - sage: A == B - True - sage: hash(A) == hash(B) - True + sage: b._cache_key() == d._cache_key() # this would be True if the parents were not included + False """ - return self + try: + hash(self) + except TypeError: + raise NotImplementedError("{} is not hashable and does not implement _cache_key()".format(type(self))) + else: + assert False, "_cache_key() must not be called for hashable elements" ############################################################################# # DATABASE Related code From ebc3316525036bcc2a94edcddaa81bca26f3ea9c Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 24 Jun 2014 09:31:14 +0200 Subject: [PATCH 330/546] trac #16504: additional doctest for nonnegative=False --- src/sage/numerical/mip.pyx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index b2833a2e20b..98e975fb523 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -601,6 +601,20 @@ cdef class MixedIntegerLinearProgram(SageObject): ... ValueError: Exactly one of the available types has to be True + Unbounded real variables:: + + sage: p = MixedIntegerLinearProgram() + sage: x = p.new_variable(real=True, nonnegative=False) + sage: p.add_constraint(x[0]+x[3]<= 8) + sage: p.show() + Maximization: + + Constraints: + x_0 + x_1 <= 8.0 + Variables: + x_0 is a continuous variable (min=-oo, max=+oo) + x_1 is a continuous variable (min=-oo, max=+oo) + TESTS: Default behaviour (:trac:`15521`):: From cd80f02c858cad7d4136a75dc4473006c58bf0f0 Mon Sep 17 00:00:00 2001 From: Jayant Date: Tue, 24 Jun 2014 03:41:55 -0400 Subject: [PATCH 331/546] Fixed parallel element bug. Modified parallel element placement. Changed point color to lighter gray. Added NotImplementedError for rank 0 matroids. --- src/sage/matroids/matroid.pyx | 6 +-- src/sage/matroids/matroids_plot_helpers.py | 63 ++++++++++++++-------- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index fe3ce494e58..b8e1838c979 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -4843,7 +4843,7 @@ cdef class Matroid(SageObject): sage: pos = {'a':(0,0), 'b': (0,1), 'c':(1,0), 'd':(1,1), 'e':(1,-1), 'f':(-1,1), 'g':(-1,-1),'h':(2,0), 'i':(0,2)} sage: M.show(pos_method=1, pos_dict=pos,lims=[-3,3,-3,3]) """ - if self.rank() > 3: + if self.rank() > 3 or self.rank() <= 0: raise NotImplementedError elif B == None: B = list(self.basis()) @@ -4891,9 +4891,9 @@ cdef class Matroid(SageObject): sage: M._cached_info['plot_positions']['k'] (0, 0) """ - # check sanity of pos_dict and add it to cached info if sane - if self.rank() > 3: + if self.rank() > 3 or self.rank() <= 0: raise NotImplementedError + # check sanity of pos_dict and add it to cached info if sane if(pos_dict!=None): import matroids_plot_helpers if matroids_plot_helpers.posdict_is_sane(self,pos_dict) ==True: diff --git a/src/sage/matroids/matroids_plot_helpers.py b/src/sage/matroids/matroids_plot_helpers.py index 51de416082a..d6bfc86d4fc 100644 --- a/src/sage/matroids/matroids_plot_helpers.py +++ b/src/sage/matroids/matroids_plot_helpers.py @@ -34,7 +34,8 @@ 3) Internal point placement and orders deciding heuristics If a custom point placement and/or line orders is desired, then user can simply specify the custom points dictionary as ``M.cached info = - {'plot_positions':,'plot_lineorders':}`` + {'plot_positions':, + 'plot_lineorders':}`` @@ -71,7 +72,9 @@ import scipy.interpolate import numpy as np from sage.plot.all import Graphics, line, text, polygon2d, point, points +from sage.plot.colors import Color from sage.sets.set import Set +from sage.matroids.advanced import newlabel def it(M, B1, nB1, lps): @@ -438,12 +441,13 @@ def slp(M1, pos_dict=None, B=None): for p in list(P)])) newP = [] for pcl in pcls: - pcl_in_basis = [p for p in list(pcl) if p in B] + pcl_list = list(pcl) + pcl_in_basis = [p for p in pcl_list if p in B] if len(pcl_in_basis) > 0: newP.extend(list(pcl - set([pcl_in_basis[0]]))) else: - newP.extend(list(pcl - set([pcl[0]]))) - return [M1.delete(L | set(newP)), L, set(newP)] + newP.extend(list(pcl - set([pcl_list[0]]))) + return [M1.delete(L | set(newP)), L, set(newP)] else: return [M1.delete(L | P), L, P] else: @@ -458,6 +462,7 @@ def addlp(M, M1, L, P, ptsdict, G=None, limits=None): INPUT: - ``M`` -- A matroid. + - ``M1`` -- A simple matroid corresponding to ``M``. - ``L`` -- List of elements in ``M.groundset()`` that are loops of matroid ``M``. - ``P`` -- List of elements in ``M.groundset()`` not in @@ -509,7 +514,8 @@ def addlp(M, M1, L, P, ptsdict, G=None, limits=None): color='black', fill=False, thickness=4) G += text(looptext, (rectx+0.5, recty+0.3), color='black', fontsize=13) - G += point((rectx+0.2, recty+0.3), color='gray', size=300, zorder=2) + G += point((rectx+0.2, recty+0.3), color=Color('#BDBDBD'), size=300, + zorder=2) G += text('Loop(s)', (rectx+0.5+0.4*len(loops)+0.1, recty+0.3), fontsize=13, color='black') limits = tracklims(limits, [rectx, rectx+rectw], [recty, recty+recth]) @@ -531,17 +537,24 @@ def addlp(M, M1, L, P, ptsdict, G=None, limits=None): # add side by side ptsdict[pcl[1]] = (basept[0], basept[1]-0.13) G += points(zip([basept[0]], [basept[1]-0.13]), - color='gray', size=300, zorder=2) + color=Color('#BDBDBD'), size=300, zorder=2) + G += text(pcl[0], (float(basept[0]), + float(basept[1])), color='black', + fontsize=13) G += text(pcl[1], (float(basept[0]), float(basept[1])-0.13), color='black', fontsize=13) limits = tracklims(limits, [basept[0]], [basept[1]-0.13]) else: # add in a bracket - pce = sorted([str(kk) for kk in pcl[1:]]) - G += text('{ '+", ".join(pce)+' }', (float(basept[0]), + pce = sorted([str(kk) for kk in pcl]) + l = newlabel(M.groundset()) + G += text(l+'={ '+", ".join(pce)+' }', (float(basept[0]), float(basept[1]-0.2)-0.034), color='black', fontsize=13) + G += text(l, (float(basept[0]), + float(basept[1])), color='black', + fontsize=13) limits = tracklims(limits, [basept[0]], [(basept[1]-0.2)-0.034]) return G, limits @@ -758,9 +771,9 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): """ G = Graphics() # create lists of loops and parallel elements and simplify given matroid - if B1 is None: - B1 = list(M1.basis()) [M, L, P] = slp(M1, pos_dict=pd, B=B1) + if B1 is None: + B1 = list(M.basis()) M._cached_info = M1._cached_info if M.rank() == 1: @@ -772,9 +785,10 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): pts = {} gnd = sorted(M.groundset()) pts[gnd[0]] = (1, float(2)/3) - G += point((1, float(2)/3), size=300, color='gray', zorder=2) - pt=[1, float(2)/3] - G += text(gnd[0], (float(pt[0]), float(pt[1])), color='black', + G += point((1, float(2)/3), size=300, color=Color('#BDBDBD'), zorder=2) + pt = [1, float(2)/3] + if len(P) == 0: + G += text(gnd[0], (float(pt[0]), float(pt[1])), color='black', fontsize=13) pts2 = pts # track limits [xmin,xmax,ymin,ymax] @@ -812,14 +826,18 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): ptsx, ptsy, x_i, y_i = createline(pts2, bline, lineorders1) lims = tracklims(lims, x_i, y_i) G += line(zip(x_i, y_i), color='black', thickness=3, zorder=1) + pels = [p for p in pts2.keys() if any([M1.rank([p, q]) == 1 + for q in P])] allpts = [list(pts2[i]) for i in M.groundset()] xpts = [float(k[0]) for k in allpts] ypts = [float(k[1]) for k in allpts] - G += points(zip(xpts, ypts), color='gray', size=300, zorder=2) + G += points(zip(xpts, ypts), color=Color('#BDBDBD'), size=300, + zorder=2) for i in pts2: - pt = list(pts2[i]) - G += text(i, (float(pt[0]), float(pt[1])), color='black', - fontsize=13) + if i not in pels: + pt = list(pts2[i]) + G += text(i, (float(pt[0]), float(pt[1])), color='black', + fontsize=13) else: if M._cached_info is None or \ 'plot_positions' not in M._cached_info.keys() or \ @@ -844,14 +862,17 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): ptsx, ptsy, x_i, y_i = createline(pts2, ll, lineorders1) lims = tracklims(lims, x_i, y_i) G += line(zip(x_i, y_i), color='black', thickness=3, zorder=1) + pels = [p for p in pts2.keys() if any([M1.rank([p, q]) == 1 + for q in P])] allpts = [list(pts2[i]) for i in M.groundset()] xpts = [float(k[0]) for k in allpts] ypts = [float(k[1]) for k in allpts] - G += points(zip(xpts, ypts), color='gray', size=300, zorder=2) + G += points(zip(xpts, ypts), color=Color('#BDBDBD'), size=300, zorder=2) for i in pts2: - pt = list(pts2[i]) - G += text(i, (float(pt[0]), float(pt[1])), color='black', - fontsize=13) + if i not in pels: + pt = list(pts2[i]) + G += text(i, (float(pt[0]), float(pt[1])), color='black', + fontsize=13) if sp is True: M1._cached_info['plot_positions'] = pts2 M1._cached_info['plot_lineorders'] = lineorders1 From e6ff553f8280f25854b2192cf3a4fa5dcd923a0b Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 24 Jun 2014 15:21:29 +0200 Subject: [PATCH 332/546] Cosmetic changes --- src/sage/graphs/digraph.py | 4 +- src/sage/graphs/generic_graph.py | 4 +- .../vertex_separation.pyx | 20 ++++---- src/sage/numerical/mip.pyx | 50 ++++++++++--------- 4 files changed, 41 insertions(+), 37 deletions(-) diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index a8af4b03c65..8b3d1573f1c 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -1783,8 +1783,8 @@ def feedback_edge_set(self, constraint_generation= True, value_only=False, solve else: p=MixedIntegerLinearProgram(maximization=False, solver=solver) - b=p.new_variable(binary = True) - d=p.new_variable(integer = True, nonnegative=True) + b=p.new_variable(binary=True) + d=p.new_variable(integer=True, nonnegative=True) n=self.order() diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 38f51aa88e0..d56ecbf96d9 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -6961,8 +6961,8 @@ def feedback_vertex_set(self, value_only=False, solver=None, verbose=0, constrai p = MixedIntegerLinearProgram(maximization = False, solver = solver) - b = p.new_variable(binary = True) - d = p.new_variable(integer = True, nonnegative=True) + b = p.new_variable(binary=True) + d = p.new_variable(integer=True, nonnegative=True) n = self.order() # The removed vertices cover all the back arcs ( third condition ) diff --git a/src/sage/graphs/graph_decompositions/vertex_separation.pyx b/src/sage/graphs/graph_decompositions/vertex_separation.pyx index 9550c72c668..23349b1c579 100644 --- a/src/sage/graphs/graph_decompositions/vertex_separation.pyx +++ b/src/sage/graphs/graph_decompositions/vertex_separation.pyx @@ -790,15 +790,15 @@ def vertex_separation_MILP(G, integrality = False, solver = None, verbosity = 0) ... if ve != vm: ... print "The solution is not optimal!" - Comparison with Different values of the integrality parameter:: + Comparison with different values of the integrality parameter:: sage: from sage.graphs.graph_decompositions import vertex_separation sage: for i in range(10): # long time (11s on sage.math, 2012) - ... G = digraphs.RandomDirectedGNP(10, 0.2) - ... va, la = vertex_separation.vertex_separation_MILP(G, integrality = False) - ... vb, lb = vertex_separation.vertex_separation_MILP(G, integrality = True) - ... if va != vb: - ... print "The integrality parameter change the result!" + ....: G = digraphs.RandomDirectedGNP(10, 0.2) + ....: va, la = vertex_separation.vertex_separation_MILP(G, integrality=False) + ....: vb, lb = vertex_separation.vertex_separation_MILP(G, integrality=True) + ....: if va != vb: + ....: print "The integrality parameter changes the result!" Giving anything else than a DiGraph:: @@ -816,10 +816,10 @@ def vertex_separation_MILP(G, integrality = False, solver = None, verbosity = 0) p = MixedIntegerLinearProgram( maximization = False, solver = solver ) # Declaration of variables. - x = p.new_variable(binary = integrality, nonnegative=bool(not integrality)) # at least one has to be set (#15221) - u = p.new_variable(binary = integrality, nonnegative=bool(not integrality)) - y = p.new_variable(binary = True) - z = p.new_variable(integer = True, nonnegative=True) + x = p.new_variable(binary=integrality, nonnegative=True) + u = p.new_variable(binary=integrality, nonnegative=True) + y = p.new_variable(binary=True) + z = p.new_variable(integer=True, nonnegative=True) N = G.num_verts() V = G.vertices() diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 98e975fb523..a2ab0297076 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -170,7 +170,6 @@ from sage.structure.sage_object cimport SageObject from sage.misc.cachefunc import cached_method from sage.numerical.linear_functions import is_LinearFunction, is_LinearConstraint from sage.misc.superseded import deprecated_function_alias, deprecation -from sage.misc.superseded import deprecated_function_alias cdef class MixedIntegerLinearProgram(SageObject): r""" @@ -242,7 +241,7 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: b = p.new_variable(binary=True) sage: p.set_objective(sum([b[v] for v in g])) sage: for (u,v) in g.edges(labels=None): - ... p.add_constraint(b[u] + b[v], max=1) + ....: p.add_constraint(b[u] + b[v], max=1) sage: p.solve(objective_only=True) 4.0 """ @@ -601,19 +600,24 @@ cdef class MixedIntegerLinearProgram(SageObject): ... ValueError: Exactly one of the available types has to be True - Unbounded real variables:: + Unbounded variables:: sage: p = MixedIntegerLinearProgram() sage: x = p.new_variable(real=True, nonnegative=False) - sage: p.add_constraint(x[0]+x[3]<= 8) + sage: y = p.new_variable(integer=True, nonnegative=False) + sage: p.add_constraint(x[0]+x[3] <= 8) + sage: p.add_constraint(y[0] >= y[1]) sage: p.show() Maximization: Constraints: x_0 + x_1 <= 8.0 + - x_2 + x_3 <= 0.0 Variables: x_0 is a continuous variable (min=-oo, max=+oo) x_1 is a continuous variable (min=-oo, max=+oo) + x_2 is an integer variable (min=-oo, max=+oo) + x_3 is an integer variable (min=-oo, max=+oo) TESTS: @@ -626,10 +630,11 @@ cdef class MixedIntegerLinearProgram(SageObject): if sum([real, binary, integer]) > 1: raise ValueError("Exactly one of the available types has to be True") - if nonnegative is None and not binary: - deprecation(15521, "The default value of 'nonnegative' will change, to "+ - "False instead of True. You should add the explicit "+ - "'nonnegative=True'.") + if nonnegative is None: + if not binary: + deprecation(15521, "The default value of 'nonnegative' will change, to "+ + "False instead of True. You should add the explicit "+ + "'nonnegative=True'.") nonnegative=True if binary: @@ -639,15 +644,13 @@ cdef class MixedIntegerLinearProgram(SageObject): else: vtype = self.__REAL - v=MIPVariable(self, + return MIPVariable(self, vtype, dim=dim, name=name, lower_bound=0 if (nonnegative or binary) else None, upper_bound=1 if binary else None) - return v - cpdef int number_of_constraints(self): r""" Returns the number of constraints assigned so far. @@ -1939,7 +1942,7 @@ cdef class MixedIntegerLinearProgram(SageObject): With a :class:`MIPVariable` as an argument:: - sage: vv = p.new_variable(real=True,nonnegative=False) + sage: vv = p.new_variable(real=True, nonnegative=False) sage: p.get_min(vv) sage: p.get_min(vv[0]) sage: p.set_min(vv,5) @@ -1976,7 +1979,7 @@ cdef class MixedIntegerLinearProgram(SageObject): With a :class:`MIPVariable` as an argument:: - sage: vv = p.new_variable(real=True,nonnegative=False) + sage: vv = p.new_variable(real=True, nonnegative=False) sage: p.get_max(vv) sage: p.get_max(vv[0]) sage: p.set_max(vv,5) @@ -2016,9 +2019,9 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p.set_min(v[1], None) sage: p.get_min(v[1]) """ - if isinstance(v, MIPVariable): - return ( v)._lower_bound - else: + try: + return (v)._lower_bound + except TypeError: return self._backend.variable_lower_bound(self._variables[v]) def get_max(self, v): @@ -2044,9 +2047,9 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p.get_max(v[1]) 6.0 """ - if isinstance(v, MIPVariable): - return ( v)._upper_bound - else: + try: + return (v)._upper_bound + except TypeError: return self._backend.variable_upper_bound(self._variables[v]) def solver_parameter(self, name, value = None): @@ -2267,8 +2270,9 @@ cdef class MIPVariable(SageObject): - ``name`` -- A name for the ``MIPVariable``. - - ``lower_bound, upper_bound`` -- lower bound and upper bound on the - variable. Set to ``None`` to indicate that the variable is unbounded. + - ``lower_bound``, ``upper_bound`` -- lower bound and upper + bound on the variable. Set to ``None`` to indicate that the + variable is unbounded. For more informations, see the method ``MixedIntegerLinearProgram.new_variable``. @@ -2373,7 +2377,7 @@ cdef class MIPVariable(SageObject): EXAMPLES:: sage: p = MixedIntegerLinearProgram() - sage: v = p.new_variable(real=True,nonnegative=True,dim=2) + sage: v = p.new_variable(real=True, nonnegative=True, dim=2) sage: p.get_min(v) 0 sage: p.get_min(v[0]) @@ -2408,7 +2412,7 @@ cdef class MIPVariable(SageObject): EXAMPLES:: sage: p = MixedIntegerLinearProgram() - sage: v = p.new_variable(real=True,nonnegative=True,dim=2) + sage: v = p.new_variable(real=True, nonnegative=True, dim=2) sage: p.get_max(v) sage: p.get_max(v[0]) sage: p.get_max(v[0][0]) From 86fdab581c3d78891c49461dc689057d9bbcce02 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 24 Jun 2014 15:31:04 +0200 Subject: [PATCH 333/546] trac #16525: IntegerRanger._repr_ now uses ... instead of .. --- src/sage/sets/integer_range.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sage/sets/integer_range.py b/src/sage/sets/integer_range.py index f3288f4a4ac..86270bcfa34 100644 --- a/src/sage/sets/integer_range.py +++ b/src/sage/sets/integer_range.py @@ -64,7 +64,7 @@ class IntegerRange(UniqueRepresentation, Parent): sage: list(IntegerRange(2,5)) [2, 3, 4] sage: I = IntegerRange(2,100,5); I - {2, 7, .., 97} + {2, 7, ..., 97} sage: list(I) [2, 7, 12, 17, 22, 27, 32, 37, 42, 47, 52, 57, 62, 67, 72, 77, 82, 87, 92, 97] sage: I.category() @@ -90,7 +90,7 @@ class IntegerRange(UniqueRepresentation, Parent): arithmetic progression starting from the ``begin`` by step ``step``:: sage: I = IntegerRange(54,Infinity,3); I - {54, 57, ..} + {54, 57, ...} sage: I.category() Category of facade infinite enumerated sets sage: p = iter(I) @@ -98,7 +98,7 @@ class IntegerRange(UniqueRepresentation, Parent): (54, 57, 60, 63, 66, 69) sage: I = IntegerRange(54,-Infinity,-3); I - {54, 51, ..} + {54, 51, ...} sage: I.category() Category of facade infinite enumerated sets sage: p = iter(I) @@ -128,7 +128,7 @@ class IntegerRange(UniqueRepresentation, Parent): but the enumeration will begin with this ``middle_point``:: sage: I = IntegerRange(123,-12,-14); I - {123, 109, .., -3} + {123, 109, ..., -3} sage: list(I) [123, 109, 95, 81, 67, 53, 39, 25, 11, -3] sage: J = IntegerRange(123,-12,-14,25); J @@ -200,7 +200,7 @@ class IntegerRange(UniqueRepresentation, Parent): ... L2.sort() ... assert L1 == L2 - Thanks to #8543 empty integer range are allowed:: + Thanks to :trac:`8543` empty integer range are allowed:: sage: TestSuite(IntegerRange(0, 5, -1)).run() """ @@ -393,9 +393,9 @@ def _repr_(self): if self.cardinality() < 6: return "{" + ", ".join(str(x) for x in self) + "}" elif self._step == 1: - return "{%s, .., %s}"%(self._begin, self._end-self._step) + return "{%s, ..., %s}"%(self._begin, self._end-self._step) else: - return "{%s, %s, .., %s}"%(self._begin, self._begin+self._step, + return "{%s, %s, ..., %s}"%(self._begin, self._begin+self._step, self._end-self._step) def rank(self,x): @@ -537,16 +537,16 @@ def _repr_(self): TESTS:: sage: IntegerRange(123,12,-4) #indirect doctest - {123, 119, .., 15} + {123, 119, ..., 15} sage: IntegerRange(-57,1,3) #indirect doctest - {-57, -54, .., 0} + {-57, -54, ..., 0} sage: IntegerRange(-57,Infinity,8) #indirect doctest - {-57, -49, ..} + {-57, -49, ...} sage: IntegerRange(-112,-Infinity,-13) #indirect doctest - {-112, -125, ..} + {-112, -125, ...} """ - return "{%s, %s, ..}"%(self._begin, self._begin+self._step) + return "{%s, %s, ...}"%(self._begin, self._begin+self._step) def __contains__(self, elt): r""" From 47c6ba49a1c00f22730f4ae55f081941770dd99e Mon Sep 17 00:00:00 2001 From: Jan Keitel Date: Tue, 24 Jun 2014 15:58:32 +0200 Subject: [PATCH 334/546] Typo in docstring for toric plotter. --- src/sage/geometry/toric_plotter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/toric_plotter.py b/src/sage/geometry/toric_plotter.py index 301988bf27c..b269b3806fb 100644 --- a/src/sage/geometry/toric_plotter.py +++ b/src/sage/geometry/toric_plotter.py @@ -604,7 +604,8 @@ def plot_walls(self, walls): sage: print tp.plot_walls([quadrant]) Graphics object consisting of 2 graphics primitives - Let's also check that truncating polyhedron is functioning correctly:: + Let's also check that truncating the polyhedron is functioning + correctly:: sage: tp = ToricPlotter({"mode": "box"}, 2, quadrant.rays()) sage: print tp.plot_walls([quadrant]) From c4f603b4b967ca841571fb0d565d4328ec0c2802 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 24 Jun 2014 16:53:06 +0200 Subject: [PATCH 335/546] Use Integer.factor() for factoring --- src/sage/rings/arith.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/arith.py b/src/sage/rings/arith.py index 0ea90152efb..8569903a5ef 100644 --- a/src/sage/rings/arith.py +++ b/src/sage/rings/arith.py @@ -4946,7 +4946,7 @@ def two_squares(n): See http://www.schorn.ch/howto.html """ from sage.rings.all import Integer - n = pari(n) + n = Integer(n) if n <= 0: if n == 0: @@ -4960,8 +4960,8 @@ def two_squares(n): # First check whether it is possible to write n as a sum of two # squares: all prime powers p^e must have p = 2 or p = 1 mod 4 # or e even. - for i in range(F.nrows()): - if F[1][i] % 2 == 1 and F[0][i] % 4 == 3: + for (p,e) in F: + if e % 2 == 1 and p % 4 == 3: raise ValueError("%s is not a sum of 2 squares"%n) # We run over all factors of n, write each factor p^e as @@ -4969,14 +4969,12 @@ def two_squares(n): # (using multiplication in Z[I]) in a^2 + b^2. a = pari(1) b = pari(0) - for i in range(F.nrows()): - e = int(F[1][i]) + for (p,e) in F: if e >= 2: - m = F[0][i] ** (e//2) + m = p ** (e//2) a *= m b *= m if e % 2 == 1: - p = F[0][i] if p == 2: # (a + bi) *= (1 + I) a,b = a - b, a + b From c668400844ce4b280f115601d057e5ac28657e61 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 24 Jun 2014 17:19:54 +0200 Subject: [PATCH 336/546] trac #16526: graphs.IntersectionGraph constructor --- src/sage/graphs/generators/intersection.py | 52 ++++++++++++++++++++++ src/sage/graphs/graph_generators.py | 4 +- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/generators/intersection.py b/src/sage/graphs/generators/intersection.py index d89cd4cc9cf..117960c429d 100644 --- a/src/sage/graphs/generators/intersection.py +++ b/src/sage/graphs/generators/intersection.py @@ -497,3 +497,55 @@ def OrthogonalArrayBlockGraph(k,n,OA=None): g.name("OA({},{})".format(k,n)) return g + +def IntersectionGraph(S): + r""" + Returns the intersection graph of the family `S` + + The intersection graph of a family `S` is a graph `G` with `V(G)=S` such + that two elements `s_1,s_2\in S` are adjacent in `G` if and only if `s_1\cap + s_2\neq \emptyset`. + + INPUT: + + - ``S`` -- a list of sets/tuples/iterables + + .. NOTE:: + + The elements of `S` must be finite, hashable, and the elements of + any `s\in S` must be hashable too. + + EXAMPLE:: + + sage: graphs.IntersectionGraph([(1,2,3),(3,4,5),(5,6,7)]) + Intersection Graph: Graph on 3 vertices + + TESTS:: + + sage: graphs.IntersectionGraph([(1,2,[1])]) + Traceback (most recent call last): + ... + TypeError: The elements of S must be hashable, and this one is not: (1, 2, [1]) + """ + from itertools import combinations + + for s in S: + try: + hash(s) + except TypeError: + raise TypeError("The elements of S must be hashable, and this one is not: {}".format(s)) + + ground_set_to_sets = {} + for s in S: + for x in s: + if x not in ground_set_to_sets: + ground_set_to_sets[x] = [] + ground_set_to_sets[x].append(s) + + g = Graph(name="Intersection Graph") + g.add_vertices(S) + for clique in ground_set_to_sets.itervalues(): + g.add_edges((u,v) for u,v in combinations(clique,2)) + + return g + diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index cf713b61862..37ac1293945 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -230,7 +230,8 @@ def __append_to_doc(methods): """ __append_to_doc( - ["IntervalGraph", + ["IntersectionGraph", + "IntervalGraph", "OrthogonalArrayBlockGraph", "PermutationGraph", "ToleranceGraph"]) @@ -1396,6 +1397,7 @@ def fusenes(self, hexagon_count, benzenoids=False): ########################################################################### import sage.graphs.generators.intersection IntervalGraph = staticmethod(sage.graphs.generators.intersection.IntervalGraph) + IntersectionGraph = staticmethod(sage.graphs.generators.intersection.IntersectionGraph) PermutationGraph = staticmethod(sage.graphs.generators.intersection.PermutationGraph) OrthogonalArrayBlockGraph = staticmethod(sage.graphs.generators.intersection.OrthogonalArrayBlockGraph) ToleranceGraph = staticmethod(sage.graphs.generators.intersection.ToleranceGraph) From c0e39eb03cdc567aa9cb1f5fd9d69cdb2b4f3368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Apitzsch?= Date: Tue, 24 Jun 2014 17:29:48 +0200 Subject: [PATCH 337/546] remove deprecated code from #9005, #14138, #14724 --- src/sage/combinat/all.py | 5 +- src/sage/combinat/combinat.py | 164 ---------------------------------- src/sage/combinat/tableau.py | 17 ---- 3 files changed, 2 insertions(+), 184 deletions(-) diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index 4c27445c5fb..c71456a3985 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -3,10 +3,9 @@ CombinatorialObject, CombinatorialClass, FilteredCombinatorialClass, \ UnionCombinatorialClass, MapCombinatorialClass, \ InfiniteAbstractCombinatorialClass, \ - number_of_combinations, number_of_arrangements, \ - derangements, number_of_derangements, tuples, number_of_tuples, \ + tuples, number_of_tuples, \ unordered_tuples, number_of_unordered_tuples, permutations, \ - permutations_iterator, number_of_permutations, cyclic_permutations, \ + cyclic_permutations, \ cyclic_permutations_iterator, bell_polynomial, fibonacci_sequence, \ fibonacci_xrange, bernoulli_polynomial diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index 0b61ebe8924..56b9aa6eb2b 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -2158,76 +2158,6 @@ def __iter__(self): ##################################################### #### combinatorial sets/lists -def number_of_combinations(mset,k): - """ - Returns the size of combinations(mset,k). IMPLEMENTATION: Wraps - GAP's NrCombinations. - - EXAMPLES:: - - sage: mset = [1,1,2,3,4,4,5] - sage: number_of_combinations(mset,2) - doctest:1: DeprecationWarning: Use Combinations(mset,k).cardinality() instead. - See http://trac.sagemath.org/14138 for details. - 12 - """ - from sage.combinat.combination import Combinations - from sage.misc.superseded import deprecation - deprecation(14138, 'Use Combinations(mset,k).cardinality() instead.') - return Combinations(mset,k).cardinality() - -def number_of_arrangements(mset,k): - """ - Returns the size of arrangements(mset,k). - - EXAMPLES:: - - sage: mset = [1,1,2,3,4,4,5] - sage: number_of_arrangements(mset,2) - doctest:1: DeprecationWarning: Use Arrangements(mset,k).cardinality() instead. - See http://trac.sagemath.org/14138 for details. - 22 - """ - from sage.combinat.permutation import Arrangements - from sage.misc.superseded import deprecation - deprecation(14138, 'Use Arrangements(mset,k).cardinality() instead.') - return Arrangements(mset, k).cardinality() - -def derangements(mset): - """ - This is deprecated in :trac:`9005`. Use instead :class:`Derangements`. - - EXAMPLES:: - - sage: mset = [1,2,3,4] - sage: D = derangements(mset); D - doctest:1: DeprecationWarning: derangements() is deprecated. Use Derangements instead. - See http://trac.sagemath.org/9005 for details. - Derangements of the set [1, 2, 3, 4] - """ - from sage.misc.superseded import deprecation - deprecation(9005,'derangements() is deprecated. Use Derangements instead.') - from sage.combinat.derangements import Derangements - return Derangements(mset) - -def number_of_derangements(mset): - """ - This is deprecated in :trac:`9005`. Use :meth:`Derangements.cardinality()` - instead. - - EXAMPLES:: - - sage: mset = [1,2,3,4] - sage: number_of_derangements(mset) - doctest:1: DeprecationWarning: number_of_derangements() is deprecated. Use Derangements.cardinality() instead. - See http://trac.sagemath.org/9005 for details. - 9 - """ - from sage.misc.superseded import deprecation - deprecation(9005,'number_of_derangements() is deprecated. Use Derangements.cardinality() instead.') - from sage.combinat.derangements import Derangements - return Derangements(mset).cardinality() - def tuples(S,k): """ An ordered tuple of length k of set is an ordered selection with @@ -2378,100 +2308,6 @@ def permutations(mset): ans = Permutations(mset) return ans.list() -def permutations_iterator(mset,n=None): - """ - Do not use this function. It is deprecated in :trac:`14138`. - Use Permutations instead. For example, instead of - - ``for p in permutations_iterator(range(1, m+1), n)`` - - use - - ``for p in Permutations(m, n)``. - - Note that :class:`Permutations`, unlike this function, treats repeated - elements as identical. - - If you insist on using this now: - - Returns an iterator (http://docs.python.org/lib/typeiter.html) - which can be used in place of permutations(mset) if all you need it - for is a 'for' loop. - - Posted by Raymond Hettinger, 2006/03/23, to the Python Cookbook: - http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/474124 - - Note- This function considers repeated elements as different - entries, so for example:: - - sage: from sage.combinat.combinat import permutations, permutations_iterator - sage: mset = [1,2,2] - sage: permutations(mset) - doctest:...: DeprecationWarning: Use the Permutations object instead. - See http://trac.sagemath.org/14772 for details. - [[1, 2, 2], [2, 1, 2], [2, 2, 1]] - sage: for p in permutations_iterator(mset): print p - doctest:...: DeprecationWarning: Use the Permutations object instead. - See http://trac.sagemath.org/14138 for details. - [1, 2, 2] - [2, 1, 2] - [2, 2, 1] - [2, 1, 2] - [2, 2, 1] - - EXAMPLES:: - - sage: X = permutations_iterator(range(3),2) - sage: [x for x in X] - [[0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1]] - """ - from sage.misc.superseded import deprecation - deprecation(14138, 'Use the Permutations object instead.') - items = mset - if n is None: - n = len(items) - from sage.combinat.permutation import Permutations - for i in range(len(items)): - v = items[i:i+1] - if n == 1: - yield v - else: - rest = items[:i] + items[i+1:] - for p in Permutations(rest, n-1): - yield v + list(p) - -def number_of_permutations(mset): - """ - Do not use this function. It was deprecated in :trac:`14138`. - Use :class:`Permutations` instead. For example, instead of - - ``number_of_permutations(mset)`` - - use - - ``Permutations(mset).cardinality()``. - - If you insist on using this now: - - Returns the size of permutations(mset). - - AUTHORS: - - - Robert L. Miller - - EXAMPLES:: - - sage: mset = [1,1,2,2,2] - sage: number_of_permutations(mset) - doctest:...: DeprecationWarning: Use the Permutations object instead. - See http://trac.sagemath.org/14138 for details. - 10 - """ - from sage.misc.superseded import deprecation - deprecation(14138, 'Use the Permutations object instead.') - from sage.combinat.permutation import Permutations - return Permutations(mset).cardinality() - def cyclic_permutations(mset): """ This function is deprecated in :trac:`14772`. Use instead diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index 8a94d92289f..1f5643d492b 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -857,23 +857,6 @@ def to_word(self): """ return self.to_word_by_row() - - def to_permutation(self): - """ - Deprecated in :trac:`14724`. Use :meth:`reading_word_permutation()` - instead. - - EXAMPLES:: - - sage: Tableau([[1,2],[3,4]]).to_permutation() - doctest:...: DeprecationWarning: to_permutation() is deprecated. Use instead reading_word_permutation() - See http://trac.sagemath.org/14724 for details. - [3, 4, 1, 2] - """ - from sage.misc.superseded import deprecation - deprecation(14724, 'to_permutation() is deprecated. Use instead reading_word_permutation()') - return self.reading_word_permutation() - def attacking_pairs(self): """ Deprecated in :trac:`15327`. Use ``T.shape().attacking_pairs()`` From 924c6cddbf1e86a6406ab93dbb7a27c2b9aa0983 Mon Sep 17 00:00:00 2001 From: Andrey Novoseltsev Date: Tue, 24 Jun 2014 09:14:35 -0700 Subject: [PATCH 338/546] Typo in the typo fix. --- src/sage/geometry/toric_plotter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/toric_plotter.py b/src/sage/geometry/toric_plotter.py index b269b3806fb..d3cfa8b186f 100644 --- a/src/sage/geometry/toric_plotter.py +++ b/src/sage/geometry/toric_plotter.py @@ -604,7 +604,7 @@ def plot_walls(self, walls): sage: print tp.plot_walls([quadrant]) Graphics object consisting of 2 graphics primitives - Let's also check that truncating the polyhedron is functioning + Let's also check that the truncating polyhedron is functioning correctly:: sage: tp = ToricPlotter({"mode": "box"}, 2, quadrant.rays()) From bc1cc7d411fd805879f3c179b6dfaf28c69fe5fc Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Wed, 25 Jun 2014 00:18:45 +0200 Subject: [PATCH 339/546] first attempt inspired from gurobi_backend --- src/sage/numerical/backends/coin_backend.pyx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sage/numerical/backends/coin_backend.pyx b/src/sage/numerical/backends/coin_backend.pyx index e7632f118a8..66d40e2c1d9 100644 --- a/src/sage/numerical/backends/coin_backend.pyx +++ b/src/sage/numerical/backends/coin_backend.pyx @@ -832,10 +832,13 @@ cdef class CoinBackend(GenericBackend): cdef double v solution = self.model.solver().getColSolution() if solution == NULL: - v = 0.0 - return v + v = 0.0 else: - return solution[variable] + v = solution[variable] + if self.is_variable_binary(variable): + return round(v) + else: + return v cpdef int ncols(self): r""" From c0a7fb816bff291f99053b623bf07468190eb49c Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Tue, 24 Jun 2014 23:25:49 +0100 Subject: [PATCH 340/546] Trac 16316: reviewer patch --- src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx | 7 +++++-- src/sage/structure/sage_object.pyx | 13 ++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx index e2f8ea6534e..6aabf11bbbe 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx @@ -484,7 +484,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): EXAMPLE: - In the following example, ``a`` and ``b`` compare equal. They can not + In the following example, ``a`` and ``b`` compare equal. They cannot have a meaningful hash value since then their hash value would have to be the same:: @@ -520,7 +520,10 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): elif self._is_inexact_zero(): return (id(self.parent()), 0, self.valuation()) else: - return id(self.parent()), tuple(tuple(c) if isinstance(c,list) else c for c in self.unit_part().list()), self.valuation(), self.precision_relative() + return (id(self.parent()), + tuple(tuple(c) if isinstance(c, list) else c + for c in self.unit_part().list()), + self.valuation(), self.precision_relative()) cdef int _set_inexact_zero(self, long absprec) except -1: """ diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index 606f5645365..9d4e53189ce 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -223,13 +223,13 @@ cdef class SageObject: def _cache_key(self): r""" - Return a key which identifies this objects for caching. The output must - be hashable itself or a tuple of objects which are hashable or define a - ``_cache_key``. + Return a key which identifies this object for caching. The output must + be hashable itself, or a tuple of objects which are hashable or define + a ``_cache_key``. This method will only be called if the object itself is not hashable. - Some immutable objects (such as `p`-adic numbers) can not implement a + Some immutable objects (such as `p`-adic numbers) cannot implement a reasonable hash function because their ``==`` operator has been modified to return ``True`` for objects which might behave differently in some computations:: @@ -269,7 +269,10 @@ cdef class SageObject: sage: c._cache_key() (..., ((0, 1), (1,)), 0, 20) - An implementation must make sure that for elements ``a`` and ``b``, if ``a != b``, then also ``a._cache_key() != b._cache_key()``. In pratice this means that the ``_cache_key`` should always include the ``id`` of the parent. + An implementation must make sure that for elements ``a`` and ``b``, + if ``a != b``, then also ``a._cache_key() != b._cache_key()``. + In practice this means that the ``_cache_key`` should always include + the ``id`` of the parent. sage: S. = Qq(4) sage: d = a + O(2) From a1cc92d80e6d4a57a3696a5407770d1d21fb09e4 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Wed, 25 Jun 2014 02:44:36 +0200 Subject: [PATCH 341/546] Fix uniqueness of function field extensions The key to create a function field has to depend on the base field and not only on the polynomial used to define it. This is because polynomials over different function fields may compare equal but still the corresponding function fields should be different. --- src/sage/rings/function_field/constructor.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/constructor.py b/src/sage/rings/function_field/constructor.py index 78aadd710f6..db31b7eda9f 100644 --- a/src/sage/rings/function_field/constructor.py +++ b/src/sage/rings/function_field/constructor.py @@ -137,12 +137,27 @@ def create_key(self,polynomial,names): sage: K. = FunctionField(QQ) sage: R.=K[] sage: L. = K.extension(x-y^2) # indirect doctest + + TESTS: + + Verify that :trac:`16530` has been resolved:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2-x) + sage: R. = L[] + sage: M. = L.extension(z-1) + sage: R. = K[] + sage: N. = K.extension(z-1) + sage: M is N + False + """ if names is None: names=polynomial.variable_name() if not isinstance(names,tuple): names=(names,) - return (polynomial,names) + return (polynomial,names,polynomial.base_ring()) def create_object(self,version,key,**extra_args): """ From ccfb89948509715643a620a3bb21332b55be4e40 Mon Sep 17 00:00:00 2001 From: Miguel Marco Date: Wed, 25 Jun 2014 07:04:07 +0200 Subject: [PATCH 342/546] Modified behaviour of .solve() for multiple variables. --- src/sage/symbolic/expression.pyx | 35 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 94b4542cbca..d1106912a04 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -8138,7 +8138,7 @@ cdef class Expression(CommutativeRingElement): if complexity_measure is None: return simplified_expr - + if complexity_measure(simplified_expr) < complexity_measure(self): return simplified_expr else: @@ -9348,7 +9348,7 @@ cdef class Expression(CommutativeRingElement): sage: x.solve((1,2)) Traceback (most recent call last): ... - TypeError: 1 is not a valid variable. + TypeError: (1, 2) are not valid variables. """ import operator cdef Expression ex @@ -9370,25 +9370,24 @@ cdef class Expression(CommutativeRingElement): if multiplicities and to_poly_solve: raise NotImplementedError("to_poly_solve does not return multiplicities") - # Take care of cases like solve([x^2-1], [x]) for consistency with - # multiple variable input in sage.symbolic.relation.solve(). - # There *should* be only one variable in the list, since it is - # passed from sage.symbolic.relation.solve() and multiple variables - # there don't call this function. + if isinstance(x, (list, tuple)): - x = x[0] + if not all([isinstance(i, Expression) for i in x]): + raise TypeError("%s are not valid variables." % repr(x)) - if x is None: - v = ex.variables() - if len(v) == 0: - if multiplicities: - return [], [] - else: - return [] - x = v[0] - if not isinstance(x, Expression): - raise TypeError("%s is not a valid variable." % repr(x)) + else: + if x is None: + v = ex.variables() + if len(v) == 0: + if multiplicities: + return [], [] + else: + return [] + x = v[0] + + if not isinstance(x, Expression): + raise TypeError("%s is not a valid variable." % repr(x)) m = ex._maxima_() P = m.parent() From 28a08aa0c6a28100d4d2e33643b0bb23b6831bb5 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Wed, 25 Jun 2014 07:29:20 +0200 Subject: [PATCH 343/546] 16007: doctest latexification; move author info to top --- src/sage/misc/latex.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py index 59671abb54e..41a1d12613d 100644 --- a/src/sage/misc/latex.py +++ b/src/sage/misc/latex.py @@ -4,6 +4,11 @@ In order to support latex formatting, an object should define a special method ``_latex_(self)`` that returns a string, which will be typeset in a mathematical mode (the exact mode depends on circumstances). + + AUTHORS: + + - William Stein: original implementation + - Joel B. Mohler: latex_variable_name() drastic rewrite and many doc-tests """ #***************************************************************************** @@ -15,7 +20,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** - EMBEDDED_MODE = False COMMON_HEADER = \ @@ -2659,9 +2663,12 @@ def latex_variable_name(x, is_fname=False): sage: latex_variable_name('x_ast') 'x_{\\ast}' - AUTHORS: - - - Joel B. Mohler: drastic rewrite and many doc-tests + TESTS:: + + sage: latex_variable_name('_C') # :trac:`16007` + 'C' + sage: latex_variable_name('_K1') + 'K_{1}' """ underscore = x.find("_") if underscore == -1: From 6026de49e31c69e658c986614d6c7bbd9c3d73e7 Mon Sep 17 00:00:00 2001 From: Miguel Marco Date: Wed, 25 Jun 2014 07:39:08 +0200 Subject: [PATCH 344/546] Doctest fixed --- src/sage/symbolic/expression.pyx | 12 +++++++++++- src/sage/symbolic/relation.py | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index d1106912a04..3e0aafdf7b8 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -9149,7 +9149,7 @@ cdef class Expression(CommutativeRingElement): INPUT: - - ``x`` - variable to solve for + - ``x`` - variable(s) to solve for - ``multiplicities`` - bool (default: False); if True, return corresponding multiplicities. This keyword is @@ -9275,6 +9275,13 @@ cdef class Expression(CommutativeRingElement): sage: sol.sage() [[x == 1/4*pi + pi*z...]] + We can be also solved for several variables:: + + sage: var('b, c') + (b, c) + sage: solve((b-1)*(c-1), [b,c]) + [[b == 1, c == r3], [b == r4, c == 1]] + Some basic inequalities can be also solved:: sage: x,y=var('x,y'); (ln(x)-ln(y)>0).solve(x) @@ -9286,6 +9293,9 @@ cdef class Expression(CommutativeRingElement): [[0 < y, y < x, 0 < x]] [[y < x, 0 < y]] + + + TESTS: :trac:`7325` (solving inequalities):: diff --git a/src/sage/symbolic/relation.py b/src/sage/symbolic/relation.py index 3c9e907844a..7c523e34e41 100644 --- a/src/sage/symbolic/relation.py +++ b/src/sage/symbolic/relation.py @@ -719,7 +719,7 @@ def solve(f, *args, **kwds): sage: solve([x == 1], (1, a)) Traceback (most recent call last): ... - TypeError: 1 is not a valid variable. + TypeError: (1, a) are not valid variables. Test that the original version of a system in the French Sage book now works (:trac:`14306`):: From 7f9a4a71218515e90a17096b4f8163bfc6e135c9 Mon Sep 17 00:00:00 2001 From: Jayant Date: Wed, 25 Jun 2014 01:56:47 -0400 Subject: [PATCH 345/546] Added rank 0 functionality. Fixed repeated labels for parallel classes bug. --- src/sage/matroids/matroid.pyx | 4 ++-- src/sage/matroids/matroids_plot_helpers.py | 28 ++++++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index b8e1838c979..1eff04534cb 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -4843,7 +4843,7 @@ cdef class Matroid(SageObject): sage: pos = {'a':(0,0), 'b': (0,1), 'c':(1,0), 'd':(1,1), 'e':(1,-1), 'f':(-1,1), 'g':(-1,-1),'h':(2,0), 'i':(0,2)} sage: M.show(pos_method=1, pos_dict=pos,lims=[-3,3,-3,3]) """ - if self.rank() > 3 or self.rank() <= 0: + if self.rank() > 3: raise NotImplementedError elif B == None: B = list(self.basis()) @@ -4891,7 +4891,7 @@ cdef class Matroid(SageObject): sage: M._cached_info['plot_positions']['k'] (0, 0) """ - if self.rank() > 3 or self.rank() <= 0: + if self.rank() > 3: raise NotImplementedError # check sanity of pos_dict and add it to cached info if sane if(pos_dict!=None): diff --git a/src/sage/matroids/matroids_plot_helpers.py b/src/sage/matroids/matroids_plot_helpers.py index d6bfc86d4fc..5fe19e98d68 100644 --- a/src/sage/matroids/matroids_plot_helpers.py +++ b/src/sage/matroids/matroids_plot_helpers.py @@ -530,6 +530,7 @@ def addlp(M, M1, L, P, ptsdict, G=None, limits=None): if M.rank([g, p]) == 1: pcl.extend([p]) pcls.append(pcl) + ext_gnd = list(M.groundset()) for pcl in pcls: if len(pcl) > 1: basept = list(ptsdict[pcl[0]]) @@ -548,7 +549,8 @@ def addlp(M, M1, L, P, ptsdict, G=None, limits=None): else: # add in a bracket pce = sorted([str(kk) for kk in pcl]) - l = newlabel(M.groundset()) + l = newlabel(set(ext_gnd)) + ext_gnd.append(l) G += text(l+'={ '+", ".join(pce)+' }', (float(basept[0]), float(basept[1]-0.2)-0.034), color='black', fontsize=13) @@ -776,7 +778,29 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): B1 = list(M.basis()) M._cached_info = M1._cached_info - if M.rank() == 1: + if M.rank() == 0: + limits = None + loops = L + looptext = ", ".join([str(l) for l in loops]) + rectx = -1 + recty = -1 + rectw = 0.5 + 0.4*len(loops) + 0.5 # controlled based on len(loops) + recth = 0.6 + G += polygon2d([[rectx, recty], [rectx, recty+recth], + [rectx+rectw, recty+recth], [rectx+rectw, recty]], + color='black', fill=False, thickness=4) + G += text(looptext, (rectx+0.5, recty+0.3), color='black', + fontsize=13) + G += point((rectx+0.2, recty+0.3), color=Color('#BDBDBD'), size=300, + zorder=2) + G += text('Loop(s)', (rectx+0.5+0.4*len(loops)+0.1, recty+0.3), + fontsize=13, color='black') + limits = tracklims(limits, [rectx, rectx+rectw], [recty, recty+recth]) + G.axes(False) + G.axes_range(xmin=limits[0]-0.5, xmax=limits[1]+0.5, + ymin=limits[2]-0.5, ymax=limits[3]+0.5) + return G + elif M.rank() == 1: if M._cached_info is not None and \ 'plot_positions' in M._cached_info.keys() and \ M._cached_info['plot_positions'] is not None: From 0aeabda119cfc33541b5913624dbcedb32d9abae Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Wed, 25 Jun 2014 08:05:15 +0200 Subject: [PATCH 346/546] Review commit #16531 --- src/sage/symbolic/expression.pyx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 3e0aafdf7b8..4e6166fdb78 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -9148,7 +9148,6 @@ cdef class Expression(CommutativeRingElement): INPUT: - - ``x`` - variable(s) to solve for - ``multiplicities`` - bool (default: False); if True, @@ -9275,7 +9274,7 @@ cdef class Expression(CommutativeRingElement): sage: sol.sage() [[x == 1/4*pi + pi*z...]] - We can be also solved for several variables:: + We can also solve for several variables:: sage: var('b, c') (b, c) @@ -9293,9 +9292,6 @@ cdef class Expression(CommutativeRingElement): [[0 < y, y < x, 0 < x]] [[y < x, 0 < y]] - - - TESTS: :trac:`7325` (solving inequalities):: @@ -9384,8 +9380,6 @@ cdef class Expression(CommutativeRingElement): if isinstance(x, (list, tuple)): if not all([isinstance(i, Expression) for i in x]): raise TypeError("%s are not valid variables." % repr(x)) - - else: if x is None: v = ex.variables() From e1c197671f7cc60ce3865319b7043ce1175f38ff Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Wed, 25 Jun 2014 04:03:29 +0200 Subject: [PATCH 347/546] content() of integer polynomials equal for NTL and FLINT This fixes an error in squarefree_decomposition(), NTL's squarefree decomposition assumed that the input polynomial has leading positive coefficient. This was not the case if the content was 1 with leading coefficient -1. --- src/sage/libs/flint/fmpz.pxi | 2 ++ src/sage/libs/flint/fmpz_poly.pxi | 1 + .../polynomial_integer_dense_flint.pyx | 32 +++++++++++++++++-- .../polynomial_integer_dense_ntl.pyx | 11 ++++++- 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/sage/libs/flint/fmpz.pxi b/src/sage/libs/flint/fmpz.pxi index 23bedbac020..ed70af5851b 100644 --- a/src/sage/libs/flint/fmpz.pxi +++ b/src/sage/libs/flint/fmpz.pxi @@ -14,8 +14,10 @@ cdef extern from "flint/fmpz.h": void fmpz_clear(fmpz_t f) void fmpz_print(fmpz_t f) int fmpz_is_one(fmpz_t f) + int fmpz_sgn(fmpz_t f) void fmpz_get_mpz(mpz_t rop, fmpz_t op) void fmpz_set_mpz(fmpz_t rop, mpz_t op) + void fmpz_neg(fmpz_t rop, fmpz_t op) void fmpz_add_ui(fmpz_t f, fmpz_t g, unsigned long c) diff --git a/src/sage/libs/flint/fmpz_poly.pxi b/src/sage/libs/flint/fmpz_poly.pxi index cbea69e663f..61fa4f568c2 100644 --- a/src/sage/libs/flint/fmpz_poly.pxi +++ b/src/sage/libs/flint/fmpz_poly.pxi @@ -29,6 +29,7 @@ cdef extern from "flint/fmpz_poly.h": void fmpz_poly_set_coeff_fmpz(fmpz_poly_t poly, unsigned long n, fmpz_t x) void fmpz_poly_get_coeff_mpz(mpz_t x, fmpz_poly_t poly, unsigned long n) + void fmpz_poly_get_coeff_fmpz(fmpz_t x, fmpz_poly_t poly, unsigned long n) void fmpz_poly_get_coeff_mpz_read_only(mpz_t x, fmpz_poly_t poly, \ unsigned long n) fmpz* fmpz_poly_get_coeff_ptr(fmpz_poly_t poly, unsigned long n) diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx index 34b13807d9f..36cf77d3870 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx @@ -330,7 +330,8 @@ cdef class Polynomial_integer_dense_flint(Polynomial): cpdef Integer content(self): r""" Return the greatest common divisor of the coefficients of this - polynomial. + polynomial. The sign is the sign of the leading coefficient. The + content of the zero polynomial is zero. EXAMPLES:: @@ -351,14 +352,27 @@ cdef class Polynomial_integer_dense_flint(Polynomial): 1 sage: (123456789123456789123456789123456789123456789*t).content() 123456789123456789123456789123456789123456789 + + Verify that :trac:`13053` has been resolved:: + + sage: R(-1).content() + -1 + """ + if self.is_zero(): + return ZZ.zero() + cdef fmpz_t c fmpz_init(c) + fmpz_poly_get_coeff_fmpz(c, self.__poly, fmpz_poly_degree(self.__poly)) + cdef int sign = fmpz_sgn(c) + fmpz_poly_content(c, self.__poly) + cdef Integer z = PY_NEW(Integer) fmpz_get_mpz(z.value, c) fmpz_clear(c) - return z + return z if sign == 1 else -z def __reduce__(self): r""" @@ -1091,6 +1105,16 @@ cdef class Polynomial_integer_dense_flint(Polynomial): sage: p = 37 * (x-1)^2 * (x-2)^2 * (x-3)^3 * (x-4) sage: p.squarefree_decomposition() (37) * (x - 4) * (x^2 - 3*x + 2)^2 * (x - 3)^3 + + TESTS: + + Verify that :trac:`13053` has been resolved:: + + sage: R. = PolynomialRing(ZZ, implementation='FLINT') + sage: f=-x^2 + sage: f.squarefree_decomposition() + (-1) * x^2 + """ cdef ZZX_c** v cdef long* e @@ -1100,10 +1124,13 @@ cdef class Polynomial_integer_dense_flint(Polynomial): cdef Integer z cdef Polynomial_integer_dense_flint fac + # the sign of the content is the sign of the leading coefficient z = self.content() if not z.is_one(): fmpz_poly_init(ppart) + # the primitive part returned by FLINT has positive leading + # coefficient fmpz_poly_primitive_part(ppart, self.__poly) fmpz_poly_get_ZZX(ntl_poly, ppart) @@ -1111,6 +1138,7 @@ cdef class Polynomial_integer_dense_flint(Polynomial): else: fmpz_poly_get_ZZX(ntl_poly, self.__poly) + # input is primitive, with positive leading coefficient ZZX_squarefree_decomposition(&v, &e, &n, &ntl_poly) F = [] diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx index 136d4687599..9adf7d91220 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx @@ -835,8 +835,17 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): sage: p = 37 * (x-1)^2 * (x-2)^2 * (x-3)^3 * (x-4) sage: p.squarefree_decomposition() (37) * (x - 4) * (x^2 - 3*x + 2)^2 * (x - 3)^3 - """ + TESTS: + + Verify that :trac:`13053` has been resolved:: + + sage: R. = PolynomialRing(ZZ, implementation='NTL') + sage: f=-x^2 + sage: f.squarefree_decomposition() + (-1) * x^2 + + """ cdef Polynomial_integer_dense_ntl p = self c = p.content() if c != 1: From f6a8bffe962f240ec264e5c47dca961aa6d537cc Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Wed, 25 Jun 2014 10:11:53 +0200 Subject: [PATCH 348/546] trac #16525: broken doctest --- src/sage/combinat/designs/orthogonal_arrays.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 0dbfd9e3860..0a04fe8df20 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -1465,7 +1465,7 @@ def OA_from_PBD(k,n,PBD, check=True): sage: _ = OA_from_PBD(3,6,pbd) Traceback (most recent call last): ... - RuntimeError: The PBD covers a point 8 which is not in {0, .., 5} + RuntimeError: The PBD covers a point 8 which is not in {0, ..., 5} """ # Size of the sets of the PBD K = set(map(len,PBD)) From 39b3ddb105044d3ff0d9b2d095ba347cca19f85f Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Mon, 23 Jun 2014 01:28:32 +0100 Subject: [PATCH 349/546] Trac 11474: CremonaDatabase: methods coefficients_and_data(), data_from_coefficients() --- src/sage/databases/cremona.py | 177 +++++++++++++++++++++++----------- 1 file changed, 121 insertions(+), 56 deletions(-) diff --git a/src/sage/databases/cremona.py b/src/sage/databases/cremona.py index 67dfde890b9..965c4e20fd9 100644 --- a/src/sage/databases/cremona.py +++ b/src/sage/databases/cremona.py @@ -771,6 +771,123 @@ def curves(self, N): ret['h3'] = [[1,-1,1,-1568,-4669],int(1),int(6)] return ret + def coefficients_and_data(self, label): + """ + Return the Weierstrass coefficients and other data for the + curve with given label. + + EXAMPLES:: + + sage: c, d = CremonaDatabase().coefficients_and_data('144b1') + sage: c + [0, 0, 0, 6, 7] + sage: d['conductor'] + 144 + sage: d['cremona_label'] + '144b1' + sage: d['rank'] + 0 + sage: d['torsion_order'] + 2 + """ + # There are two possible strings: the Cremona label and the LMFDB label. + # They are distinguished by the presence of a period. + if label.find('.') == -1: + cremona_label = label + lmfdb_label = None + else: + cremona_label = lmfdb_to_cremona(label) + lmfdb_label = label + + N, iso, num = parse_cremona_label(cremona_label) + label = str(N)+iso+str(num) + if self.get_skeleton() == _miniCremonaSkeleton: + q = self.__connection__.cursor().execute("SELECT eqn,rank,tors " \ + + 'FROM t_curve,t_class USING(class) WHERE curve=?', (label,)) + else: + q = self.__connection__.cursor().execute("SELECT eqn,rank,tors," \ + + "deg,gens,cp,om,L,reg,sha FROM t_curve,t_class " \ + + "USING(class) WHERE curve=?",(label,)) + try: + c = q.next() + except StopIteration: + if N < self.largest_conductor(): + message = "There is no elliptic curve with label " + label \ + + " in the database" + elif is_package_installed('database_cremona_ellcurve'): + message = "There is no elliptic curve with label " + label \ + + " in the currently available databases" + else: + message = "There is no elliptic curve with label " \ + + label + " in the default database; try installing " \ + + "the optional package database_cremona_ellcurve which " \ + + "contains the complete Cremona database" + raise ValueError(message) + ainvs = eval(c[0]) + data = {'cremona_label': label, + 'rank': c[1], + 'torsion_order': c[2], + 'conductor': N} + if lmfdb_label: + data['lmfdb_label'] = lmfdb_label + if len(c) > 3: + if num == 1: + data['modular_degree'] = (c[3]) + data['gens'] = eval(c[4]) + data['db_extra'] = list(c[5:]) + elif c[1] == 0: + # we know the rank is 0, so the gens are empty + data['gens'] = [] + return ainvs, data + + def data_from_coefficients(self, ainvs): + """ + Return elliptic curve data for the curve with given + Weierstrass coefficients. + + EXAMPLES:: + + sage: d = CremonaDatabase().data_from_coefficients([0, 0, 1, 3182, -53895]) + sage: d['conductor'] + 137411 + sage: d['cremona_label'] + '137411a1' + sage: d['rank'] + 1 + sage: d['torsion_order'] + 1 + """ + ainvs = str(list(ainvs)) + if self.get_skeleton() == _miniCremonaSkeleton: + q = self.__connection__.cursor().execute("SELECT curve,rank,tors " + + 'FROM t_curve,t_class USING(class) WHERE eqn=?', + (ainvs.replace(' ', ''),)) + else: + q = self.__connection__.cursor().execute("SELECT curve,rank,tors," + + "deg,gens,cp,om,L,reg,sha FROM t_curve,t_class " + + "USING(class) WHERE eqn=?", + (ainvs.replace(' ', ''),)) + try: + c = q.next() + except StopIteration: + raise RuntimeError("There is no elliptic curve with coefficients " + + ainvs + " in the database") + label = str(c[0]) + N, iso, num = parse_cremona_label(label) + data = {'cremona_label': label, + 'rank': c[1], + 'torsion_order': c[2], + 'conductor': N} + if len(c) > 3: + if num == 1: + data['modular_degree'] = (c[3]) + data['gens'] = eval(c[4]) + data['db_extra'] = list(c[5:]) + elif c[1] == 0: + # we know the rank is 0, so the gens are empty + data['gens'] = [] + return data + def elliptic_curve_from_ainvs(self, ainvs): """ Returns the elliptic curve in the database of with minimal @@ -804,13 +921,8 @@ def elliptic_curve_from_ainvs(self, ainvs): ... ValueError: There is no elliptic curve with label 10a1 in the database """ - q = self.__connection__.cursor().execute("SELECT curve FROM t_curve " \ - + "WHERE eqn=?",(str(ainvs).replace(' ',''),)) - try: - return self.elliptic_curve(q.next()[0]) - except StopIteration: - raise RuntimeError("No elliptic curve with ainvs (=%s) "%ainvs \ - + "in the database.") + data = self.data_from_coefficients(ainvs) + return elliptic.EllipticCurve(ainvs, **data) def elliptic_curve(self, label): """ @@ -846,55 +958,8 @@ def elliptic_curve(self, label): sage: c.elliptic_curve('462.f3') Elliptic Curve defined by y^2 + x*y = x^3 - 363*x + 1305 over Rational Field """ - # There are two possible strings: the Cremona label and the LMFDB label. - # They are distinguished by the presence of a period. - if label.find('.') == -1: - cremona_label = label - lmfdb_label = None - else: - cremona_label = lmfdb_to_cremona(label) - lmfdb_label = label - - N, iso, num = parse_cremona_label(cremona_label) - label = str(N)+iso+str(num) - if self.get_skeleton() == _miniCremonaSkeleton: - q = self.__connection__.cursor().execute("SELECT eqn,rank,tors " \ - + 'FROM t_curve,t_class USING(class) WHERE curve=?', (label,)) - else: - q = self.__connection__.cursor().execute("SELECT eqn,rank,tors," \ - + "deg,gens,cp,om,L,reg,sha FROM t_curve,t_class " \ - + "USING(class) WHERE curve=?",(label,)) - try: - c = q.next() - F = elliptic.EllipticCurve(eval(c[0])) - F._set_cremona_label(label) - F._set_rank(c[1]) - F._set_torsion_order(c[2]) - F._set_conductor(N) - if lmfdb_label: - F._lmfdb_label = lmfdb_label - if len(c) > 3: - if num == 1: - F._set_modular_degree(c[3]) - F._set_gens(eval(c[4])) - F.db_extra = list(c[5:]) - elif c[1] == 0: - # we know the rank is 0, so the gens are empty - F._set_gens([]) - return F - except StopIteration: - if N < self.largest_conductor(): - message = "There is no elliptic curve with label " + label \ - + " in the database" - elif is_package_installed('database_cremona_ellcurve'): - message = "There is no elliptic curve with label " + label \ - + " in the currently available databases" - else: - message = "There is no elliptic curve with label " \ - + label + " in the default database; try installing " \ - + "the optional package database_cremona_ellcurve which " \ - + "contains the complete Cremona database" - raise ValueError(message) + ainvs, data = self.coefficients_and_data(label) + return elliptic.EllipticCurve(ainvs, **data) def iter(self, conductors): """ From 86f1a2b3a168d1c5d57d92ef99b46929ce40e2c9 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Sun, 22 Jun 2014 21:16:06 +0100 Subject: [PATCH 350/546] Trac 11474: construct elliptic curves using a UniqueFactory --- src/sage/categories/homset.py | 2 +- src/sage/doctest/forker.py | 6 +- .../schemes/elliptic_curves/constructor.py | 280 +++++++++++++----- .../schemes/elliptic_curves/ec_database.py | 4 +- .../elliptic_curves/ell_curve_isogeny.py | 20 +- src/sage/schemes/elliptic_curves/ell_field.py | 3 + .../elliptic_curves/ell_finite_field.py | 84 +++--- .../schemes/elliptic_curves/ell_generic.py | 92 ++---- .../elliptic_curves/ell_number_field.py | 30 +- .../elliptic_curves/ell_padic_field.py | 39 +-- .../elliptic_curves/ell_rational_field.py | 134 ++++++--- src/sage/schemes/elliptic_curves/heegner.py | 10 +- .../schemes/elliptic_curves/padic_lseries.py | 1 + .../schemes/elliptic_curves/period_lattice.py | 2 +- 14 files changed, 386 insertions(+), 321 deletions(-) diff --git a/src/sage/categories/homset.py b/src/sage/categories/homset.py index 79dbcd7cf33..f63e972e416 100644 --- a/src/sage/categories/homset.py +++ b/src/sage/categories/homset.py @@ -668,7 +668,7 @@ def __hash__(self): sage: E = EllipticCurve('37a') sage: H = E(0).parent(); H Abelian group of points on Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field - sage: hash(H) + sage: hash(H) # random output -1145411691 # 32-bit -8446824869798451307 # 64-bit """ diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 2273d434a82..aee36f0005c 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -1205,13 +1205,13 @@ def report_unexpected_exception(self, out, test, example, exc_info): sage: _ = sage0.eval("DTR = sdf.SageDocTestRunner(SageOutputChecker(), verbose=False, sage_options=DD, optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS)") sage: sage0._prompt = r"\(Pdb\) " sage: sage0.eval("DTR.run(DT, clear_globs=False)") # indirect doctest - '... ArithmeticError("Invariants %s define a singular curve."%ainvs)' + '... ArithmeticError("invariants " + str(ainvs) + " define a singular curve")' sage: sage0.eval("l") '...if self.discriminant() == 0:...raise ArithmeticError...' sage: sage0.eval("u") - '...EllipticCurve_field.__init__(self, [field(x) for x in ainvs])' + '...EllipticCurve_field.__init__(self, K, ainvs)' sage: sage0.eval("p ainvs") - '[0, 0]' + '(0, 0, 0, 0, 0)' sage: sage0._prompt = "sage: " sage: sage0.eval("quit") 'TestResults(failed=1, attempted=1)' diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index b0b14738e15..ad9a353ac3a 100644 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -40,15 +40,17 @@ from sage.structure.sequence import Sequence from sage.structure.element import parent +from sage.structure.factory import UniqueFactory from sage.symbolic.ring import SR from sage.symbolic.expression import is_SymbolicEquation -def EllipticCurve(x=None, y=None, j=None, minimal_twist=True): +class EllipticCurveFactory(UniqueFactory): r""" Construct an elliptic curve. - In Sage, an elliptic curve is always specified by its a-invariants + In Sage, an elliptic curve is always specified by a long + Weierstrass equation .. math:: @@ -288,81 +290,175 @@ def EllipticCurve(x=None, y=None, j=None, minimal_twist=True): TypeError: invalid input to EllipticCurve constructor """ - import ell_generic, ell_field, ell_finite_field, ell_number_field, ell_rational_field, ell_padic_field # here to avoid circular includes - - if j is not None: - if not x is None: - if is_Ring(x): + def create_key_and_extra_args(self, x=None, y=None, j=None, minimal_twist=True, **kwds): + """ + Return a ``UniqueFactory`` key and possibly extra parameters. + + INPUT: + + See the documentation for :class:`EllipticCurveFactory`. + + OUTPUT: + + A pair ``(key, extra_args)``: + + - ``key`` has the form `(R, (a_1, a_2, a_3, a_4, a_6))`, + representing a ring and the Weierstrass coefficients of an + elliptic curve over that ring; + + - ``extra_args`` is a dictionary containing additional data to + be inserted into the elliptic curve structure. + + EXAMPLES:: + + sage: EllipticCurve.create_key_and_extra_args(j=8000) + ((Rational Field, (0, -1, 0, -3, -1)), {}) + + When constructing a curve over `\\QQ` from a Cremona or LMFDB + label, the invariants from the database are returned as + ``extra_args``:: + + sage: key, data = EllipticCurve.create_key_and_extra_args('389.a1') + sage: key + (Rational Field, (0, 1, 1, -2, 0)) + sage: data['conductor'] + 389 + sage: data['cremona_label'] + '389a1' + sage: data['lmfdb_label'] + '389.a1' + sage: data['rank'] + 2 + sage: data['torsion_order'] + 1 + + User-specified keywords are also included in ``extra_args``:: + + sage: key, data = EllipticCurve.create_key_and_extra_args((0, 0, 1, -23737, 960366), rank=4) + sage: data['rank'] + 4 + + Furthermore, keywords takes precedence over data from the + database, which can be used to specify an alternative set of + generators for the Mordell-Weil group:: + + sage: key, data = EllipticCurve.create_key_and_extra_args('5077a1', gens=[[1, -1], [-2, 3], [4, -7]]) + sage: data['gens'] + [[1, -1], [-2, 3], [4, -7]] + sage: E = EllipticCurve.create_object(0, key, **data) + sage: E.gens() + [(-2 : 3 : 1), (1 : -1 : 1), (4 : -7 : 1)] + + Note that elliptic curves are equal if and only they have the + same base ring and Weierstrass equation; the data in + ``extra_args`` do not influence comparison of elliptic curves. + A consequence of this is that passing keyword arguments only + works when constructing an elliptic curve the first time: + + sage: E = EllipticCurve('5077a1', gens=[[1, -1], [-2, 3], [4, -7]]) + sage: E.gens() + [(-2 : 3 : 1), (1 : -1 : 1), (4 : -7 : 1)] + sage: E = EllipticCurve('5077a1', gens=[[-2, 3], [-1, 3], [0, 2]]) + sage: E.gens() + [(-2 : 3 : 1), (1 : -1 : 1), (4 : -7 : 1)] + + .. WARNING:: + + Manually specifying extra data is almost never necessary + and is not guaranteed to have any effect, as the above + example shows. Almost no checking is done, so specifying + incorrect data may lead to wrong results of computations + instead of errors or warnings. + + """ + R = None + if is_Ring(x): + (R, x) = (x, y) + + if j is not None: + if R is not None: try: - j = x(j) + j = R(j) except (ZeroDivisionError, ValueError, TypeError): - raise ValueError("First parameter must be a ring containing %s"%j) - else: + raise ValueError("First parameter must be a ring containing %s" % j) + elif x is not None: raise ValueError("First parameter (if present) must be a ring when j is specified") - return EllipticCurve_from_j(j, minimal_twist) + x = coefficients_from_j(j, minimal_twist) - if x is None: - raise TypeError("invalid input to EllipticCurve constructor") + if is_SymbolicEquation(x): + x = x.lhs() - x.rhs() - if is_SymbolicEquation(x): - x = x.lhs() - x.rhs() + if parent(x) is SR: + x = x._polynomial_(rings.QQ['x', 'y']) - if parent(x) is SR: - x = x._polynomial_(rings.QQ['x', 'y']) + if is_MPolynomial(x): + if y is None: + x = coefficients_from_Weierstrass_polynomial(x) + else: + x = coefficients_from_cubic(x, y, morphism=False) - if is_MPolynomial(x): - if y is None: - return EllipticCurve_from_Weierstrass_polynomial(x) - else: - return EllipticCurve_from_cubic(x, y, morphism=False) + if isinstance(x, basestring): + # Interpret x as a Cremona or LMFDB label. + from sage.databases.cremona import CremonaDatabase + x, data = CremonaDatabase().coefficients_and_data(x) + # User-provided keywords may override database entries. + data.update(kwds) + kwds = data - if is_Ring(x): - if is_RationalField(x): - return ell_rational_field.EllipticCurve_rational_field(x, y) - elif is_FiniteField(x) or (is_IntegerModRing(x) and x.characteristic().is_prime()): - return ell_finite_field.EllipticCurve_finite_field(x, y) - elif rings.is_pAdicField(x): - return ell_padic_field.EllipticCurve_padic_field(x, y) - elif is_NumberField(x): - return ell_number_field.EllipticCurve_number_field(x, y) - elif x in _Fields: - return ell_field.EllipticCurve_field(x, y) - return ell_generic.EllipticCurve_generic(x, y) + if not isinstance(x, (list, tuple)): + raise TypeError("invalid input to EllipticCurve constructor") - if isinstance(x, unicode): - x = str(x) + if len(x) == 2: + x = (0, 0, 0, x[0], x[1]) + elif len(x) != 5: + raise ValueError("sequence of coefficients must have length 2 or 5") - if isinstance(x, basestring): - return ell_rational_field.EllipticCurve_rational_field(x) + if R is None: + R = Sequence(x).universe() + if R in (rings.ZZ, int, long): + R = rings.QQ - if is_RingElement(x) and y is None: - raise TypeError("invalid input to EllipticCurve constructor") + return (R, tuple(R(a) for a in x)), kwds - if not isinstance(x, (list, tuple)): - raise TypeError("invalid input to EllipticCurve constructor") + def create_object(self, version, key, **kwds): + """ + Create an object from a ``UniqueFactory`` key. - x = Sequence(x) - if not (len(x) in [2,5]): - raise ValueError("sequence of coefficients must have length 2 or 5") - R = x.universe() + EXAMPLES:: - if isinstance(x[0], (rings.Rational, rings.Integer, int, long)): - return ell_rational_field.EllipticCurve_rational_field(x, y) + sage: E = EllipticCurve.create_object(0, (GF(3), (1, 2, 0, 1, 2))) + sage: type(E) + - elif is_NumberField(R): - return ell_number_field.EllipticCurve_number_field(x, y) + .. NOTE:: - elif rings.is_pAdicField(R): - return ell_padic_field.EllipticCurve_padic_field(x, y) + Keyword arguments are currently only passed to the + constructor for elliptic curves over `\\QQ`; elliptic + curves over other fields do not support them. - elif is_FiniteField(R) or (is_IntegerModRing(R) and R.characteristic().is_prime()): - return ell_finite_field.EllipticCurve_finite_field(x, y) + """ + R, x = key - elif R in _Fields: - return ell_field.EllipticCurve_field(x, y) + if R is rings.QQ: + from ell_rational_field import EllipticCurve_rational_field + return EllipticCurve_rational_field(x, **kwds) + elif is_NumberField(R): + from ell_number_field import EllipticCurve_number_field + return EllipticCurve_number_field(R, x) + elif rings.is_pAdicField(R): + from ell_padic_field import EllipticCurve_padic_field + return EllipticCurve_padic_field(R, x) + elif is_FiniteField(R) or (is_IntegerModRing(R) and R.characteristic().is_prime()): + from ell_finite_field import EllipticCurve_finite_field + return EllipticCurve_finite_field(R, x) + elif R in _Fields: + from ell_field import EllipticCurve_field + return EllipticCurve_field(R, x) + from ell_generic import EllipticCurve_generic + return EllipticCurve_generic(R, x) - return ell_generic.EllipticCurve_generic(x, y) +EllipticCurve = EllipticCurveFactory('sage.schemes.elliptic_curves.constructor.EllipticCurve') def EllipticCurve_from_Weierstrass_polynomial(f): @@ -404,7 +500,21 @@ def EllipticCurve_from_Weierstrass_polynomial(f): sage: from sage.schemes.elliptic_curves.constructor import EllipticCurve_from_Weierstrass_polynomial sage: EllipticCurve_from_Weierstrass_polynomial(-w^2 + z^3 + 1) Elliptic Curve defined by y^2 = x^3 + 1 over Rational Field - """ + """ + return EllipticCurve(coefficients_from_Weierstrass_polynomial(f)) + +def coefficients_from_Weierstrass_polynomial(f): + """ + Return the coefficients `(a_1, a_2, a_3, a_4, a_5)` for a cubic in + Weierstrass form. + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.constructor import coefficients_from_Weierstrass_polynomial + sage: R. = QQ[] + sage: coefficients_from_Weierstrass_polynomial(-w^2 + z^3 + 1) + [0, 0, 0, 0, 1] + """ R = f.parent() cubic_variables = [ x for x in R.gens() if f.degree(x) == 3 ] quadratic_variables = [ y for y in R.gens() if f.degree(y) == 2 ] @@ -438,7 +548,7 @@ def EllipticCurve_from_Weierstrass_polynomial(f): raise ValueError('the coefficient of x^3 and -y^2 must be the same') elif x3 != 1: a1, a2, a3, a4, a6 = a1/x3, a2/x3, a3/x3, a4/x3, a6/x3 - return EllipticCurve([a1, a2, a3, a4, a6]) + return [a1, a2, a3, a4, a6] def EllipticCurve_from_c4c6(c4, c6): @@ -516,6 +626,36 @@ def EllipticCurve_from_j(j, minimal_twist=True): True """ + return EllipticCurve(coefficients_from_j(j, minimal_twist)) + +def coefficients_from_j(j, minimal_twist=True): + """ + Return Weierstrass coefficients `(a_1, a_2, a_3, a_4, a_6)` for an + elliptic curve with given `j`-invariant. + + INPUT: + + See :func:`EllipticCurve_from_j`. + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.constructor import coefficients_from_j + sage: coefficients_from_j(0) + [0, 0, 1, 0, 0] + sage: coefficients_from_j(1728) + [0, 0, 0, -1, 0] + sage: coefficients_from_j(1) + [1, 0, 0, 36, 3455] + + The ``minimal_twist`` parameter (ignored except over `\\QQ` and + True by default) controls whether or not a minimal twist is + computed:: + + sage: coefficients_from_j(100) + [0, 1, 0, 3392, 307888] + sage: coefficients_from_j(100, minimal_twist=False) + [0, 0, 0, 488400, -530076800] + """ try: K = j.parent() except AttributeError: @@ -526,26 +666,26 @@ def EllipticCurve_from_j(j, minimal_twist=True): char=K.characteristic() if char==2: if j == 0: - return EllipticCurve(K, [ 0, 0, 1, 0, 0 ]) + return Sequence([0, 0, 1, 0, 0], universe=K) else: - return EllipticCurve(K, [ 1, 0, 0, 0, 1/j ]) + return Sequence([1, 0, 0, 0, 1/j], universe=K) if char == 3: if j==0: - return EllipticCurve(K, [ 0, 0, 0, 1, 0 ]) + return Sequence([0, 0, 0, 1, 0], universe=K) else: - return EllipticCurve(K, [ 0, j, 0, 0, -j**2 ]) + return Sequence([0, j, 0, 0, -j**2], universe=K) if K is rings.RationalField(): # we construct the minimal twist, i.e. the curve with minimal # conductor with this j_invariant: if j == 0: - return EllipticCurve(K, [ 0, 0, 1, 0, 0 ]) # 27a3 + return Sequence([0, 0, 1, 0, 0], universe=K) # 27a3 if j == 1728: - return EllipticCurve(K, [ 0, 0, 0, -1, 0 ]) # 32a2 + return Sequence([0, 0, 0, -1, 0], universe=K) # 32a2 if not minimal_twist: k=j-1728 - return EllipticCurve(K, [0,0,0,-3*j*k, -2*j*k**2]) + return Sequence([0, 0, 0, -3*j*k, -2*j*k**2], universe=K) n = j.numerator() m = n-1728*j.denominator() @@ -567,15 +707,15 @@ def EllipticCurve_from_j(j, minimal_twist=True): Elist = [E1] + [E1.quadratic_twist(t) for t in tw] crv_cmp = lambda E,F: cmp(E.conductor(),F.conductor()) Elist.sort(cmp=crv_cmp) - return Elist[0] + return Sequence(Elist[0].ainvs()) # defaults for all other fields: if j == 0: - return EllipticCurve(K, [ 0, 0, 0, 0, 1 ]) + return Sequence([0, 0, 0, 0, 1], universe=K) if j == 1728: - return EllipticCurve(K, [ 0, 0, 0, 1, 0 ]) + return Sequence([0, 0, 0, 1, 0], universe=K) k=j-1728 - return EllipticCurve(K, [0,0,0,-3*j*k, -2*j*k**2]) + return Sequence([0, 0, 0, -3*j*k, -2*j*k**2], universe=K) def EllipticCurve_from_cubic(F, P, morphism=True): diff --git a/src/sage/schemes/elliptic_curves/ec_database.py b/src/sage/schemes/elliptic_curves/ec_database.py index f4d0970b408..f812b3ddce5 100644 --- a/src/sage/schemes/elliptic_curves/ec_database.py +++ b/src/sage/schemes/elliptic_curves/ec_database.py @@ -35,7 +35,7 @@ import os -from ell_rational_field import (EllipticCurve_rational_field) +from constructor import EllipticCurve class EllipticCurves: def rank(self, rank, tors=0, n=10, labels=False): @@ -98,7 +98,7 @@ def rank(self, rank, tors=0, n=10, labels=False): if labels: v.append(label) else: - E = EllipticCurve_rational_field(eval(ainvs)) + E = EllipticCurve(eval(ainvs)) E._set_rank(r) E._set_torsion_order(t) E._set_conductor(N) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index 5ef333d27bc..da33f19a8fe 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -59,7 +59,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from copy import deepcopy, copy +from copy import copy from sage.categories import homset @@ -1174,7 +1174,7 @@ def __neg__(self): kernel_list = self.__kernel_list self.__kernel_list = None - output = deepcopy(self) + output = copy(self) # reset the kernel lists output.__kernel_list = copy(kernel_list) @@ -3771,13 +3771,9 @@ def compute_intermediate_curves(E1, E2): sage: compute_intermediate_curves(E, E2) (Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1, Elliptic Curve defined by y^2 = x^3 + 16*x over Number Field in i with defining polynomial x^2 + 1, - Generic morphism: - From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1 - To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1 + Generic endomorphism of Abelian group of points on Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1 Via: (u,r,s,t) = (1, 0, 0, 0), - Generic morphism: - From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 16*x over Number Field in i with defining polynomial x^2 + 1 - To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 16*x over Number Field in i with defining polynomial x^2 + 1 + Generic endomorphism of Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 16*x over Number Field in i with defining polynomial x^2 + 1 Via: (u,r,s,t) = (1, 0, 0, 0)) """ @@ -3847,13 +3843,9 @@ def compute_sequence_of_maps(E1, E2, ell): sage: E = EllipticCurve(K, [0,0,0,1,0]) sage: E2 = EllipticCurve(K, [0,0,0,16,0]) sage: compute_sequence_of_maps(E, E2, 4) - (Generic morphism: - From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1 - To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1 + (Generic endomorphism of Abelian group of points on Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1 Via: (u,r,s,t) = (1, 0, 0, 0), - Generic morphism: - From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 16*x over Number Field in i with defining polynomial x^2 + 1 - To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 16*x over Number Field in i with defining polynomial x^2 + 1 + Generic endomorphism of Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 16*x over Number Field in i with defining polynomial x^2 + 1 Via: (u,r,s,t) = (1, 0, 0, 0), Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1, Elliptic Curve defined by y^2 = x^3 + 16*x over Number Field in i with defining polynomial x^2 + 1, diff --git a/src/sage/schemes/elliptic_curves/ell_field.py b/src/sage/schemes/elliptic_curves/ell_field.py index 47cb4472e25..4c53147ba71 100644 --- a/src/sage/schemes/elliptic_curves/ell_field.py +++ b/src/sage/schemes/elliptic_curves/ell_field.py @@ -19,6 +19,7 @@ from sage.rings.complex_field import is_ComplexField from sage.rings.real_mpfr import is_RealField from constructor import EllipticCurve +from sage.schemes.elliptic_curves.ell_point import EllipticCurvePoint_field from ell_curve_isogeny import EllipticCurveIsogeny, isogeny_codomain_from_kernel @@ -26,6 +27,8 @@ class EllipticCurve_field(ell_generic.EllipticCurve_generic): base_field = ell_generic.EllipticCurve_generic.base_ring + _point = EllipticCurvePoint_field + # Twists: rewritten by John Cremona as follows: # # Quadratic twist allowed except when char=2, j=0 diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 98a2840e887..f671a3c4bb5 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -50,59 +50,43 @@ class EllipticCurve_finite_field(EllipticCurve_field, HyperellipticCurve_finite_field): """ Elliptic curve over a finite field. - """ - def __init__(self, x, y=None): - """ - Special constructor for elliptic curves over a finite field - - EXAMPLES:: - - sage: EllipticCurve(GF(101),[2,3]) - Elliptic Curve defined by y^2 = x^3 + 2*x + 3 over Finite Field of size 101 - :: - - sage: F=GF(101^2, 'a') - sage: EllipticCurve([F(2),F(3)]) - Elliptic Curve defined by y^2 = x^3 + 2*x + 3 over Finite Field in a of size 101^2 - - Elliptic curves over `\ZZ/N\ZZ` with `N` prime are of type - "elliptic curve over a finite field":: - - sage: F = Zmod(101) - sage: EllipticCurve(F, [2, 3]) - Elliptic Curve defined by y^2 = x^3 + 2*x + 3 over Ring of integers modulo 101 - sage: E = EllipticCurve([F(2), F(3)]) - sage: type(E) - - sage: E.category() - Category of schemes over Ring of integers modulo 101 - - Elliptic curves over `\ZZ/N\ZZ` with `N` composite are of type - "generic elliptic curve":: - - sage: F = Zmod(95) - sage: EllipticCurve(F, [2, 3]) - Elliptic Curve defined by y^2 = x^3 + 2*x + 3 over Ring of integers modulo 95 - sage: E = EllipticCurve([F(2), F(3)]) - sage: type(E) - - sage: E.category() - Category of schemes over Ring of integers modulo 95 - sage: TestSuite(E).run(skip=["_test_elements"]) - """ - if isinstance(x, list): - seq = Sequence(x) - else: - seq = Sequence(y, universe=x) - ainvs = list(seq) - field = seq.universe() - if not isinstance(field, ring.Ring): - raise TypeError + EXAMPLES:: - EllipticCurve_field.__init__(self, ainvs) + sage: EllipticCurve(GF(101),[2,3]) + Elliptic Curve defined by y^2 = x^3 + 2*x + 3 over Finite Field of size 101 + + sage: F=GF(101^2, 'a') + sage: EllipticCurve([F(2),F(3)]) + Elliptic Curve defined by y^2 = x^3 + 2*x + 3 over Finite Field in a of size 101^2 + + Elliptic curves over `\ZZ/N\ZZ` with `N` prime are of type + "elliptic curve over a finite field":: + + sage: F = Zmod(101) + sage: EllipticCurve(F, [2, 3]) + Elliptic Curve defined by y^2 = x^3 + 2*x + 3 over Ring of integers modulo 101 + sage: E = EllipticCurve([F(2), F(3)]) + sage: type(E) + + sage: E.category() + Category of schemes over Ring of integers modulo 101 + + Elliptic curves over `\ZZ/N\ZZ` with `N` composite are of type + "generic elliptic curve":: + + sage: F = Zmod(95) + sage: EllipticCurve(F, [2, 3]) + Elliptic Curve defined by y^2 = x^3 + 2*x + 3 over Ring of integers modulo 95 + sage: E = EllipticCurve([F(2), F(3)]) + sage: type(E) + + sage: E.category() + Category of schemes over Ring of integers modulo 95 + sage: TestSuite(E).run(skip=["_test_elements"]) + """ - self._point = ell_point.EllipticCurvePoint_finite_field + _point = ell_point.EllipticCurvePoint_finite_field def plot(self, *args, **kwds): """ diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 3604674a3bb..53e27de3228 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -63,6 +63,7 @@ from sage.rings.number_field.number_field_base import is_NumberField import sage.misc.misc as misc from sage.misc.cachefunc import cached_method, cached_function +from sage.misc.fast_methods import WithEqualityById # Schemes import sage.schemes.projective.projective_space as projective_space @@ -101,7 +102,7 @@ def is_EllipticCurve(x): """ return isinstance(x, EllipticCurve_generic) -class EllipticCurve_generic(plane_curve.ProjectiveCurve_generic): +class EllipticCurve_generic(WithEqualityById, plane_curve.ProjectiveCurve_generic): r""" Elliptic curve over a generic base ring. @@ -116,18 +117,22 @@ class EllipticCurve_generic(plane_curve.ProjectiveCurve_generic): sage: -5*P (179051/80089 : -91814227/22665187 : 1) """ - def __init__(self, ainvs, extra=None): + def __init__(self, K, ainvs): r""" - Constructor from `a`-invariants (long or short Weierstrass coefficients). + Construct an elliptic curve from Weierstrass `a`-coefficients. INPUT: - - ``ainvs`` (list) -- either `[a_1,a_2,a_3,a_4,a_6]` or - `[a_4,a_6]` (with `a_1=a_2=a_3=0` in the second case). + - ``K`` -- a ring - .. note:: + - ``ainvs`` -- a list or tuple `[a_1, a_2, a_3, a_4, a_6]` of + Weierstrass coefficients. + + .. NOTE:: - See constructor.py for more variants. + This class should not be called directly; use + :class:`sage.constructor.EllipticCurve` to construct + elliptic curves. EXAMPLES:: @@ -146,41 +151,26 @@ def __init__(self, ainvs, extra=None): sage: EllipticCurve(IntegerModRing(91),[1,2,3,4,5]) Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Ring of integers modulo 91 """ - if extra is not None: # possibility of two arguments - K, ainvs = ainvs, extra - else: - K = ainvs[0].parent() - assert len(ainvs) == 2 or len(ainvs) == 5 self.__base_ring = K - ainvs = [K(x) for x in ainvs] - if len(ainvs) == 2: - ainvs = [K(0),K(0),K(0)] + ainvs - self.__ainvs = tuple(ainvs) + self.__ainvs = tuple(K(a) for a in ainvs) if self.discriminant() == 0: - raise ArithmeticError("Invariants %s define a singular curve."%ainvs) + raise ArithmeticError("invariants " + str(ainvs) + " define a singular curve") PP = projective_space.ProjectiveSpace(2, K, names='xyz'); x, y, z = PP.coordinate_ring().gens() a1, a2, a3, a4, a6 = ainvs f = y**2*z + (a1*x + a3*z)*y*z \ - (x**3 + a2*x**2*z + a4*x*z**2 + a6*z**3) plane_curve.ProjectiveCurve_generic.__init__(self, PP, f) - # TODO: cleanup, are these two point classes redundant? # See #1975: we deliberately set the class to # EllipticCurvePoint_finite_field for finite rings, so that we # can do some arithmetic on points over Z/NZ, for teaching # purposes. - from sage.rings.finite_rings.constructor import is_FiniteField from sage.rings.finite_rings.integer_mod_ring import is_IntegerModRing - if is_FiniteField(K) or is_IntegerModRing(K): - self._morphism = self._point = ell_point.EllipticCurvePoint_finite_field - elif K.is_field(): - if is_NumberField(K): - self._morphism = self._point = ell_point.EllipticCurvePoint_number_field - else: - self._morphism = self._point = ell_point.EllipticCurvePoint_field - else: - self._morphism = self._point = ell_point.EllipticCurvePoint + if is_IntegerModRing(K): + self._point = ell_point.EllipticCurvePoint_finite_field + + _point = ell_point.EllipticCurvePoint def _defining_params_(self): r""" @@ -198,19 +188,6 @@ def _defining_params_(self): """ return (self.__base_ring, list(self.__ainvs)) - def __hash__(self): - """ - TESTS:: - - sage: E = EllipticCurve('37a') - sage: hash(E) - -1437250549 # 32-bit - -2189969105152029685 # 64-bit - sage: hash(E) != hash(E.change_ring(GF(7))) - True - """ - return hash((self.__base_ring, self.__ainvs)) - def _repr_(self): """ String representation of elliptic curve. @@ -427,25 +404,6 @@ def _symbolic_(self, SR): x, y = SR.var('x, y') return y**2 + a[0]*x*y + a[2]*y == x**3 + a[1]*x**2 + a[3]*x + a[4] - def __cmp__(self, other): - """ - Standard comparison function for elliptic curves, to allow sorting - and equality testing. - - EXAMPLES:: - - sage: E=EllipticCurve(QQ,[1,1]) - sage: F=EllipticCurve(QQ,[0,0,0,1,1]) - sage: E==F - True - """ - if not isinstance(other, EllipticCurve_generic): - return -1 - t = cmp(self.base_ring(), other.base_ring()) - if t: - return t - return cmp(self.ainvs(), other.ainvs()) - def __contains__(self, P): """ Returns True if and only if P is a point on the elliptic curve. P @@ -2301,19 +2259,11 @@ def isomorphisms(self, other, field=None): sage: E = EllipticCurve_from_j(QQ(0)) # a curve with j=0 over QQ sage: F = EllipticCurve('27a3') # should be the same one sage: E.isomorphisms(F); - [Generic morphism: - From: Abelian group of points on Elliptic Curve defined - by y^2 + y = x^3 over Rational Field - To: Abelian group of points on Elliptic Curve defined - by y^2 + y = x^3 over Rational Field - Via: (u,r,s,t) = (-1, 0, 0, -1), Generic morphism: - From: Abelian group of points on Elliptic Curve defined - by y^2 + y = x^3 over Rational Field - To: Abelian group of points on Elliptic Curve defined - by y^2 + y = x^3 over Rational Field + [Generic endomorphism of Abelian group of points on Elliptic Curve defined by y^2 + y = x^3 over Rational Field + Via: (u,r,s,t) = (-1, 0, 0, -1), + Generic endomorphism of Abelian group of points on Elliptic Curve defined by y^2 + y = x^3 over Rational Field Via: (u,r,s,t) = (1, 0, 0, 0)] - We can also find isomorphisms defined over extension fields:: sage: E=EllipticCurve(GF(7),[0,0,0,1,1]) diff --git a/src/sage/schemes/elliptic_curves/ell_number_field.py b/src/sage/schemes/elliptic_curves/ell_number_field.py index 6ed45de5107..2425f34cc57 100644 --- a/src/sage/schemes/elliptic_curves/ell_number_field.py +++ b/src/sage/schemes/elliptic_curves/ell_number_field.py @@ -119,15 +119,8 @@ class EllipticCurve_number_field(EllipticCurve_field): sage: EllipticCurve([i, i - 1, i + 1, 24*i + 15, 14*i + 35]) Elliptic Curve defined by y^2 + i*x*y + (i+1)*y = x^3 + (i-1)*x^2 + (24*i+15)*x + (14*i+35) over Number Field in i with defining polynomial x^2 + 1 """ - def __init__(self, x, y=None): + def __init__(self, K, ainvs): r""" - Allow some ways to create an elliptic curve over a number - field in addition to the generic ones. - - INPUT: - - - ``x``, ``y`` -- see examples. - EXAMPLES: A curve from the database of curves over `\QQ`, but over a larger field: @@ -142,25 +135,10 @@ def __init__(self, x, y=None): Elliptic Curve defined by y^2 + y = x^3 + (-1)*x^2 over Number Field in i with defining polynomial x^2 + 1 """ - if y is None: - if isinstance(x, list): - ainvs = x - field = ainvs[0].parent() - else: - if isinstance(y, str): - from sage.databases.cremona import CremonaDatabase - field = x - X = CremonaDatabase()[y] - ainvs = list(X.a_invariants()) - else: - field = x - ainvs = y - if not (isinstance(field, Ring) and isinstance(ainvs,list)): - raise TypeError - - EllipticCurve_field.__init__(self, [field(x) for x in ainvs]) - self._point = ell_point.EllipticCurvePoint_number_field self._known_points = [] + EllipticCurve_field.__init__(self, K, ainvs) + + _point = ell_point.EllipticCurvePoint_number_field def base_extend(self, R): """ diff --git a/src/sage/schemes/elliptic_curves/ell_padic_field.py b/src/sage/schemes/elliptic_curves/ell_padic_field.py index 9ca4d8d94d5..e7b226283d1 100644 --- a/src/sage/schemes/elliptic_curves/ell_padic_field.py +++ b/src/sage/schemes/elliptic_curves/ell_padic_field.py @@ -35,38 +35,19 @@ class EllipticCurve_padic_field(EllipticCurve_field, HyperellipticCurve_padic_field): """ Elliptic curve over a padic field. + + EXAMPLES:: + + sage: Qp=pAdicField(17) + sage: E=EllipticCurve(Qp,[2,3]); E + Elliptic Curve defined by y^2 = x^3 + (2+O(17^20))*x + (3+O(17^20)) over 17-adic Field with capped relative precision 20 + sage: E == loads(dumps(E)) + True """ - def __init__(self, x, y=None): - """ - Constructor from [a1,a2,a3,a4,a6] or [a4,a6]. - EXAMPLES:: + _point = ell_point.EllipticCurvePoint_field - sage: Qp=pAdicField(17) - sage: E=EllipticCurve(Qp,[2,3]); E - Elliptic Curve defined by y^2 = x^3 + (2+O(17^20))*x + (3+O(17^20)) over 17-adic Field with capped relative precision 20 - sage: E == loads(dumps(E)) - True - """ - if y is None: - if isinstance(x, list): - ainvs = x - field = ainvs[0].parent() - else: - if isinstance(y, str): - field = x - X = sage.databases.cremona.CremonaDatabase()[y] - ainvs = [field(a) for a in X.a_invariants()] - else: - field = x - ainvs = y - if not (isinstance(field, ring.Ring) and isinstance(ainvs,list)): - raise TypeError - - EllipticCurve_field.__init__(self, [field(x) for x in ainvs]) - - self._point = ell_point.EllipticCurvePoint_field - self._genus = 1 + _genus = 1 def frobenius(self, P=None): """ diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index cc2226e88e1..c7b583fb753 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -144,19 +144,20 @@ class EllipticCurve_rational_field(EllipticCurve_number_field): Elliptic Curve defined by y^2 + y = x^3 + x^2 - 2*x over Rational Field """ - def __init__(self, ainvs, extra=None): + def __init__(self, ainvs, **kwds): r""" Constructor for the EllipticCurve_rational_field class. INPUT: - - ``ainvs`` (list or string) -- either `[a_1,a_2,a_3,a_4,a_6]` - or `[a_4,a_6]` (with `a_1=a_2=a_3=0`) or a valid label from - the database. + - ``ainvs`` -- a list or tuple `[a_1, a_2, a_3, a_4, a_6]` of + Weierstrass coefficients. - .. note:: + .. NOTE:: - See constructor.py for more variants. + This class should not be called directly; use + :class:`sage.constructor.EllipticCurve` to construct + elliptic curves. EXAMPLES:: @@ -190,8 +191,6 @@ def __init__(self, ainvs, extra=None): [True, True] """ - if extra is not None: # possibility of two arguments (the first would be the field) - ainvs = extra self.__np = {} self.__gens = {} self.__rank = {} @@ -199,27 +198,24 @@ def __init__(self, ainvs, extra=None): self.__generalized_modular_degree = {} self.__generalized_congruence_number = {} self._isoclass = {} - if isinstance(ainvs, str): - label = ainvs - X = sage.databases.cremona.CremonaDatabase()[label] - EllipticCurve_number_field.__init__(self, Q, list(X.a_invariants())) - for attr in ['rank', 'torsion_order', 'cremona_label', 'conductor', - 'modular_degree', 'gens', 'regulator']: - s = "_EllipticCurve_rational_field__"+attr - if hasattr(X,s): - if attr == 'gens': # see #10999 - gens_dict = getattr(X, s) - for boo in gens_dict.keys(): - gens_dict[boo] = [self(P) for P in gens_dict[boo]] - setattr(self, s, gens_dict) - else: - setattr(self, s, getattr(X, s)) - if hasattr(X,'_lmfdb_label'): - self._lmfdb_label = X._lmfdb_label - return EllipticCurve_number_field.__init__(self, Q, ainvs) - if self.base_ring() != Q: - raise TypeError("Base field (=%s) must be the Rational Field."%self.base_ring()) + + if 'conductor' in kwds: + self._set_conductor(kwds['conductor']) + if 'cremona_label' in kwds: + self._set_cremona_label(kwds['cremona_label']) + if 'gens' in kwds: + self._set_gens(kwds['gens']) + if 'lmfdb_label' in kwds: + self._lmfdb_label = kwds['lmfdb_label'] + if 'modular_degree' in kwds: + self._set_modular_degree(kwds['modular_degree']) + if 'rank' in kwds: + self._set_rank(kwds['rank']) + if 'regulator' in kwds: + self.__regulator[True] = kwds['regulator'] + if 'torsion_order' in kwds: + self._set_torsion_order(kwds['torsion_order']) def _set_rank(self, r): """ @@ -279,11 +275,11 @@ def _set_cremona_label(self, L): sage: E._set_cremona_label('bogus') sage: E.label() 'bogus' - sage: E.database_curve().label() + sage: label = E.database_attributes()['cremona_label']; label '37a1' sage: E.label() # no change 'bogus' - sage: E._set_cremona_label(E.database_curve().label()) + sage: E._set_cremona_label(label) sage: E.label() # now it is correct '37a1' """ @@ -306,6 +302,7 @@ def _set_conductor(self, N): sage: E._set_conductor(99) # bogus value -- not checked sage: E.conductor() # returns bogus cached value 99 + sage: E._set_conductor(37) """ self.__conductor_pari = Integer(N) @@ -326,9 +323,7 @@ def _set_modular_degree(self, deg): sage: E._set_modular_degree(123456789) sage: E.modular_degree() 123456789 - sage: E._set_modular_degree(E.database_curve().modular_degree()) - sage: E.modular_degree() - 1984 + sage: E._set_modular_degree(1984) """ self.__modular_degree = Integer(deg) @@ -634,6 +629,7 @@ def pari_curve(self, prec=None, factor=1): :: sage: E = EllipticCurve('37a1') + sage: _ = E.__dict__.pop('_pari_curve') # clear cached data sage: Epari = E.pari_curve() sage: Epari[14].python().prec() 64 @@ -748,11 +744,51 @@ def pari_mincurve(self, prec=None, factor=1): # self.__min_transform = change return mc + @cached_method + def database_attributes(self): + """ + Return a dictionary containing information about ``self`` in + the elliptic curve database. + + If there is no elliptic curve isomorphic to ``self`` in the + database, a ``RuntimeError`` is raised. + + EXAMPLES:: + + sage: E = EllipticCurve((0, 0, 1, -1, 0)) + sage: data = E.database_attributes() + sage: data['conductor'] + 37 + sage: data['cremona_label'] + '37a1' + sage: data['rank'] + 1 + sage: data['torsion_order'] + 1 + + sage: E = EllipticCurve((8, 13, 21, 34, 55)) + sage: E.database_attributes() + Traceback (most recent call last): + ... + RuntimeError: no database entry for Elliptic Curve defined by y^2 + 8*x*y + 21*y = x^3 + 13*x^2 + 34*x + 55 over Rational Field + + """ + from sage.databases.cremona import CremonaDatabase + ainvs = self.minimal_model().ainvs() + try: + return CremonaDatabase().data_from_coefficients(ainvs) + except RuntimeError: + raise RuntimeError("no database entry for %s" % self) + def database_curve(self): """ Return the curve in the elliptic curve database isomorphic to this curve, if possible. Otherwise raise a RuntimeError exception. + Since :trac:`11474`, this returns exactly the same curve as + :meth:`minimal_model`; the only difference is the additional + work of checking whether the curve is in the database. + EXAMPLES:: sage: E = EllipticCurve([0,1,2,3,4]) @@ -777,7 +813,6 @@ def database_curve(self): raise RuntimeError("Elliptic curve %s not in the database."%self) return self.__database_curve - def Np(self, p): r""" The number of points on `E` modulo `p`. @@ -1459,6 +1494,7 @@ def simon_two_descent(self, verbose=0, lim1=5, lim3=50, limtriv=3, sage: E.simon_two_descent() (1, 1, [(0 : 0 : 1)]) sage: E = EllipticCurve('389a1') + sage: E._known_points = [] # clear cached points sage: E.simon_two_descent() (2, 2, [(5/4 : 5/8 : 1), (-3/4 : 7/8 : 1)]) sage: E = EllipticCurve('5077a1') @@ -1691,9 +1727,10 @@ def rank(self, use_database=False, verbose=False, return self.__rank[True] if use_database: try: - self.__rank[True] = self.database_curve().rank() + self.__rank[True] = self.database_attributes()['rank'] return self.__rank[True] - except (AttributeError, RuntimeError): + except (KeyError, RuntimeError): + # curve not in database, or rank not known pass if not only_use_mwrank: N = self.conductor() @@ -1894,13 +1931,12 @@ def _compute_gens(self, proof, if use_database: try: - E = self.database_curve() + E = self.minimal_model() + data = self.database_attributes() iso = E.isomorphism_to(self) - try: - return [iso(P) for P in E.__gens[True]], True - except (KeyError,AttributeError): # database curve does not have the gens - pass - except (RuntimeError, KeyError): # curve or gens not in database + return [iso(E(P)) for P in data['gens']], True + except (KeyError, RuntimeError): + # curve not in database, or generators not known pass if self.conductor() > 10**7: @@ -2609,7 +2645,7 @@ def minimal_model(self): return self.__minimal_model except AttributeError: F = self.pari_mincurve() - self.__minimal_model = EllipticCurve_rational_field([Q(F[i]) for i in range(5)]) + self.__minimal_model = constructor.EllipticCurve([Q(F[i]) for i in range(5)]) return self.__minimal_model def is_minimal(self): @@ -3543,16 +3579,16 @@ def cremona_label(self, space=False): RuntimeError: Cremona label not known for Elliptic Curve defined by y^2 + y = x^3 - 79*x + 342 over Rational Field. """ try: - if not space: - return self.__cremona_label.replace(' ','') - return self.__cremona_label + label = self.__cremona_label except AttributeError: try: - X = self.database_curve() + label = self.database_attributes()['cremona_label'] except RuntimeError: raise RuntimeError("Cremona label not known for %s."%self) - self.__cremona_label = X.__cremona_label - return self.cremona_label(space) + self.__cremona_label = label + if not space: + return label.replace(' ', '') + return label label = cremona_label diff --git a/src/sage/schemes/elliptic_curves/heegner.py b/src/sage/schemes/elliptic_curves/heegner.py index ad96a2e836a..214d9822e41 100644 --- a/src/sage/schemes/elliptic_curves/heegner.py +++ b/src/sage/schemes/elliptic_curves/heegner.py @@ -2624,7 +2624,7 @@ def __hash__(self): EXAMPLES:: sage: y = EllipticCurve('389a').heegner_point(-7,5) - sage: hash(y) + sage: hash(y) # random output -756867903203770682 # 64-bit -274399546 # 32-bit """ @@ -2929,7 +2929,7 @@ def __hash__(self): """ EXAMPLES:: - sage: hash(EllipticCurve('389a').heegner_point(-7,5)) + sage: hash(EllipticCurve('389a').heegner_point(-7,5)) # random output -756867903203770682 # 64-bit -274399546 # 32-bit """ @@ -3488,7 +3488,7 @@ def _numerical_approx_conjugates_over_QQ(self, prec=53): [(-1.89564392373896 - 0.444771808762067*I : -1.50000000000000 + 2.13102976222246*I : 1.00000000000000), ...] sage: y._numerical_approx_conjugates_over_QQ(prec=10) [(-1.9 - 0.44*I : -1.5 + 2.1*I : 1.0), ... - (-1.9 - 0.44*I : -1.5 + 2.1*I : 1.0)] + (1.4 + 0.0024*I : -1.7 - 0.0046*I : 1.0)] """ v = [] for z in self.conjugates_over_K(): @@ -3518,8 +3518,8 @@ def _numerical_approx_xy_poly(self, prec=53): sage: E = EllipticCurve('37a') sage: y = E.heegner_point(-7,3); y Heegner point of discriminant -7 and conductor 3 on elliptic curve of conductor 37 - sage: y._numerical_approx_xy_poly() - (X^8 + 6.00000000000000*X^7 + 8.99999999999998*X^6 - 12.0000000000000*X^5 - 42.0000000000000*X^4 - 17.999999999999...*X^3 + 36.0000000000001*X^2 + 35.9999999999999*X + 8.999999999999..., X^8 + 12.0000000000000*X^7 + 72.0000000000000*X^6 + 270.000000000000*X^5 + 678.000000000001*X^4 + 1152.00000000000*X^3 + 1269.00000000000*X^2 + 810.00000000000...*X + 225.000000000001) + sage: y._numerical_approx_xy_poly() # rel tol 1e-14 + (X^8 + 6.00000000000000*X^7 + 8.99999999999998*X^6 - 12.0000000000000*X^5 - 42.0000000000000*X^4 - 17.9999999999999*X^3 + 36.0000000000001*X^2 + 35.9999999999999*X + 8.99999999999995, X^8 + 12.0000000000000*X^7 + 72.0000000000000*X^6 + 270.000000000000*X^5 + 678.000000000001*X^4 + 1152.00000000000*X^3 + 1269.00000000000*X^2 + 810.000000000002*X + 225.000000000001) """ v = self._numerical_approx_conjugates_over_QQ(prec) R = ComplexField(prec)['X'] diff --git a/src/sage/schemes/elliptic_curves/padic_lseries.py b/src/sage/schemes/elliptic_curves/padic_lseries.py index 1e147b254aa..4011736c51f 100644 --- a/src/sage/schemes/elliptic_curves/padic_lseries.py +++ b/src/sage/schemes/elliptic_curves/padic_lseries.py @@ -663,6 +663,7 @@ def _get_series_from_cache(self, n, prec, D, eta): sage: E = EllipticCurve('11a1') sage: Lp = E.padic_lseries(5) + sage: Lp._pAdicLseries__series = {} # clear cached series sage: Lp._get_series_from_cache(3,5,1,0) sage: Lp.series(3,prec=5) 5 + 4*5^2 + 4*5^3 + O(5^4) + O(5)*T + O(5)*T^2 + O(5)*T^3 + O(5)*T^4 + O(T^5) diff --git a/src/sage/schemes/elliptic_curves/period_lattice.py b/src/sage/schemes/elliptic_curves/period_lattice.py index 7025cc01b22..a62cc9259fc 100644 --- a/src/sage/schemes/elliptic_curves/period_lattice.py +++ b/src/sage/schemes/elliptic_curves/period_lattice.py @@ -1044,7 +1044,7 @@ def ei(self): sage: E = EllipticCurve([0,1,0,a,a]) sage: L = E.period_lattice(K.embeddings(RealField())[0]) sage: L.ei() - [0.?e-19 - 1.122462048309373?*I, 0.?e-19 + 1.122462048309373?*I, -1] + [0.?e-17 - 1.122462048309373?*I, 0.?e-17 + 1.122462048309373?*I, -1] sage: L = E.period_lattice(K.embeddings(ComplexField())[0]) sage: L.ei() From f94b5d84c3b3f983371de0a703dcfa8e3e74c395 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Mon, 23 Jun 2014 01:08:08 +0100 Subject: [PATCH 351/546] Trac 11474: reorganise documentation a bit --- .../elliptic_curves/ell_rational_field.py | 49 +++++-------------- 1 file changed, 12 insertions(+), 37 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index c7b583fb753..89b7f3793e2 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -117,13 +117,14 @@ class EllipticCurve_rational_field(EllipticCurve_number_field): INPUT: - - ``ainvs`` (list or string) -- either `[a_1,a_2,a_3,a_4,a_6]` or - `[a_4,a_6]` (with `a_1=a_2=a_3=0`) or a valid label from the - database. + - ``ainvs`` -- a list or tuple `[a_1, a_2, a_3, a_4, a_6]` of + Weierstrass coefficients. .. note:: - See constructor.py for more variants. + This class should not be called directly; use + :class:`sage.constructor.EllipticCurve` to construct + elliptic curves. EXAMPLES: @@ -133,52 +134,26 @@ class EllipticCurve_rational_field(EllipticCurve_number_field): Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Rational Field Construction from Weierstrass coefficients (`a`-invariants), - short form (sets `a_1=a_2=a_3=0`):: + short form (sets `a_1 = a_2 = a_3 = 0`):: sage: EllipticCurve([4,5]).ainvs() (0, 0, 0, 4, 5) - Construction from a database label:: + Constructor from a Cremona label:: sage: EllipticCurve('389a1') Elliptic Curve defined by y^2 + y = x^3 + x^2 - 2*x over Rational Field + Constructor from an LMFDB label:: + + sage: EllipticCurve('462.f3') + Elliptic Curve defined by y^2 + x*y = x^3 - 363*x + 1305 over Rational Field + """ def __init__(self, ainvs, **kwds): r""" Constructor for the EllipticCurve_rational_field class. - INPUT: - - - ``ainvs`` -- a list or tuple `[a_1, a_2, a_3, a_4, a_6]` of - Weierstrass coefficients. - - .. NOTE:: - - This class should not be called directly; use - :class:`sage.constructor.EllipticCurve` to construct - elliptic curves. - - EXAMPLES:: - - sage: E = EllipticCurve([1,2,3,4,5]); E - Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Rational Field - - Constructor from `[a_4,a_6]` sets `a_1=a_2=a_3=0`:: - - sage: EllipticCurve([4,5]).ainvs() - (0, 0, 0, 4, 5) - - Constructor from a Cremona label:: - - sage: EllipticCurve('389a1') - Elliptic Curve defined by y^2 + y = x^3 + x^2 - 2*x over Rational Field - - Constructor from an LMFDB label:: - - sage: EllipticCurve('462.f3') - Elliptic Curve defined by y^2 + x*y = x^3 - 363*x + 1305 over Rational Field - TESTS: When constructing a curve from the large database using a From 7c1997305aaa712c4e692a0796f1736594b7158c Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Mon, 23 Jun 2014 01:24:21 +0100 Subject: [PATCH 352/546] Trac 11474: minor documentation improvements --- .../schemes/elliptic_curves/constructor.py | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index ad9a353ac3a..ef803a422c9 100644 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -49,42 +49,42 @@ class EllipticCurveFactory(UniqueFactory): r""" Construct an elliptic curve. - In Sage, an elliptic curve is always specified by a long - Weierstrass equation + In Sage, an elliptic curve is always specified by + (the coefficients of) a long Weierstrass equation .. math:: - y^2 + a_1 xy + a_3 y = x^3 + a_2 x^2 + a_4 x + a_6. + y^2 + a_1 xy + a_3 y = x^3 + a_2 x^2 + a_4 x + a_6. INPUT: There are several ways to construct an elliptic curve: - ``EllipticCurve([a1,a2,a3,a4,a6])``: Elliptic curve with given - a-invariants. The invariants are coerced into the parent of the - first element. If all are integers, they are coerced into the - rational numbers. + `a`-invariants. The invariants are coerced into a common parent. + If all are integers, they are coerced into the rational numbers. - ``EllipticCurve([a4,a6])``: Same as above, but `a_1=a_2=a_3=0`. - - ``EllipticCurve(label)``: Returns the elliptic curve over Q from - the Cremona database with the given label. The label is a + - ``EllipticCurve(label)``: Returns the elliptic curve over `\QQ` + from the Cremona database with the given label. The label is a string, such as ``"11a"`` or ``"37b2"``. The letters in the label *must* be lower case (Cremona's new labeling). - ``EllipticCurve(R, [a1,a2,a3,a4,a6])``: Create the elliptic - curve over ``R`` with given a-invariants. Here ``R`` can be an - arbitrary ring. Note that addition need not be defined. + curve over `R` with given `a`-invariants. Here `R` can be an + arbitrary commutative ring, although most functionality is only + implemented over fields. - ``EllipticCurve(j=j0)`` or ``EllipticCurve_from_j(j0)``: Return - an elliptic curve with j-invariant ``j0``. + an elliptic curve with `j`-invariant ``j0``. - - ``EllipticCurve(polynomial)``: Read off the a-invariants from + - ``EllipticCurve(polynomial)``: Read off the `a`-invariants from the polynomial coefficients, see :func:`EllipticCurve_from_Weierstrass_polynomial`. - In each case above where the input is a list of length 2 or 5, one - can instead give a 2 or 5-tuple instead. + Instead of giving the coefficients as a *list* of length 2 or 5, + one can also give a *tuple*. EXAMPLES: From 9871e7fa0e194b6e21a2f7a5f2d727bbd855ff88 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Mon, 23 Jun 2014 01:24:57 +0100 Subject: [PATCH 353/546] Trac 11474: add doctest --- src/sage/schemes/elliptic_curves/constructor.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index ef803a422c9..d5319a0144e 100644 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -177,12 +177,18 @@ class EllipticCurveFactory(UniqueFactory): sage: EllipticCurve(GF(144169),j=1728) Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 144169 + Elliptic curves over the same ring with the same Weierstrass + coefficients are identical, even when they are constructed in + different ways (see :trac:`11474`):: + + sage: EllipticCurve('11a3') is EllipticCurve(QQ, [0, -1, 1, 0, 0]) + True + By default, when a rational value of `j` is given, the constructed curve is a minimal twist (minimal conductor for curves with that `j`-invariant). This can be changed by setting the optional parameter ``minimal_twist``, which is True by default, to False:: - sage: EllipticCurve(j=100) Elliptic Curve defined by y^2 = x^3 + x^2 + 3392*x + 307888 over Rational Field sage: E =EllipticCurve(j=100); E From 7b33df2464a78e70766fbb031af1750e5ed6239a Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 25 Jun 2014 10:53:25 +0200 Subject: [PATCH 354/546] Speed up sums of squares --- src/sage/rings/arith.py | 88 +++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 38 deletions(-) diff --git a/src/sage/rings/arith.py b/src/sage/rings/arith.py index 7e5ad6443d6..f3f8f13c0b2 100644 --- a/src/sage/rings/arith.py +++ b/src/sage/rings/arith.py @@ -4928,10 +4928,10 @@ def two_squares(n): ValueError: 21 is not a sum of 2 squares sage: two_squares(21^2) (0, 21) - sage: a,b = two_squares(2009); a,b - (28, 35) + sage: a,b = two_squares(100000000000000000129); a,b + (4418521500, 8970878873) sage: a^2 + b^2 - 2009 + 100000000000000000129 sage: two_squares(2^222+1) (253801659504708621991421712450521, 2583712713213354898490304645018692) sage: two_squares(0) @@ -4945,7 +4945,7 @@ def two_squares(n): See http://www.schorn.ch/howto.html """ - from sage.rings.all import Integer + from sage.rings.all import Integer, Mod n = Integer(n) if n <= 0: @@ -4954,6 +4954,10 @@ def two_squares(n): return (z, z) raise ValueError("%s is not a sum of 2 squares"%n) + if n.nbits() <= 32: + from sage.rings import sum_of_squares + return sum_of_squares.two_squares_pyx(n) + # Start by factoring n (which seems to be unavoidable) F = n.factor(proof=False) @@ -4967,8 +4971,8 @@ def two_squares(n): # We run over all factors of n, write each factor p^e as # a sum of 2 squares and accumulate the product # (using multiplication in Z[I]) in a^2 + b^2. - a = pari(1) - b = pari(0) + a = Integer(1) + b = Integer(0) for (p,e) in F: if e >= 2: m = p ** (e//2) @@ -4981,7 +4985,7 @@ def two_squares(n): else: # p = 1 mod 4 # Find a square root of -1 mod p. # If y is a non-square, then y^((p-1)/4) is a square root of -1. - y = pari(2).Mod(p) + y = Mod(2,p) while True: s = y**((p-1)/4) if not s*s + 1: @@ -4997,8 +5001,8 @@ def two_squares(n): # Multiply (a + bI) by (r + sI) a,b = a*r - b*s, b*r + a*s - a = Integer(a.abs()) - b = Integer(b.abs()) + a = a.abs() + b = b.abs() assert a*a + b*b == n if a <= b: return (a,b) @@ -5020,19 +5024,21 @@ def three_squares(n): EXAMPLES:: sage: three_squares(389) - (7, 12, 14) + (1, 8, 18) sage: three_squares(946) - (3, 19, 24) + (9, 9, 28) sage: three_squares(2986) - (12, 21, 49) + (3, 24, 49) sage: three_squares(7^100) (0, 0, 1798465042647412146620280340569649349251249) sage: three_squares(11^111-1) (616274160655975340150706442680, 901582938385735143295060746161, 6270382387635744140394001363065311967964099981788593947233) - sage: three_squares(16 * 7) + sage: three_squares(7 * 2^41) + (1048576, 2097152, 3145728) + sage: three_squares(7 * 2^42) Traceback (most recent call last): ... - ValueError: 112 is not a sum of 3 squares + ValueError: 30786325577728 is not a sum of 3 squares sage: three_squares(0) (0, 0, 0) sage: three_squares(-1) @@ -5045,7 +5051,7 @@ def three_squares(n): See http://www.schorn.ch/howto.html """ from sage.rings.all import Integer - n = pari(n) + n = Integer(n) if n <= 0: if n == 0: @@ -5053,17 +5059,20 @@ def three_squares(n): return (z, z, z) raise ValueError("%s is not a sum of 3 squares"%n) + if n.nbits() <= 32: + from sage.rings import sum_of_squares + return sum_of_squares.three_squares_pyx(n) + # First, remove all factors 4 from n e = n.valuation(2)//2 - m = 2**e - N = n/(m*m) - m = Integer(m) + m = Integer(1) << e + N = n >> (2*e) # Let x be the largest integer at most sqrt(N) - x = N.sqrtint() + x, r = N.sqrtrem() # We need to check for this special case, # otherwise N - x^2 will always factor. - if x*x == N: + if not r: z = ZZ.zero() return (z, z, x*m) @@ -5076,7 +5085,7 @@ def three_squares(n): x -= 1 while x >= 0: p = N - x*x - if p.ispseudoprime(): + if p.is_pseudoprime(): break x -= 2 elif N % 4 == 2: @@ -5085,7 +5094,7 @@ def three_squares(n): x -= 1 while x >= 0: p = N - x*x - if p.ispseudoprime(): + if p.is_pseudoprime(): break x -= 2 elif N % 8 == 3: @@ -5093,8 +5102,8 @@ def three_squares(n): if x % 2 == 0: x -= 1 while x >= 0: - p = (N - x*x)/2 - if p.ispseudoprime(): + p = (N - x*x) >> 1 + if p.is_pseudoprime(): break x -= 2 else: # 7 mod 8 @@ -5106,7 +5115,7 @@ def three_squares(n): if N > 10000: from warnings import warn warn("Brute forcing sum of 3 squares for large N = %s"%N, RuntimeWarning) - x = N.sqrtint() + x = N.isqrt() # In the usual case, this loop will only be executed once, since # we already know the "right" value of x. @@ -5147,15 +5156,15 @@ def four_squares(n): sage: four_squares(130) (0, 0, 3, 11) sage: four_squares(1101011011004) - (130, 348, 1170, 1049290) + (90, 102, 1220, 1049290) sage: four_squares(10^100-1) (155024616290, 2612183768627, 14142135623730950488016887, 99999999999999999999999999999999999999999999999999) - sage: for i in range(10000): # long time + sage: for i in range(2^129, 2^129+10000): # long time ....: S = four_squares(i) ....: assert sum(x^2 for x in S) == i """ from sage.rings.all import Integer - n = pari(n) + n = Integer(n) if n <= 0: if n == 0: @@ -5163,15 +5172,18 @@ def four_squares(n): return (z, z, z, z) raise ValueError("%s is not a sum of 4 squares"%n) + if n.nbits() <= 32: + from sage.rings import sum_of_squares + return sum_of_squares.four_squares_pyx(n) + # First, remove all factors 4 from n e = n.valuation(2)//2 - m = 2**e - N = n/(m*m) - m = Integer(m) + m = Integer(1) << e + N = n >> (2*e) # Subtract a suitable x^2 such that N - x^2 is 1,2,3,5,6 mod 8, # which can then be written as a sum of 3 squares. - x = N.sqrtint() + x = N.isqrt() y = N - x*x if y >= 7 and (y % 4 == 0 or y % 8 == 7): x -= 1 @@ -5239,7 +5251,7 @@ def sum_of_k_squares(k,n): ValueError: k = -1 must be non-negative """ from sage.rings.all import Integer - n = pari(n) + n = Integer(n) k = int(k) if k <= 4: @@ -5251,9 +5263,9 @@ def sum_of_k_squares(k,n): return two_squares(n) if k == 1: if n >= 0: - x = n.sqrtint() - if n == x*x: - return (Integer(x),) + x, r = n.sqrtrem() + if not r: + return (x,) raise ValueError("%s is not a sum of 1 square"%n) if k == 0: if n == 0: @@ -5267,8 +5279,8 @@ def sum_of_k_squares(k,n): # Recursively subtract the largest square t = [] while k > 4: - x = n.sqrtint() - t.insert(0,Integer(x)) + x = n.isqrt() + t.insert(0, x) n -= x*x k -= 1 From 0fa89d548f431a8929c76f3205bdb6c3779cf432 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 13 Jun 2014 12:53:19 +0200 Subject: [PATCH 355/546] trac #16347: Wilson's construction with two truncated groups --- src/sage/combinat/designs/design_catalog.py | 3 +- src/sage/combinat/designs/latin_squares.py | 28 +- .../combinat/designs/orthogonal_arrays.py | 240 +++++++++++------- 3 files changed, 174 insertions(+), 97 deletions(-) diff --git a/src/sage/combinat/designs/design_catalog.py b/src/sage/combinat/designs/design_catalog.py index 63176682328..0d938e233b0 100644 --- a/src/sage/combinat/designs/design_catalog.py +++ b/src/sage/combinat/designs/design_catalog.py @@ -39,7 +39,7 @@ :delim: | :meth:`~sage.combinat.designs.block_design.ProjectiveGeometryDesign` - :meth:`~sage.combinat.designs.block_design.ProjectivePlaneDesign` + :meth:`~sage.combinat.designs.block_design.DesarguesianProjectivePlaneDesign` :meth:`~sage.combinat.designs.bibd.BalancedIncompleteBlockDesign` :meth:`~sage.combinat.designs.block_design.AffineGeometryDesign` :meth:`~sage.combinat.designs.block_design.WittDesign` @@ -50,6 +50,7 @@ :meth:`~sage.combinat.designs.orthogonal_arrays.orthogonal_array` :meth:`~sage.combinat.designs.bibd.steiner_triple_system` :meth:`~sage.combinat.designs.steiner_quadruple_systems.steiner_quadruple_system` + :meth:`~sage.combinat.designs.block_design.projective_plane` And the :meth:`designs.best_known_covering_design_from_LJCR ` function diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 9ca46ac9653..545077405c1 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -33,19 +33,19 @@ 0| +oo +oo 1 2 3 4 1 6 7 8 2 10 5 12 4 4 15 16 5 18 20| 4 5 3 22 7 24 4 26 5 28 4 30 31 5 4 5 8 36 4 5 - 40| 7 40 5 42 5 6 4 46 8 48 6 5 5 52 5 6 7 7 2 58 + 40| 7 40 5 42 5 6 4 46 8 48 6 5 5 52 5 6 7 7 5 58 60| 5 60 5 6 63 7 5 66 5 6 6 70 7 72 5 7 6 6 6 78 - 80| 9 80 8 82 6 6 6 3 7 88 4 6 6 4 3 6 7 96 6 8 - 100| 8 100 6 102 7 7 5 106 5 108 4 6 7 112 3 7 5 8 4 6 - 120| 6 120 5 6 5 124 6 126 127 7 6 130 6 6 6 6 7 136 4 138 - 140| 6 7 6 10 10 7 6 7 5 148 6 150 7 8 8 5 5 156 4 6 - 160| 7 7 6 162 5 7 4 166 7 168 6 8 6 172 6 6 10 9 6 178 + 80| 9 80 8 82 6 6 6 6 7 88 5 6 6 6 6 6 7 96 6 8 + 100| 8 100 6 102 7 7 6 106 6 108 6 6 7 112 6 7 5 8 4 6 + 120| 6 120 6 6 6 124 6 126 127 7 6 130 6 6 6 6 7 136 6 138 + 140| 6 7 6 10 10 7 6 7 6 148 6 150 7 8 8 6 6 156 6 6 + 160| 7 7 6 162 5 7 6 166 7 168 6 8 6 172 6 6 10 9 6 178 180| 6 180 6 6 7 8 6 10 6 6 6 190 7 192 6 7 6 196 6 198 200| 7 7 6 7 6 6 6 8 12 11 10 210 6 7 6 7 7 8 6 10 - 220| 6 12 6 222 7 8 6 226 6 228 6 6 7 232 6 7 6 6 5 238 - 240| 7 240 6 242 6 7 6 12 7 7 5 250 6 10 7 7 255 256 4 7 - 260| 6 8 7 262 7 8 6 10 6 268 6 270 15 16 5 10 10 276 6 8 - 280| 7 280 6 282 6 12 6 7 15 288 6 6 5 292 6 6 7 10 10 12 + 220| 6 12 6 222 7 8 6 226 6 228 6 6 7 232 6 7 6 6 6 238 + 240| 7 240 6 242 6 7 6 12 7 7 6 250 6 10 7 7 255 256 6 7 + 260| 6 8 7 262 7 8 6 10 6 268 7 270 15 16 6 10 10 276 6 8 + 280| 7 280 6 282 6 12 6 7 15 288 6 6 6 292 6 6 7 10 10 12 TODO: @@ -295,7 +295,7 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi [4 5 6 7 1 2 3 9 0 8], [7 1 2 3 4 5 6 9 8 0] ] """ - from sage.combinat.designs.orthogonal_arrays import orthogonal_array + from sage.combinat.designs.orthogonal_arrays import orthogonal_array, _set_OA_cache, _get_OA_cache from sage.matrix.constructor import Matrix from sage.rings.arith import factor from database import MOLS_constructions @@ -312,6 +312,9 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi if existence: return k + if existence and not who_asked and _get_OA_cache(k+2,n) is not None: + return _get_OA_cache(k+2,n) + if n == 1: if existence: return True @@ -323,6 +326,7 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi raise EmptySetError("There exist at most n-1 MOLS of size n if n>=2.") elif n in MOLS_constructions and k <= MOLS_constructions[n][0]: + _set_OA_cache(MOLS_constructions[n][0]+2,n,True) if existence: return True _, construction = MOLS_constructions[n] @@ -355,6 +359,8 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi matrices = [Matrix(M) for M in matrices] else: + if not who_asked: + _set_OA_cache(k+2,n,Unknown) if existence: return Unknown raise NotImplementedError("I don't know how to build {} MOLS of order {}".format(k,n)) diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 0dbfd9e3860..81400a49a70 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -6,8 +6,6 @@ .. TODO:: - - Implement an improvement of Wilson's construction for u=1,2 in [CD96]_ - - A resolvable `OA(k,n)` is equivalent to a `OA(k+1,n)`. Sage should be able to return resolvable OA, with sorted rows (so that building the decomposition is easy. @@ -279,6 +277,9 @@ def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): if existence: return k + if existence and not who_asked and _get_OA_cache(k,n) is not None: + return _get_OA_cache(k,n) + if n == 1: if existence: return True @@ -290,12 +291,14 @@ def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): raise EmptySetError("No Transversal Design exists when k>=n+2 if n>=2") elif n == 12 and k <= 6: + _set_OA_cache(6,12,True) if existence: return True from sage.combinat.designs.database import TD_6_12 TD = [l[:k] for l in TD_6_12()] elif TD_find_product_decomposition(k,n): + _set_OA_cache(k,n,True) if existence: return True n1,n2 = TD_find_product_decomposition(k,n) @@ -303,11 +306,6 @@ def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): TD2 = transversal_design(k,n2, check = False) TD = TD_product(k,TD1,n1,TD2,n2, check = False) - elif find_wilson_decomposition(k,n): - if existence: - return True - TD = wilson_construction(*find_wilson_decomposition(k,n), check = False) - # Section 6.6 of [Stinson2004] elif (orthogonal_array not in who_asked and orthogonal_array(k, n, existence=True, who_asked = who_asked + (transversal_design,)) is not Unknown): @@ -325,6 +323,8 @@ def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): TD = [[i*n+c for i,c in enumerate(l)] for l in OA] else: + if not who_asked: + _set_OA_cache(k,n,Unknown) if existence: return Unknown raise NotImplementedError("I don't know how to build a TD({},{})!".format(k,n)) @@ -368,9 +368,9 @@ def is_transversal_design(B,k,n, verbose=False): return is_orthogonal_array([[x%n for x in R] for R in B],k,n,verbose=verbose) @cached_function -def find_wilson_decomposition(k,n): +def find_wilson_decomposition_with_one_truncated_group(k,n): r""" - Finds a wilson decomposition of `k,n` + Finds a wilson decomposition of `k,n` with one truncated group. This method looks for possible integers `m,t,u` satisfying that `mt+u=n` and such that Sage knows how to build a `TD(k,m), TD(k,m+1),TD(k+1,t)` and a @@ -386,10 +386,10 @@ def find_wilson_decomposition(k,n): EXAMPLES:: - sage: from sage.combinat.designs.orthogonal_arrays import find_wilson_decomposition - sage: find_wilson_decomposition(4,38) + sage: from sage.combinat.designs.orthogonal_arrays import find_wilson_decomposition_with_one_truncated_group + sage: find_wilson_decomposition_with_one_truncated_group(4,38) (4, 7, 5, 3) - sage: find_wilson_decomposition(4,20) + sage: find_wilson_decomposition_with_one_truncated_group(4,20) False """ # If there exists a TD(k+1,t) then k+1 < t+2, i.e. k <= t @@ -413,100 +413,148 @@ def find_wilson_decomposition(k,n): return False -def wilson_construction(k,m,t,u, check = True): +@cached_function +def find_wilson_decomposition_with_two_truncated_groups(k,n): r""" - Returns a `TD(k,mt+u)` by Wilson's construction. + Helper function for Wilson's construction with two trucated columns - Wilson's construction builds a `TD(k,mt+u)` from the following designs : + Find integers `r,m,r_1,r_2` satisfying `n=rm+r_1+r_2` and `1\leq r_1,r_2 2*r-2 or + not orthogonal_array(k,m ,existence=True) or + not orthogonal_array(k,m+1,existence=True) or + not orthogonal_array(k,m+2,existence=True)): + continue + + for r1 in range(max(1,r1_p_r2-(r-1)),min(r,r1_p_r2+1)): + if not orthogonal_array(k,r1,existence=True): + continue + r2 = r1_p_r2-r1 + if orthogonal_array(k,r2,existence=True): + assert n == r*m+r1+r2 + return k,r,m,r1,r2 + return False + +def wilson_construction(OA,k,r,m,n_trunc,u,check=True): + r""" + Returns a `OA(k,rm+u)` from a truncated `OA(k+s,r)` by Wilson's construction. + + Let `iOA` be an incomplete `OA(k+s,r)` with `s` truncated columns of sizes + `u_1,...,u_s`, whose blocks have sizes in `\{k+b_1,...,k+b_t\}`. If there + exist: + + - An `OA(k,m+b_i)` for every `1\leq i\leq t` + + - An `OA(k,u_i)` for every `1\leq i\leq s` + + Then there exists an `OA(k,rm+\sum u_i)`. INPUT: - - ``k,m,t,u`` -- integers with `k\geq 2` and `1\leq u\leq t`. + - ``OA`` -- an incomplete orthogonal array with ``k+n_trunc`` columns. The + elements of a column of size `c` must belong to `\{0,...,c\}`. The missing + entries of a block are represented by ``None`` values. + + - ``k,r,m,n_trunc`` (integers) + + - ``u`` (list) -- a list of length ``n_trunc`` such that column ``k+i`` has + size ``u[i]``. - ``check`` (boolean) -- whether to check that output is correct before returning it. As this is expected to be useless (but we are cautious guys), you may want to disable it whenever you want speed. Set to ``True`` by default. + REFERENCE: + + .. [HananiBIBD] Balanced incomplete block designs and related designs, + Haim Hanani, + Discrete Mathematics 11.3 (1975) pages 255-369. + EXAMPLES:: sage: from sage.combinat.designs.orthogonal_arrays import wilson_construction - sage: from sage.combinat.designs.orthogonal_arrays import find_wilson_decomposition + sage: from sage.combinat.designs.orthogonal_arrays import OA_relabel + sage: from sage.combinat.designs.orthogonal_arrays import find_wilson_decomposition_with_one_truncated_group sage: total = 0 sage: for k in range(3,8): ....: for n in range(1,30): - ....: if find_wilson_decomposition(k,n): + ....: if find_wilson_decomposition_with_one_truncated_group(k,n): ....: total += 1 - ....: k,m,t,u = find_wilson_decomposition(k,n) - ....: _ = wilson_construction(k,m,t,u, check=True) + ....: k,m,r,u = find_wilson_decomposition_with_one_truncated_group(k,n) + ....: OA = designs.orthogonal_array(k+1,r,check=False) + ....: OA = OA_relabel(OA,k+1,r,matrix=[range(r)]*k+[range(u)+[None]*(r-u)]) + ....: _ = wilson_construction(OA,k,r,m,1,[u],check=True) sage: print total 41 """ - # Raises a NotImplementedError if one of them does not exist. - TDkm = transversal_design(k,m,check=False) - TDkm1 = transversal_design(k,m+1,check=False) - TDk1t = transversal_design(k+1,t,check=False) - TDku = transversal_design(k,u,check=False) + n = r*m+sum(u) + master_design = OA - # Truncaed TDk1t - truncated_TDk1t = [[x for x in B if x= min_unknown or k <= max_unknown): + elif min_unknown is not None and (k >= min_unknown and k <= max_unknown): return Unknown elif k >= min_false: return False @@ -836,6 +886,8 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): from database import OA_constructions from block_design import projective_plane, projective_plane_to_OA + assert n>=0 + # If k is set to None we find the largest value available if k is None: if n == 0 or n == 1: @@ -855,6 +907,9 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): if k < t: raise ValueError("undefined for k Date: Wed, 25 Jun 2014 12:17:34 +0200 Subject: [PATCH 356/546] trac #16437: cut the branches in W. dec. with two trunc. blocks --- .../combinat/designs/orthogonal_arrays.py | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 81400a49a70..4367856ecae 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -435,19 +435,32 @@ def find_wilson_decomposition_with_two_truncated_groups(k,n): sage: find_wilson_decomposition_with_two_truncated_groups(5,58) (5, 7, 7, 4, 5) """ - for r in range(1,n-2): # as r*1+1+1 <= n + for r in [1] + range(k+1,n-2): # as r*1+1+1 <= n and because we need + # an OA(k+2,r), necessarily r=1 or r >= k+1 if not orthogonal_array(k+2,r,existence=True): continue - for m in range(max(1,(n-(2*r-2))//r),(n-2)/r+1): # as r*m+1+1 <= n + m_min = (n - (2*r-2))//r + m_max = (n - 2)//r + if m_min > 1: + m_values = range(max(m_min,k-1), m_max+1) + else: + m_values = [1] + range(k-1, m_max+1) + for m in m_values: r1_p_r2 = n-r*m # the sum of r1+r2 - if (r1_p_r2 < 2 or - r1_p_r2 > 2*r-2 or + # it is automatically >= 2 since m <= m_max + if (r1_p_r2 > 2*r-2 or not orthogonal_array(k,m ,existence=True) or not orthogonal_array(k,m+1,existence=True) or not orthogonal_array(k,m+2,existence=True)): continue - for r1 in range(max(1,r1_p_r2-(r-1)),min(r,r1_p_r2+1)): + r1_min = r1_p_r2 - (r-1) + r1_max = min(r-1, r1_p_r2) + if r1_min > 1: + r1_values = range(max(k-1,r1_min), r1_max+1) + else: + r1_values = [1] + range(k-1, r1_max+1) + for r1 in r1_values: if not orthogonal_array(k,r1,existence=True): continue r2 = r1_p_r2-r1 From 8ebd21be12c81b751a7f09067221bef01014b705 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 25 Jun 2014 12:21:31 +0200 Subject: [PATCH 357/546] trac #16347: use is_sum_of_squares_pyx instead of two_squares --- src/sage/combinat/designs/block_design.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/designs/block_design.py b/src/sage/combinat/designs/block_design.py index 21e31d6c81e..0d2ff31df64 100644 --- a/src/sage/combinat/designs/block_design.py +++ b/src/sage/combinat/designs/block_design.py @@ -374,7 +374,7 @@ def projective_plane(n, check=True, existence=False): sage: designs.projective_plane(6) Traceback (most recent call last): ... - EmptySetError: By the Ryser-Chowla theorem, no projective plane of order 6 exists. + EmptySetError: By the Bruck-Ryser theorem, no projective plane of order 6 exists. sage: designs.projective_plane(10) Traceback (most recent call last): ... @@ -386,7 +386,7 @@ def projective_plane(n, check=True, existence=False): sage: designs.projective_plane(14) Traceback (most recent call last): ... - EmptySetError: By the Ryser-Chowla theorem, no projective plane of order 14 exists. + EmptySetError: By the Bruck-Ryser theorem, no projective plane of order 14 exists. TESTS:: @@ -399,7 +399,8 @@ def projective_plane(n, check=True, existence=False): sage: designs.projective_plane(12, existence=True) Unknown """ - from sage.rings.arith import is_prime_power, two_squares + from sage.rings.arith import is_prime_power + from sage.rings.sum_of_squares import is_sum_of_two_squares_pyx if n <= 1: if existence: @@ -413,14 +414,11 @@ def projective_plane(n, check=True, existence=False): "projective planes of order 10\" (1989), Canad. J. Math.") raise EmptySetError("No projective plane of order 10 exists by %s"%ref) - if (n%4) in [1,2]: - try: - two_squares(n) - except ValueError: - if existence: - return False - raise EmptySetError("By the Ryser-Chowla theorem, no projective" - " plane of order "+str(n)+" exists.") + if (n%4) in [1,2] and not is_sum_of_two_squares_pyx(n): + if existence: + return False + raise EmptySetError("By the Bruck-Ryser theorem, no projective" + " plane of order {} exists.".format(n)) if not is_prime_power(n): if existence: From 0175134767948882f3d8c2ea0a612161ed3d4154 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 25 Jun 2014 12:47:54 +0200 Subject: [PATCH 358/546] trac #16347: doc + simplifications --- .../combinat/designs/orthogonal_arrays.py | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 4367856ecae..2022eb02625 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -405,10 +405,10 @@ def find_wilson_decomposition_with_one_truncated_group(k,n): if k >= m+2: break - if (transversal_design(k ,m , existence=True) and - transversal_design(k ,m+1, existence=True) and - transversal_design(k+1,t , existence=True) and - transversal_design(k ,u , existence=True)): + if (orthogonal_array(k ,m , existence=True) and + orthogonal_array(k ,m+1, existence=True) and + orthogonal_array(k+1,t , existence=True) and + orthogonal_array(k ,u , existence=True)): return k,m,t,u return False @@ -473,15 +473,16 @@ def wilson_construction(OA,k,r,m,n_trunc,u,check=True): r""" Returns a `OA(k,rm+u)` from a truncated `OA(k+s,r)` by Wilson's construction. - Let `iOA` be an incomplete `OA(k+s,r)` with `s` truncated columns of sizes + Let `OA` be a truncated `OA(k+s,r)` with `s` truncated columns of sizes `u_1,...,u_s`, whose blocks have sizes in `\{k+b_1,...,k+b_t\}`. If there exist: - - An `OA(k,m+b_i)` for every `1\leq i\leq t` + - An `OA(k,m+b_i) - b_i.OA(k,1)` for every `1\leq i\leq t` - An `OA(k,u_i)` for every `1\leq i\leq s` - Then there exists an `OA(k,rm+\sum u_i)`. + Then there exists an `OA(k,rm+\sum u_i)`. The construction is a + generalization of Lemma 3.16 in [HananiBIBD]_. INPUT: @@ -528,16 +529,13 @@ def wilson_construction(OA,k,r,m,n_trunc,u,check=True): assert n_trunc == len(u) # Computing the sizes of the blocks by filtering out None entries - block_sizes = set() - for B in OA: - s_b = sum(xx!=None for xx in B) - block_sizes.add(s_b) + block_sizes = set(sum(xx!=None for xx in B) for B in OA) # For each block of size k+i we need a OA(k,m+i)-i.OA(k,1) - OA_k_mpi = {i-k+m: incomplete_orthogonal_array( k ,i-k+m,(1,)*(i-k)) for i in block_sizes} + OA_k_mpi = {i-k+m: incomplete_orthogonal_array(k, i-k+m, (1,)*(i-k)) for i in block_sizes} # For each truncated column of size uu we need a OA(k,uu) - OA_k_u = {uu: orthogonal_array( k ,uu) for uu in u} + OA_k_u = {uu: orthogonal_array(k, uu) for uu in u} # Building the actual design ! The set of integers is : # 0*m+0...0*m+(m-1)|...|(r-1)m+0...(r-1)m+(m-1)|mr+0...mr+(r1-1)|mr+r1...mr+r1+r2-1 @@ -944,10 +942,14 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): from itertools import product return map(list, product(range(n), repeat=k)) + elif t != 2: + if existence: + return Unknown + raise NotImplementedError("Only trivial orthogonal arrays are implemented for t>=2") + # projective spaces are equivalent to OA(n+1,n,2) - elif (t == 2 and - (projective_plane(n, existence=True) or - (k == n+1 and projective_plane(n, existence=True) is False))): + elif (projective_plane(n, existence=True) or + (k == n+1 and projective_plane(n, existence=True) is False)): _set_OA_cache(n+1,n,projective_plane(n, existence=True)) if k == n+1: if existence: @@ -987,7 +989,7 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): OA = OA_relabel(OA,k+2,r,matrix=[range(r)]*k+[range(u1)+[None]*(r-u1),range(u2)+[None]*(r-u2)]) OA = wilson_construction(OA,k,r,m,2,[u1,u2],check=False) - elif (t == 2 and transversal_design not in who_asked and + elif (transversal_design not in who_asked and transversal_design(k,n,existence=True,who_asked=who_asked+(orthogonal_array,)) is not Unknown): # forward existence @@ -1005,7 +1007,7 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): raise EmptySetError("There exists no OA({},{})!".format(k,n)) # Section 6.5.1 from [Stinson2004] - elif (t == 2 and mutually_orthogonal_latin_squares not in who_asked and + elif (mutually_orthogonal_latin_squares not in who_asked and mutually_orthogonal_latin_squares(n,k-2, existence=True,who_asked=who_asked+(orthogonal_array,)) is not Unknown): # forward existence From 6559eec9b16565ca1ab3f3469455f011305f1819 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 25 Jun 2014 13:55:33 +0200 Subject: [PATCH 359/546] trac #16446: fix doctest --- src/sage/combinat/designs/bibd.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index 6bc45f29a0b..e30f967f3ac 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -149,9 +149,9 @@ def balanced_incomplete_block_design(v,k,existence=False,use_LJCR=False): For `k > 5` there are currently very few constructions:: sage: [v for v in xrange(150) if designs.balanced_incomplete_block_design(v,6,existence=True) is True] - [1, 6, 31] + [1, 6, 31, 91] sage: [v for v in xrange(150) if designs.balanced_incomplete_block_design(v,6,existence=True) is Unknown] - [16, 21, 36, 46, 51, 61, 66, 76, 81, 91, 96, 106, 111, 121, 126, 136, 141] + [16, 21, 36, 46, 51, 61, 66, 76, 81, 96, 106, 111, 121, 126, 136, 141] """ if v == 1: if existence: From 16a0c5db37a0d4e92b74e0f60a6586eea6eb3e50 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 25 Jun 2014 15:13:00 +0200 Subject: [PATCH 360/546] trac #16446: use deprecated_function_alias instead --- src/sage/combinat/designs/design_catalog.py | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/src/sage/combinat/designs/design_catalog.py b/src/sage/combinat/designs/design_catalog.py index ed823e25992..8e725069e24 100644 --- a/src/sage/combinat/designs/design_catalog.py +++ b/src/sage/combinat/designs/design_catalog.py @@ -87,20 +87,6 @@ from sage.combinat.designs.bibd import balanced_incomplete_block_design, steiner_triple_system # deprecated in june 2014 (#16446) -def BalancedIncompleteBlockDesign(v,k,existence=False,use_LJCR=False): - r""" - This function is deprecated. - - TESTS:: - - sage: bibd = designs.BalancedIncompleteBlockDesign(21,5) - doctest:...: DeprecationWarning: designs.BalancedIncompleteBlockDesign is - deprecated. Please use designs.balanced_incomplete_block_design instead. - See http://trac.sagemath.org/16446 for details. - sage: bibd - Incidence structure with 21 points and 21 blocks - """ - from sage.misc.superseded import deprecation - deprecation(16446, "designs.BalancedIncompleteBlockDesign is deprecated. Please use designs.balanced_incomplete_block_design instead.") - - return balanced_incomplete_block_design(v, k, existence=existence, use_LJCR=use_LJCR) +from sage.misc.superseded import deprecated_function_alias +BalancedIncompleteBlockDesign = deprecated_function_alias(16446, + balanced_incomplete_block_design) From 9ff5062f8d005f19bcdfabf1f9ea65a11856da0b Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sun, 1 Jun 2014 00:30:36 +0200 Subject: [PATCH 361/546] trac #16423: Table of MOLS from the handbook and comparison with Sage --- src/sage/combinat/designs/database.py | 1 - src/sage/combinat/designs/latin_squares.py | 127 +++++++++++++++++++-- 2 files changed, 119 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index bb5fcd66049..3b2c748ecab 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -2868,4 +2868,3 @@ def CDF_221_5_1(): (201,5,1): CDF_201_5_1, (221,5,1): CDF_221_5_1 } - diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 545077405c1..62c3a497cfa 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -20,17 +20,10 @@ :: - sage: def MOLS_table(number_of_lines): - ....: print " "+join(['%3s'%str(i) for i in range(20)]) - ....: print " "+"_"*80 - ....: for i in range(20*15): - ....: if i%20==0: - ....: print "\n"+'%3s'%str(i)+"|", - ....: print '%3s'%str(designs.mutually_orthogonal_latin_squares(i,None,existence=True) if i>1 else "+oo"), + sage: from sage.combinat.designs.latin_squares import MOLS_table sage: MOLS_table(15) # long time 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ________________________________________________________________________________ - 0| +oo +oo 1 2 3 4 1 6 7 8 2 10 5 12 4 4 15 16 5 18 20| 4 5 3 22 7 24 4 26 5 28 4 30 31 5 4 5 8 36 4 5 40| 7 40 5 42 5 6 4 46 8 48 6 5 5 52 5 6 7 7 5 58 @@ -47,6 +40,29 @@ 260| 6 8 7 262 7 8 6 10 6 268 7 270 15 16 6 10 10 276 6 8 280| 7 280 6 282 6 12 6 7 15 288 6 6 6 292 6 6 7 10 10 12 + +Comparison with the results from the Handbook of Combinatorial Designs (2ed):: + + sage: MOLS_table(15,compare=True) # long time + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 + ________________________________________________________________________________ + 0| + + 20| + 40| - - + 60| + - - - - - - + 80| - - - - - - + 100| - - - - - - - - + 120| - - - - - - - - + 140| - - - - - + 160| - - - - - - + 180| - - - + 200| - - - - - - + 220| - - - - + 240| - - - - - + 260| - - - - - - - - - + 280| - - + + TODO: * Look at [ColDin01]_. @@ -426,3 +442,98 @@ def latin_square_product(M,N,*others): return latin_square_product(P, others[0],*others[1:]) else: return P + + +def MOLS_table(number_of_lines,compare=False): + r""" + Prints the MOLS table that Sage can produce. + + INPUT: + + - ``compare`` (boolean) -- if sets to ``True`` the MOLS displays + with `+` and `-` entries its difference with the table from the + Handbook of Combinatorial Designs (2ed). + + EXAMPLES:: + + sage: from sage.combinat.designs.latin_squares import MOLS_table + sage: MOLS_table(5) # long time + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 + ________________________________________________________________________________ + 0| +oo +oo 1 2 3 4 1 6 7 8 2 10 5 12 4 4 15 16 3 18 + 20| 4 5 3 22 7 24 4 26 5 28 4 30 31 5 4 5 8 36 4 5 + 40| 7 40 5 42 5 6 4 46 8 48 6 5 5 52 5 6 7 3 2 58 + 60| 5 60 5 6 63 7 2 66 4 4 6 70 7 72 3 7 3 6 3 78 + 80| 9 80 8 82 6 6 6 3 7 88 3 6 4 4 3 6 7 96 6 8 + sage: MOLS_table(5,compare=True) # long time + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 + ________________________________________________________________________________ + 0| + + 20| + 40| - - + 60| + - - - - - - + 80| - - - - - - + """ + global Handbook_2ed_table + hb = Handbook_2ed_table + from string import join + print " "+join(['%3s'%str(i) for i in range(20)]) + print " "+"_"*80, + for i in range(20*number_of_lines): + if i%20==0: + print "\n"+'%3s'%str(i)+"|", + + k = mutually_orthogonal_latin_squares(i,None,existence=True) + if compare: + if i < 2: + c = " " + elif hb[i] == k: + c = " " + elif hb[i] < k: + c = " +" + else: + c = " -" + else: + if i < 2: + c = "+oo" + else: + c = k + print '{:3}'.format(c), + +# MOLS table from the Handbook of Combinatorial Designs 2ed +Handbook_2ed_table = [None,None,1,2,3,4,1,6,7,8,2,10,5,12,3,4,15,16,3,18,4,5,3, + 22,7,24,4,26,5,28,4,30,31,5,4,5,8,36,4,5,7,40,5,42,5,6,4,46,8,48,6,5,5,52,5, + 6,7,7,5,58,4,60,5,6,63,7,5,66,5,6,6,70,7,72,5,7,6,6,6,78,9,80,8,82,6,6,6,6, + 7,88,6,7,6,6,6,6,7,96,6,8,8,100,6,102,7,7,6,106,6,108,6,6,13,112,6,7,6,8,6, + 6,7,120,6,6,6,124,6,126,127,7,6,130,6,7,6,7,7,136,6,138,6,7,6,10,10,7,6,7,6, + 148,6,150,7,8,8,7,6,156,7,6,9,7,6,162,6,7,6,166,7,168,6,8,6,172,6,6,14,9,6, + 178,6,180,6,6,7,9,6,10,6,8,6,190,7,192,6,7,6,196,6,198,7,8,6,7,6,8,6,8,14,11, + 10,210,6,7,6,7,7,8,6,10,6,12,6,222,13,8,6,226,6,228,6,7,7,232,6,7,6,7,6,238, + 7,240,6,242,6,7,6,12,7,7,6,250,6,12,9,7,255,256,6,12,6,8,8,262,7,8,7,10,7,268, + 7,270,15,16,6,13,10,276,6,9,7,280,6,282,6,12,6,7,15,288,6,6,6,292,6,6,7,10, + 10,12,7,7,7,7,15,15,6,306,7,7,7,310,7,312,7,10,7,316,7,10,15,15,6,16,8,12,6, + 7,7,9,6,330,7,8,7,6,8,336,6,7,6,10,10,342,7,7,6,346,6,348,8,12,18,352,6,9,7, + 9,6,358,8,360,6,7,7,10,6,366,15,15,7,15,7,372,7,15,7,13,7,378,7,12,7,382,15, + 15,7,15,7,388,7,16,7,8,7,7,8,396,7,7,15,400,7,15,11,8,7,15,8,408,7,13,8,12, + 10,9,18,15,7,418,7,420,7,15,7,16,6,7,7,10,6,430,15,432,6,15,6,18,7,438,7,15, + 7,442,7,13,7,11,15,448,7,15,7,7,7,15,7,456,7,16,7,460,7,462,15,15,7,466,8,8, + 7,15,7,15,10,18,7,15,6,478,15,15,6,15,8,7,6,486,7,15,6,490,6,16,6,7,15,15,6, + 498,7,12,9,502,7,15,6,15,7,508,6,15,511,18,7,15,8,12,8,15,8,520,10,522,12,15, + 8,16,15,528,7,15,8,12,7,15,8,15,10,15,12,540,7,15,18,7,7,546,7,8,7,18,7,7,7, + 7,7,556,7,12,15,7,7,562,7,7,6,7,7,568,6,570,7,7,15,22,8,576,7,7,7,8,7,10,7, + 8,7,586,7,18,17,7,15,592,8,15,7,7,8,598,14,600,12,15,7,15,16,606,18,15,7,15, + 8,612,8,15,7,616,7,618,8,22,8,15,15,624,7,8,8,16,7,630,7,8,7,8,7,12,7,8,9,640, + 7,642,7,7,7,646,8,10,7,7,7,652,7,7,15,15,7,658,7,660,7,15,7,15,7,22,7,15,7, + 15,15,672,7,24,8,676,7,15,7,15,7,682,8,15,7,15,15,15,7,690,8,15,7,15,7,16,7, + 15,8,700,7,18,15,15,7,15,8,708,7,15,7,22,21,15,7,15,8,718,15,9,8,12,10,24,12, + 726,7,728,16,16,18,732,7,7,22,10,8,738,7,7,7,742,7,15,7,8,7,10,7,750,15,15, + 8,15,8,756,8,15,7,760,8,15,8,15,8,15,15,768,8,15,8,772,8,24,23,15,8,18,8,18, + 7,26,15,15,10,786,12,15,7,15,20,15,18,15,8,796,22,16,24,15,8,15,8,15,8,15,8, + 808,8,810,8,15,8,15,15,18,8,8,8,820,8,822,8,15,8,826,8,828,8,15,12,16,7,8,7, + 26,25,838,8,840,8,20,8,10,8,16,15,15,12,22,7,852,16,15,22,856,7,858,22,15,24, + 862,26,15,7,15,8,15,9,15,7,15,7,15,7,876,8,15,15,880,8,882,8,15,7,886,7,15, + 8,15,10,18,8,15,13,15,8,28,27,16,8,8,8,22,8,906,8,18,10,910,15,14,8,15,16,10, + 18,918,24,8,22,12,24,24,26,8,28,928,7,18,7,7,7,14,7,936,7,15,7,940,7,22,15, + 15,7,946,7,12,12,15,7,952,7,15,7,15,8,15,15,960,29,15,8,15,8,966,8,15,8,970, + 10,18,12,15,15,976,16,18,18,15,7,982,27,15,24,15,26,22,28,990,31,31,7,15,8, + 996,25,26] From e64be9845ccf9a763bc6b89dcb6530e9962bbe92 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Mon, 2 Jun 2014 09:08:01 +0200 Subject: [PATCH 362/546] trac #16423: tiny code improvement and alignment --- src/sage/combinat/designs/latin_squares.py | 112 ++++++++++++--------- 1 file changed, 63 insertions(+), 49 deletions(-) diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 62c3a497cfa..65e0b08b361 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -477,63 +477,77 @@ def MOLS_table(number_of_lines,compare=False): global Handbook_2ed_table hb = Handbook_2ed_table from string import join - print " "+join(['%3s'%str(i) for i in range(20)]) - print " "+"_"*80, + print " " + join('%3d'%i for i in range(20)) + print " " + "_"*80, for i in range(20*number_of_lines): if i%20==0: - print "\n"+'%3s'%str(i)+"|", - + print "\n%3d|"%i, k = mutually_orthogonal_latin_squares(i,None,existence=True) if compare: - if i < 2: - c = " " - elif hb[i] == k: - c = " " + if i < 2 or hb[i] == k: + c = "" elif hb[i] < k: - c = " +" + c = "+" else: - c = " -" + c = "-" else: if i < 2: c = "+oo" else: c = k - print '{:3}'.format(c), - -# MOLS table from the Handbook of Combinatorial Designs 2ed -Handbook_2ed_table = [None,None,1,2,3,4,1,6,7,8,2,10,5,12,3,4,15,16,3,18,4,5,3, - 22,7,24,4,26,5,28,4,30,31,5,4,5,8,36,4,5,7,40,5,42,5,6,4,46,8,48,6,5,5,52,5, - 6,7,7,5,58,4,60,5,6,63,7,5,66,5,6,6,70,7,72,5,7,6,6,6,78,9,80,8,82,6,6,6,6, - 7,88,6,7,6,6,6,6,7,96,6,8,8,100,6,102,7,7,6,106,6,108,6,6,13,112,6,7,6,8,6, - 6,7,120,6,6,6,124,6,126,127,7,6,130,6,7,6,7,7,136,6,138,6,7,6,10,10,7,6,7,6, - 148,6,150,7,8,8,7,6,156,7,6,9,7,6,162,6,7,6,166,7,168,6,8,6,172,6,6,14,9,6, - 178,6,180,6,6,7,9,6,10,6,8,6,190,7,192,6,7,6,196,6,198,7,8,6,7,6,8,6,8,14,11, - 10,210,6,7,6,7,7,8,6,10,6,12,6,222,13,8,6,226,6,228,6,7,7,232,6,7,6,7,6,238, - 7,240,6,242,6,7,6,12,7,7,6,250,6,12,9,7,255,256,6,12,6,8,8,262,7,8,7,10,7,268, - 7,270,15,16,6,13,10,276,6,9,7,280,6,282,6,12,6,7,15,288,6,6,6,292,6,6,7,10, - 10,12,7,7,7,7,15,15,6,306,7,7,7,310,7,312,7,10,7,316,7,10,15,15,6,16,8,12,6, - 7,7,9,6,330,7,8,7,6,8,336,6,7,6,10,10,342,7,7,6,346,6,348,8,12,18,352,6,9,7, - 9,6,358,8,360,6,7,7,10,6,366,15,15,7,15,7,372,7,15,7,13,7,378,7,12,7,382,15, - 15,7,15,7,388,7,16,7,8,7,7,8,396,7,7,15,400,7,15,11,8,7,15,8,408,7,13,8,12, - 10,9,18,15,7,418,7,420,7,15,7,16,6,7,7,10,6,430,15,432,6,15,6,18,7,438,7,15, - 7,442,7,13,7,11,15,448,7,15,7,7,7,15,7,456,7,16,7,460,7,462,15,15,7,466,8,8, - 7,15,7,15,10,18,7,15,6,478,15,15,6,15,8,7,6,486,7,15,6,490,6,16,6,7,15,15,6, - 498,7,12,9,502,7,15,6,15,7,508,6,15,511,18,7,15,8,12,8,15,8,520,10,522,12,15, - 8,16,15,528,7,15,8,12,7,15,8,15,10,15,12,540,7,15,18,7,7,546,7,8,7,18,7,7,7, - 7,7,556,7,12,15,7,7,562,7,7,6,7,7,568,6,570,7,7,15,22,8,576,7,7,7,8,7,10,7, - 8,7,586,7,18,17,7,15,592,8,15,7,7,8,598,14,600,12,15,7,15,16,606,18,15,7,15, - 8,612,8,15,7,616,7,618,8,22,8,15,15,624,7,8,8,16,7,630,7,8,7,8,7,12,7,8,9,640, - 7,642,7,7,7,646,8,10,7,7,7,652,7,7,15,15,7,658,7,660,7,15,7,15,7,22,7,15,7, - 15,15,672,7,24,8,676,7,15,7,15,7,682,8,15,7,15,15,15,7,690,8,15,7,15,7,16,7, - 15,8,700,7,18,15,15,7,15,8,708,7,15,7,22,21,15,7,15,8,718,15,9,8,12,10,24,12, - 726,7,728,16,16,18,732,7,7,22,10,8,738,7,7,7,742,7,15,7,8,7,10,7,750,15,15, - 8,15,8,756,8,15,7,760,8,15,8,15,8,15,15,768,8,15,8,772,8,24,23,15,8,18,8,18, - 7,26,15,15,10,786,12,15,7,15,20,15,18,15,8,796,22,16,24,15,8,15,8,15,8,15,8, - 808,8,810,8,15,8,15,15,18,8,8,8,820,8,822,8,15,8,826,8,828,8,15,12,16,7,8,7, - 26,25,838,8,840,8,20,8,10,8,16,15,15,12,22,7,852,16,15,22,856,7,858,22,15,24, - 862,26,15,7,15,8,15,9,15,7,15,7,15,7,876,8,15,15,880,8,882,8,15,7,886,7,15, - 8,15,10,18,8,15,13,15,8,28,27,16,8,8,8,22,8,906,8,18,10,910,15,14,8,15,16,10, - 18,918,24,8,22,12,24,24,26,8,28,928,7,18,7,7,7,14,7,936,7,15,7,940,7,22,15, - 15,7,946,7,12,12,15,7,952,7,15,7,15,8,15,15,960,29,15,8,15,8,966,8,15,8,970, - 10,18,12,15,15,976,16,18,18,15,7,982,27,15,24,15,26,22,28,990,31,31,7,15,8, - 996,25,26] + print '{:>3}'.format(c), + +# MOLS table from the Handbook of Combinatorial Designs 2ed (page 176) +Handbook_2ed_table = [ + None,None, + 1, 2, 3, 4, 1, 6, 7, 8, 2, 10, 5, 12, 3, 4, 15, 16, 3, 18, + 4, 5, 3, 22, 7, 24, 4, 26, 5, 28, 4, 30, 31, 5, 4, 5, 8, 36, 4, 5, + 7, 40, 5, 42, 5, 6, 4, 46, 8, 48, 6, 5, 5, 52, 5, 6, 7, 7, 5, 58, + 4, 60, 5, 6, 63, 7, 5, 66, 5, 6, 6, 70, 7, 72, 5, 7, 6, 6, 6, 78, + 9, 80, 8, 82, 6, 6, 6, 6, 7, 88, 6, 7, 6, 6, 6, 6, 7, 96, 6, 8, + 8,100, 6,102, 7, 7, 6,106, 6,108, 6, 6, 13,112, 6, 7, 6, 8, 6, 6, + 7,120, 6, 6, 6,124, 6,126,127, 7, 6,130, 6, 7, 6, 7, 7,136, 6,138, + 6, 7, 6, 10, 10, 7, 6, 7, 6,148, 6,150, 7, 8, 8, 7, 6,156, 7, 6, + 9, 7, 6,162, 6, 7, 6,166, 7,168, 6, 8, 6,172, 6, 6, 14, 9, 6,178, + 6,180, 6, 6, 7, 9, 6, 10, 6, 8, 6,190, 7,192, 6, 7, 6,196, 6,198, + 7, 8, 6, 7, 6, 8, 6, 8, 14, 11, 10,210, 6, 7, 6, 7, 7, 8, 6, 10, + 6, 12, 6,222, 13, 8, 6,226, 6,228, 6, 7, 7,232, 6, 7, 6, 7, 6,238, + 7,240, 6,242, 6, 7, 6, 12, 7, 7, 6,250, 6, 12, 9, 7,255,256, 6, 12, + 6, 8, 8,262, 7, 8, 7, 10, 7,268, 7,270, 15, 16, 6, 13, 10,276, 6, 9, + 7,280, 6,282, 6, 12, 6, 7, 15,288, 6, 6, 6,292, 6, 6, 7, 10, 10, 12, + 7, 7, 7, 7, 15, 15, 6,306, 7, 7, 7,310, 7,312, 7, 10, 7,316, 7, 10, + 15, 15, 6, 16, 8, 12, 6, 7, 7, 9, 6,330, 7, 8, 7, 6, 8,336, 6, 7, + 6, 10, 10,342, 7, 7, 6,346, 6,348, 8, 12, 18,352, 6, 9, 7, 9, 6,358, + 8,360, 6, 7, 7, 10, 6,366, 15, 15, 7, 15, 7,372, 7, 15, 7, 13, 7,378, + 7, 12, 7,382, 15, 15, 7, 15, 7,388, 7, 16, 7, 8, 7, 7, 8,396, 7, 7, + 15,400, 7, 15, 11, 8, 7, 15, 8,408, 7, 13, 8, 12, 10, 9, 18, 15, 7,418, + 7,420, 7, 15, 7, 16, 6, 7, 7, 10, 6,430, 15,432, 6, 15, 6, 18, 7,438, + 7, 15, 7,442, 7, 13, 7, 11, 15,448, 7, 15, 7, 7, 7, 15, 7,456, 7, 16, + 7,460, 7,462, 15, 15, 7,466, 8, 8, 7, 15, 7, 15, 10, 18, 7, 15, 6,478, + 15, 15, 6, 15, 8, 7, 6,486, 7, 15, 6,490, 6, 16, 6, 7, 15, 15, 6,498, + 7, 12, 9,502, 7, 15, 6, 15, 7,508, 6, 15,511, 18, 7, 15, 8, 12, 8, 15, + 8,520, 10,522, 12, 15, 8, 16, 15,528, 7, 15, 8, 12, 7, 15, 8, 15, 10, 15, + 12,540, 7, 15, 18, 7, 7,546, 7, 8, 7, 18, 7, 7, 7, 7, 7,556, 7, 12, + 15, 7, 7,562, 7, 7, 6, 7, 7,568, 6,570, 7, 7, 15, 22, 8,576, 7, 7, + 7, 8, 7, 10, 7, 8, 7,586, 7, 18, 17, 7, 15,592, 8, 15, 7, 7, 8,598, + 14,600, 12, 15, 7, 15, 16,606, 18, 15, 7, 15, 8,612, 8, 15, 7,616, 7,618, + 8, 22, 8, 15, 15,624, 7, 8, 8, 16, 7,630, 7, 8, 7, 8, 7, 12, 7, 8, + 9,640, 7,642, 7, 7, 7,646, 8, 10, 7, 7, 7,652, 7, 7, 15, 15, 7,658, + 7,660, 7, 15, 7, 15, 7, 22, 7, 15, 7, 15, 15,672, 7, 24, 8,676, 7, 15, + 7, 15, 7,682, 8, 15, 7, 15, 15, 15, 7,690, 8, 15, 7, 15, 7, 16, 7, 15, + 8,700, 7, 18, 15, 15, 7, 15, 8,708, 7, 15, 7, 22, 21, 15, 7, 15, 8,718, + 15, 9, 8, 12, 10, 24, 12,726, 7,728, 16, 16, 18,732, 7, 7, 22, 10, 8,738, + 7, 7, 7,742, 7, 15, 7, 8, 7, 10, 7,750, 15, 15, 8, 15, 8,756, 8, 15, + 7,760, 8, 15, 8, 15, 8, 15, 15,768, 8, 15, 8,772, 8, 24, 23, 15, 8, 18, + 8, 18, 7, 26, 15, 15, 10,786, 12, 15, 7, 15, 20, 15, 18, 15, 8,796, 22, 16, + 24, 15, 8, 15, 8, 15, 8, 15, 8,808, 8,810, 8, 15, 8, 15, 15, 18, 8, 8, + 8,820, 8,822, 8, 15, 8,826, 8,828, 8, 15, 12, 16, 7, 8, 7, 26, 25,838, + 8,840, 8, 20, 8, 10, 8, 16, 15, 15, 12, 22, 7,852, 16, 15, 22,856, 7,858, + 22, 15, 24,862, 26, 15, 7, 15, 8, 15, 9, 15, 7, 15, 7, 15, 7,876, 8, 15, + 15,880, 8,882, 8, 15, 7,886, 7, 15, 8, 15, 10, 18, 8, 15, 13, 15, 8, 28, + 27, 16, 8, 8, 8, 22, 8,906, 8, 18, 10,910, 15, 14, 8, 15, 16, 10, 18,918, + 24, 8, 22, 12, 24, 24, 26, 8, 28,928, 7, 18, 7, 7, 7, 14, 7,936, 7, 15, + 7,940, 7, 22, 15, 15, 7,946, 7, 12, 12, 15, 7,952, 7, 15, 7, 15, 8, 15, + 15,960, 29, 15, 8, 15, 8,966, 8, 15, 8,970, 10, 18, 12, 15, 15,976, 16, 18, + 18, 15, 7,982, 27, 15, 24, 15, 26, 22, 28,990, 31, 31, 7, 15, 8,996, 25, 26 +] From e948cf64a435f48206b3f46ff9693201c5675ed5 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Mon, 2 Jun 2014 11:05:59 +0200 Subject: [PATCH 363/546] trac #16423: Aligning the alignment --- src/sage/combinat/designs/latin_squares.py | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 65e0b08b361..8ec97c83a65 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -500,19 +500,19 @@ def MOLS_table(number_of_lines,compare=False): # MOLS table from the Handbook of Combinatorial Designs 2ed (page 176) Handbook_2ed_table = [ None,None, - 1, 2, 3, 4, 1, 6, 7, 8, 2, 10, 5, 12, 3, 4, 15, 16, 3, 18, - 4, 5, 3, 22, 7, 24, 4, 26, 5, 28, 4, 30, 31, 5, 4, 5, 8, 36, 4, 5, - 7, 40, 5, 42, 5, 6, 4, 46, 8, 48, 6, 5, 5, 52, 5, 6, 7, 7, 5, 58, - 4, 60, 5, 6, 63, 7, 5, 66, 5, 6, 6, 70, 7, 72, 5, 7, 6, 6, 6, 78, - 9, 80, 8, 82, 6, 6, 6, 6, 7, 88, 6, 7, 6, 6, 6, 6, 7, 96, 6, 8, - 8,100, 6,102, 7, 7, 6,106, 6,108, 6, 6, 13,112, 6, 7, 6, 8, 6, 6, - 7,120, 6, 6, 6,124, 6,126,127, 7, 6,130, 6, 7, 6, 7, 7,136, 6,138, - 6, 7, 6, 10, 10, 7, 6, 7, 6,148, 6,150, 7, 8, 8, 7, 6,156, 7, 6, - 9, 7, 6,162, 6, 7, 6,166, 7,168, 6, 8, 6,172, 6, 6, 14, 9, 6,178, - 6,180, 6, 6, 7, 9, 6, 10, 6, 8, 6,190, 7,192, 6, 7, 6,196, 6,198, - 7, 8, 6, 7, 6, 8, 6, 8, 14, 11, 10,210, 6, 7, 6, 7, 7, 8, 6, 10, - 6, 12, 6,222, 13, 8, 6,226, 6,228, 6, 7, 7,232, 6, 7, 6, 7, 6,238, - 7,240, 6,242, 6, 7, 6, 12, 7, 7, 6,250, 6, 12, 9, 7,255,256, 6, 12, + 1, 2, 3, 4, 1, 6, 7, 8, 2, 10, 5, 12, 3, 4, 15, 16, 3, 18, + 4, 5, 3, 22, 7, 24, 4, 26, 5, 28, 4, 30, 31, 5, 4, 5, 8, 36, 4, 5, + 7, 40, 5, 42, 5, 6, 4, 46, 8, 48, 6, 5, 5, 52, 5, 6, 7, 7, 5, 58, + 4, 60, 5, 6, 63, 7, 5, 66, 5, 6, 6, 70, 7, 72, 5, 7, 6, 6, 6, 78, + 9, 80, 8, 82, 6, 6, 6, 6, 7, 88, 6, 7, 6, 6, 6, 6, 7, 96, 6, 8, + 8,100, 6,102, 7, 7, 6,106, 6,108, 6, 6, 13,112, 6, 7, 6, 8, 6, 6, + 7,120, 6, 6, 6,124, 6,126,127, 7, 6,130, 6, 7, 6, 7, 7,136, 6,138, + 6, 7, 6, 10, 10, 7, 6, 7, 6,148, 6,150, 7, 8, 8, 7, 6,156, 7, 6, + 9, 7, 6,162, 6, 7, 6,166, 7,168, 6, 8, 6,172, 6, 6, 14, 9, 6,178, + 6,180, 6, 6, 7, 9, 6, 10, 6, 8, 6,190, 7,192, 6, 7, 6,196, 6,198, + 7, 8, 6, 7, 6, 8, 6, 8, 14, 11, 10,210, 6, 7, 6, 7, 7, 8, 6, 10, + 6, 12, 6,222, 13, 8, 6,226, 6,228, 6, 7, 7,232, 6, 7, 6, 7, 6,238, + 7,240, 6,242, 6, 7, 6, 12, 7, 7, 6,250, 6, 12, 9, 7,255,256, 6, 12, 6, 8, 8,262, 7, 8, 7, 10, 7,268, 7,270, 15, 16, 6, 13, 10,276, 6, 9, 7,280, 6,282, 6, 12, 6, 7, 15,288, 6, 6, 6,292, 6, 6, 7, 10, 10, 12, 7, 7, 7, 7, 15, 15, 6,306, 7, 7, 7,310, 7,312, 7, 10, 7,316, 7, 10, From 0a7d853bf758ac7abce1acfe54e18c5ea784d21f Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 13 Jun 2014 13:27:36 +0200 Subject: [PATCH 364/546] trac #16423: Broken doctests --- src/sage/combinat/designs/latin_squares.py | 47 +++++++++++----------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 8ec97c83a65..8f07c85c38a 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -46,22 +46,21 @@ sage: MOLS_table(15,compare=True) # long time 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ________________________________________________________________________________ - 0| + + 0| + + 20| - 40| - - - 60| + - - - - - - - 80| - - - - - - - 100| - - - - - - - - - 120| - - - - - - - - - 140| - - - - - - 160| - - - - - - - 180| - - - - 200| - - - - - - - 220| - - - - - 240| - - - - - - 260| - - - - - - - - - - 280| - - - + 40| + 60| + + 80| - - + 100| - - - + 120| - - - + 140| - - + 160| - - - + 180| - - + 200| - - - + 220| - - - + 240| - - - + 260| - - - - - + 280| TODO: @@ -460,19 +459,19 @@ def MOLS_table(number_of_lines,compare=False): sage: MOLS_table(5) # long time 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ________________________________________________________________________________ - 0| +oo +oo 1 2 3 4 1 6 7 8 2 10 5 12 4 4 15 16 3 18 + 0| +oo +oo 1 2 3 4 1 6 7 8 2 10 5 12 4 4 15 16 5 18 20| 4 5 3 22 7 24 4 26 5 28 4 30 31 5 4 5 8 36 4 5 - 40| 7 40 5 42 5 6 4 46 8 48 6 5 5 52 5 6 7 3 2 58 - 60| 5 60 5 6 63 7 2 66 4 4 6 70 7 72 3 7 3 6 3 78 - 80| 9 80 8 82 6 6 6 3 7 88 3 6 4 4 3 6 7 96 6 8 + 40| 7 40 5 42 5 6 4 46 8 48 6 5 5 52 5 6 7 7 5 58 + 60| 5 60 5 6 63 7 5 66 5 6 6 70 7 72 5 7 6 6 6 78 + 80| 9 80 8 82 6 6 6 6 7 88 5 6 6 6 6 6 7 96 6 8 sage: MOLS_table(5,compare=True) # long time - 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ________________________________________________________________________________ - 0| + + 0| + + 20| - 40| - - - 60| + - - - - - - - 80| - - - - - - + 40| + 60| + + 80| - - """ global Handbook_2ed_table hb = Handbook_2ed_table From b3293519d314c2bd98c8074a4bd24f1535d92247 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 13 Jun 2014 15:19:23 +0200 Subject: [PATCH 365/546] trac #16499: Cheap speedup in the OA recursive constructions --- src/sage/combinat/designs/latin_squares.py | 14 +-- .../combinat/designs/orthogonal_arrays.py | 92 +++++++++++++------ 2 files changed, 71 insertions(+), 35 deletions(-) diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 8f07c85c38a..8e1d3318c20 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -310,7 +310,7 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi [4 5 6 7 1 2 3 9 0 8], [7 1 2 3 4 5 6 9 8 0] ] """ - from sage.combinat.designs.orthogonal_arrays import orthogonal_array, _set_OA_cache, _get_OA_cache + from sage.combinat.designs.orthogonal_arrays import orthogonal_array, _OA_cache_set, _OA_cache_get, _OA_cache_construction_available from sage.matrix.constructor import Matrix from sage.rings.arith import factor from database import MOLS_constructions @@ -327,8 +327,10 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi if existence: return k - if existence and not who_asked and _get_OA_cache(k+2,n) is not None: - return _get_OA_cache(k+2,n) + if existence and not who_asked and _OA_cache_get(k+2,n) is not None: + return _OA_cache_get(k+2,n) + + may_be_available = _OA_cache_construction_available(k+2,n) is not False if n == 1: if existence: @@ -340,8 +342,8 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi return False raise EmptySetError("There exist at most n-1 MOLS of size n if n>=2.") - elif n in MOLS_constructions and k <= MOLS_constructions[n][0]: - _set_OA_cache(MOLS_constructions[n][0]+2,n,True) + elif may_be_available and n in MOLS_constructions and k <= MOLS_constructions[n][0]: + _OA_cache_set(MOLS_constructions[n][0]+2,n,True) if existence: return True _, construction = MOLS_constructions[n] @@ -375,7 +377,7 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi else: if not who_asked: - _set_OA_cache(k+2,n,Unknown) + _OA_cache_set(k+2,n,Unknown) if existence: return Unknown raise NotImplementedError("I don't know how to build {} MOLS of order {}".format(k,n)) diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 2022eb02625..011a37c604e 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -277,8 +277,10 @@ def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): if existence: return k - if existence and not who_asked and _get_OA_cache(k,n) is not None: - return _get_OA_cache(k,n) + if existence and not who_asked and _OA_cache_get(k,n) is not None: + return _OA_cache_get(k,n) + + may_be_available = _OA_cache_construction_available(k,n) is not False if n == 1: if existence: @@ -291,14 +293,14 @@ def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): raise EmptySetError("No Transversal Design exists when k>=n+2 if n>=2") elif n == 12 and k <= 6: - _set_OA_cache(6,12,True) + _OA_cache_set(6,12,True) if existence: return True from sage.combinat.designs.database import TD_6_12 TD = [l[:k] for l in TD_6_12()] - elif TD_find_product_decomposition(k,n): - _set_OA_cache(k,n,True) + elif may_be_available and TD_find_product_decomposition(k,n): + _OA_cache_set(k,n,True) if existence: return True n1,n2 = TD_find_product_decomposition(k,n) @@ -324,7 +326,7 @@ def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): else: if not who_asked: - _set_OA_cache(k,n,Unknown) + _OA_cache_set(k,n,Unknown) if existence: return Unknown raise NotImplementedError("I don't know how to build a TD({},{})!".format(k,n)) @@ -665,7 +667,7 @@ def TD_product(k,TD1,n1,TD2,n2, check=True): # truth_value. _OA_cache = {0:(Infinity,None,None,None),1:(Infinity,None,None,None)} -def _set_OA_cache(k,n,truth_value): +def _OA_cache_set(k,n,truth_value): r""" Sets a value in the OA cache of existence results @@ -677,12 +679,12 @@ def _set_OA_cache(k,n,truth_value): EXAMPLES:: - sage: from sage.combinat.designs.orthogonal_arrays import _set_OA_cache, _get_OA_cache, _OA_cache + sage: from sage.combinat.designs.orthogonal_arrays import _OA_cache_set, _OA_cache_get, _OA_cache sage: if 10 in _OA_cache: ....: del _OA_cache[10] - sage: _get_OA_cache(4,10) - sage: _set_OA_cache(4,10,True) - sage: _get_OA_cache(4,10) + sage: _OA_cache_get(4,10) + sage: _OA_cache_set(4,10,True) + sage: _OA_cache_get(4,10) True """ global _OA_cache @@ -702,7 +704,7 @@ def _set_OA_cache(k,n,truth_value): _OA_cache[n] = (max_true, min_unknown, max_unknown, min_false) -def _get_OA_cache(k,n): +def _OA_cache_get(k,n): r""" Gets a value from the OA cache of existence results @@ -712,16 +714,16 @@ def _get_OA_cache(k,n): EXAMPLES:: - sage: from sage.combinat.designs.orthogonal_arrays import _set_OA_cache, _get_OA_cache - sage: _get_OA_cache(0,10) + sage: from sage.combinat.designs.orthogonal_arrays import _OA_cache_set, _OA_cache_get + sage: _OA_cache_get(0,10) True - sage: _get_OA_cache(1,10) + sage: _OA_cache_get(1,10) True - sage: _get_OA_cache(2,10) + sage: _OA_cache_get(2,10) True - sage: _get_OA_cache(2**10+1,2**10) - sage: _set_OA_cache(2**10+1,2**10,True) - sage: _get_OA_cache(2**10+1,2**10) + sage: _OA_cache_get(2**10+1,2**10) + sage: _OA_cache_set(2**10+1,2**10,True) + sage: _OA_cache_get(2**10+1,2**10) True """ global _OA_cache @@ -743,6 +745,36 @@ def _get_OA_cache(k,n): return None +def _OA_cache_construction_available(k,n): + r""" + Tests if a construction is implemented using the cache's information + + INPUT: + + - ``k,n`` (integers) + + EXAMPLES:: + + sage: from sage.combinat.designs.orthogonal_arrays import _OA_cache_construction_available + sage: _OA_cache_construction_available(5,10) + Unknown + sage: designs.orthogonal_array(5,10,existence=True) + Unknown + sage: _OA_cache_construction_available(5,10) + False + """ + ans = _OA_cache.get(n,None) + if ans is not None: + max_true, min_unknown, max_unknown, min_false = ans + if k <= max_true: + return True + if min_unknown is not None and k >= min_unknown: + return False + else: + return Unknown + else: + return Unknown + def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): r""" Return an orthogonal array of parameters `k,n,t`. @@ -918,8 +950,10 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): if k < t: raise ValueError("undefined for k Date: Wed, 25 Jun 2014 16:11:38 +0200 Subject: [PATCH 366/546] trac #16504: Updates the tutorial --- .../thematic_tutorials/linear_programming.rst | 36 ++++++++----------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/src/doc/en/thematic_tutorials/linear_programming.rst b/src/doc/en/thematic_tutorials/linear_programming.rst index 809b2bbae3e..e7e896c7caf 100644 --- a/src/doc/en/thematic_tutorials/linear_programming.rst +++ b/src/doc/en/thematic_tutorials/linear_programming.rst @@ -80,15 +80,14 @@ Let us ask Sage to solve the following LP: \text{Max: } & x + y - 3z\\ \text{Such that: } & x + 2y \leq 4\\ \text{} & 5z - y \leq 8\\ + \text{} & x,y,z \geq 0\\ To achieve it, we need to define a corresponding ``MILP`` object, along with 3 -variables ``x,y`` and ``z`` (which by default can only take **non-negative -values**, see next section):: +variables ``x,y`` and ``z``:: sage: p = MixedIntegerLinearProgram() - sage: x, y, z = p['x'], p['y'], p['z'] - doctest:839: DeprecationWarning: The default behaviour of new_variable() will soon change ! It will return 'real' variables instead of nonnegative ones. Please be explicit and call new_variable(nonnegative=True) instead. - See http://trac.sagemath.org/15521 for details. + sage: v = p.new_variable(real=True, nonnegative=True) + sage: x, y, z = v['x'], v['y'], v['z'] Next, we set the objective function @@ -135,21 +134,16 @@ We can read the optimal assignation found by the solver for `x, y` and Variables ^^^^^^^^^ -In the previous example, we obtained variables through ``p['x'], p['y']`` and -``p['z']``, which is a convenient shortcut when our LP is defined over a small -number of variables. This being said, larger LP/MILP will require us to -associate a LP variable to many Sage objects, which can be integers, strings, or -even the vertices and edges of a graph. We use in this case an alternative -syntax. - -If we need 15 variables `x_1, \dots, x_{15}`, we will first define a dictionary -of variables with the ``new_variable`` method +In the previous example, we obtained variables through ``v['x'], v['y']`` and +``v['z']``. This being said, larger LP/MILP will require us to associate a LP +variable to many Sage objects, which can be integers, strings, or even the +vertices and edges of a graph. For example: .. link :: - sage: x = p.new_variable() + sage: x = p.new_variable(real=True, nonnegative=True) With this new object ``x`` we can now write constraints using ``x[1],...,x[15]``. @@ -185,16 +179,14 @@ And because any immutable object can be used as a key, doubly indexed variables Typed variables and bounds """""""""""""""""""""""""" -By default, all the LP variables represent **non-negative reals**. - **Types :** If you want a variable to assume only integer or binary values, use the ``integer=True`` or ``binary=True`` arguments of the ``new_variable`` method. Alternatively, call the ``set_integer`` and ``set_binary`` methods. -**Bounds :** By default all variables represent **non-negative reals**. If you -want a variable to represent arbitrary reals (or arbitrary integers), you should -redefine its lower bound using the ``set_min`` method. If you want to set an -upper bound on a variable, use the ``set_max`` method. +**Bounds :** If you want your variables to only take nonnegative values, you can +say so when calling ``new_variable`` with the argument ``nonnegative=True``. If +you want to set a different upper/lower bound on a variable, add a constraint or +use the use the ``set_min``, ``set_max`` methods. Basic linear programs --------------------- @@ -409,7 +401,7 @@ graph, in which all the edges have a capacity of 1:: :: sage: p = MixedIntegerLinearProgram() - sage: f = p.new_variable() + sage: f = p.new_variable(real=True, nonnegative=True) sage: s, t = 0, 2 .. link From a67c04f13bd0aa2b46e6998d2b996437f342470c Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Mon, 16 Jun 2014 14:36:26 +0200 Subject: [PATCH 367/546] trac #16500: New recursive constructions of Orthogonal Arrays --- src/doc/en/reference/combinat/designs.rst | 1 + src/doc/en/reference/combinat/index.rst | 2 +- src/sage/combinat/designs/latin_squares.py | 40 +- .../combinat/designs/orthogonal_arrays.py | 7 + .../designs/orthogonal_arrays_recursive.py | 561 ++++++++++++++++++ 5 files changed, 590 insertions(+), 21 deletions(-) create mode 100644 src/sage/combinat/designs/orthogonal_arrays_recursive.py diff --git a/src/doc/en/reference/combinat/designs.rst b/src/doc/en/reference/combinat/designs.rst index b18fd7373d4..9fbb579f291 100644 --- a/src/doc/en/reference/combinat/designs.rst +++ b/src/doc/en/reference/combinat/designs.rst @@ -20,6 +20,7 @@ Constructions ../sage/combinat/designs/steiner_quadruple_systems ../sage/combinat/designs/latin_squares ../sage/combinat/designs/orthogonal_arrays + ../sage/combinat/designs/orthogonal_arrays_recursive ../sage/combinat/designs/difference_family ../sage/combinat/designs/database diff --git a/src/doc/en/reference/combinat/index.rst b/src/doc/en/reference/combinat/index.rst index 681522c304f..da6bad48c9d 100644 --- a/src/doc/en/reference/combinat/index.rst +++ b/src/doc/en/reference/combinat/index.rst @@ -26,6 +26,7 @@ Combinatorics sage/combinat/alternating_sign_matrix sage/combinat/composition sage/combinat/core + designs sage/combinat/knutson_tao_puzzles sage/combinat/gelfand_tsetlin_patterns sage/combinat/necklace @@ -190,7 +191,6 @@ Combinatorics root_systems crystals rigged_configurations - designs species **Developer Tools** diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 8e1d3318c20..7e124b2e3cd 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -28,16 +28,16 @@ 20| 4 5 3 22 7 24 4 26 5 28 4 30 31 5 4 5 8 36 4 5 40| 7 40 5 42 5 6 4 46 8 48 6 5 5 52 5 6 7 7 5 58 60| 5 60 5 6 63 7 5 66 5 6 6 70 7 72 5 7 6 6 6 78 - 80| 9 80 8 82 6 6 6 6 7 88 5 6 6 6 6 6 7 96 6 8 - 100| 8 100 6 102 7 7 6 106 6 108 6 6 7 112 6 7 5 8 4 6 - 120| 6 120 6 6 6 124 6 126 127 7 6 130 6 6 6 6 7 136 6 138 - 140| 6 7 6 10 10 7 6 7 6 148 6 150 7 8 8 6 6 156 6 6 - 160| 7 7 6 162 5 7 6 166 7 168 6 8 6 172 6 6 10 9 6 178 - 180| 6 180 6 6 7 8 6 10 6 6 6 190 7 192 6 7 6 196 6 198 - 200| 7 7 6 7 6 6 6 8 12 11 10 210 6 7 6 7 7 8 6 10 - 220| 6 12 6 222 7 8 6 226 6 228 6 6 7 232 6 7 6 6 6 238 - 240| 7 240 6 242 6 7 6 12 7 7 6 250 6 10 7 7 255 256 6 7 - 260| 6 8 7 262 7 8 6 10 6 268 7 270 15 16 6 10 10 276 6 8 + 80| 9 80 8 82 6 6 6 6 7 88 6 7 6 6 6 6 7 96 6 8 + 100| 8 100 6 102 7 7 6 106 6 108 6 6 7 112 6 7 6 8 6 6 + 120| 6 120 6 6 6 124 6 126 127 7 6 130 6 7 6 6 7 136 6 138 + 140| 6 7 6 10 10 7 6 7 6 148 6 150 7 8 8 7 6 156 6 6 + 160| 7 7 6 162 6 7 6 166 7 168 6 8 6 172 6 6 10 9 6 178 + 180| 6 180 6 6 7 8 6 10 6 7 6 190 7 192 6 7 6 196 6 198 + 200| 7 7 6 7 6 7 6 8 12 11 10 210 6 7 6 7 7 8 6 10 + 220| 6 12 6 222 7 8 6 226 6 228 6 7 7 232 6 7 6 7 6 238 + 240| 7 240 6 242 6 7 6 12 7 7 6 250 6 10 7 7 255 256 6 12 + 260| 6 8 7 262 7 8 7 10 7 268 7 270 15 16 6 13 10 276 6 9 280| 7 280 6 282 6 12 6 7 15 288 6 6 6 292 6 6 7 10 10 12 @@ -50,16 +50,16 @@ 20| 40| 60| + - 80| - - - 100| - - - - 120| - - - - 140| - - - 160| - - - + 80| + 100| - + 120| - - + 140| - + 160| - - 180| - - 200| - - - - 220| - - - - 240| - - - - 260| - - - - - + 220| - + 240| - - + 260| - 280| TODO: @@ -465,7 +465,7 @@ def MOLS_table(number_of_lines,compare=False): 20| 4 5 3 22 7 24 4 26 5 28 4 30 31 5 4 5 8 36 4 5 40| 7 40 5 42 5 6 4 46 8 48 6 5 5 52 5 6 7 7 5 58 60| 5 60 5 6 63 7 5 66 5 6 6 70 7 72 5 7 6 6 6 78 - 80| 9 80 8 82 6 6 6 6 7 88 5 6 6 6 6 6 7 96 6 8 + 80| 9 80 8 82 6 6 6 6 7 88 6 7 6 6 6 6 7 96 6 8 sage: MOLS_table(5,compare=True) # long time 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ________________________________________________________________________________ @@ -473,7 +473,7 @@ def MOLS_table(number_of_lines,compare=False): 20| 40| 60| + - 80| - - + 80| """ global Handbook_2ed_table hb = Handbook_2ed_table diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 011a37c604e..290d7debe0f 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -928,6 +928,7 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): from latin_squares import mutually_orthogonal_latin_squares from database import OA_constructions from block_design import projective_plane, projective_plane_to_OA + from orthogonal_arrays_recursive import find_recursive_construction assert n>=0 @@ -1023,6 +1024,12 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): OA = OA_relabel(OA,k+2,r,matrix=[range(r)]*k+[range(u1)+[None]*(r-u1),range(u2)+[None]*(r-u2)]) OA = wilson_construction(OA,k,r,m,2,[u1,u2],check=False) + elif may_be_available and find_recursive_construction(k,n,existence=True): + _OA_cache_set(k,n,True) + if existence: + return True + OA = find_recursive_construction(k,n) + elif (transversal_design not in who_asked and transversal_design(k,n,existence=True,who_asked=who_asked+(orthogonal_array,)) is not Unknown): diff --git a/src/sage/combinat/designs/orthogonal_arrays_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_recursive.py new file mode 100644 index 00000000000..3a768b2f6f6 --- /dev/null +++ b/src/sage/combinat/designs/orthogonal_arrays_recursive.py @@ -0,0 +1,561 @@ +r""" +Orthogonal arrays (Recursive constructions) + +This module implements several functions to find recursive constructions of +:mod:`Orthogonal Arrays ` using +Wilson's construction. To this end, they compute and truncate OA in specific +ways. + +The main function of this module, i.e. :func:`find_recursive_construction`, +queries all implemented recursive constructions of designs. It is used by +Sage's function +:func:`~sage.combinat.designs.orthogonal_arrays.orthogonal_array`. + +Functions +--------- +""" +from sage.misc.cachefunc import cached_function +from sage.misc.unknown import Unknown +from orthogonal_arrays import orthogonal_array, wilson_construction +from sage.combinat.designs.orthogonal_arrays import is_orthogonal_array + +def find_recursive_construction(k,n,existence=False): + r""" + Find a recursive construction of a `OA(k,n)` + + This method attempts to build an `OA(k,n)` through the following + constructions: + + - :func:`construction_3_3` + - :func:`construction_3_4` + - :func:`construction_3_5` + - :func:`construction_3_6` + + INPUT: + + - ``k,n`` (integers) + + - ``existence`` (boolean) -- whether to return the actual OA, or only + indicate whether it can be built. + + EXAMPLES:: + + sage: from sage.combinat.designs.orthogonal_arrays_recursive import find_recursive_construction + sage: from sage.combinat.designs.orthogonal_arrays import is_orthogonal_array + sage: count = 0 + sage: for n in range(10,150): + ....: k = designs.orthogonal_array(None,n,existence=True) + ....: if find_recursive_construction(k,n,existence=True): + ....: count = count + 1 + ....: OA = find_recursive_construction(k,n,existence=False) + ....: assert is_orthogonal_array(OA,k,n,2,verbose=True) + sage: print count + 40 + """ + for find_c in [find_construction_3_3, + find_construction_3_4, + find_construction_3_5, + find_construction_3_6]: + + if find_c(k,n): + if existence: + return True + else: + f,args = find_c(k,n) + return f(*args) + + return Unknown + +@cached_function +def find_construction_3_3(k,n): + r""" + Finds a decomposition for construction 3.3 + + INPUT: + + - ``k,n`` (integers) + + .. SEEALSO:: + + :func:`construction_3_3` + + OUTPUT: + + A pair ``f,args`` such that ``f(*args)`` returns the requested OA. + + EXAMPLES:: + + sage: from sage.combinat.designs.orthogonal_arrays_recursive import find_construction_3_3 + sage: find_construction_3_3(11,177)[1] + (11, 11, 16, 1) + sage: find_construction_3_3(12,11) + """ + for mm in range(2,n//2+1): + if (not orthogonal_array( k , mm , existence=True) or + not orthogonal_array( k ,mm+1, existence=True)): + continue + + for nn in range(2,n//mm+1): + i = n-nn*mm + if i<=0: + continue + + if (orthogonal_array(k+i, nn , existence=True) and + orthogonal_array( k ,mm+i, existence=True)): + return construction_3_3, (k,nn,mm,i) + +def construction_3_3(k,n,m,i): + r""" + Returns an `OA(k,nm+i)`. + + This is Wilson's construction with `i` truncated columns of size `1`, such + that a block `B_0` of the incomplete OA intersects all truncated + columns. There is a slight difference however, as the block `B_0` is only + considered up to its first `k` coordinates, and a `OA(k,i)` is used instead + of `iOA(k,1)` (thus there is no need for an `OA(k,m+i)`. + + This is construction 3.3 from [AC07]_. + + INPUT: + + - ``k,n,m,i`` (integers) such that the following designs are available : + `OA(k,n),OA(k,m),OA(k,m+1),OA(k,r)`. + + .. SEEALSO:: + + :func:`find_construction_3_3` + + EXAMPLES:: + + sage: from sage.combinat.designs.orthogonal_arrays_recursive import find_construction_3_3 + sage: from sage.combinat.designs.orthogonal_arrays_recursive import construction_3_3 + sage: from sage.combinat.designs.orthogonal_arrays import is_orthogonal_array + sage: k=11;n=177 + sage: is_orthogonal_array(construction_3_3(*find_construction_3_3(k,n)[1]),k,n,2) + True + """ + from orthogonal_arrays import OA_relabel, incomplete_orthogonal_array + # Builds an OA(k+i,n) containing a block [0]*(k+i) + OA = incomplete_orthogonal_array(k+i,n,(1,)) + OA = [[(x+1)%n for x in B] for B in OA] + + # Truncated version + OA = [B[:k]+[0 if x == 0 else None for x in B[k:]] for B in OA] + + OA = wilson_construction(OA,k,n,m,i,[1]*i,check=False)[:-i] + matrix = [range(m)+range(n*m,n*m+i)]*k + OA.extend(OA_relabel(orthogonal_array(k,m+i),k,m+i,matrix=matrix)) + assert is_orthogonal_array(OA,k,n*m+i) + return OA + +@cached_function +def find_construction_3_4(k,n): + r""" + Finds a decomposition for construction 3.4 + + INPUT: + + - ``k,n`` (integers) + + .. SEEALSO:: + + :func:`construction_3_4` + + OUTPUT: + + A pair ``f,args`` such that ``f(*args)`` returns the requested OA. + + EXAMPLES:: + + sage: from sage.combinat.designs.orthogonal_arrays_recursive import find_construction_3_4 + sage: find_construction_3_4(8,196)[1] + (8, 25, 7, 12, 9) + sage: find_construction_3_4(9,24) + """ + for mm in range(2,n//2+1): + if (not orthogonal_array(k,mm+0,existence=True) or + not orthogonal_array(k,mm+1,existence=True) or + not orthogonal_array(k,mm+2,existence=True)): + continue + + for nn in range(2,n//mm+1): + i = n-nn*mm + if i<=0: + continue + + for s in range(1,min(i,nn)): + r = i-s + if (orthogonal_array(k+r+1,nn,existence=True) and + orthogonal_array(k , s,existence=True) and + (orthogonal_array(k,mm+r,existence=True) or orthogonal_array(k,mm+r+1,existence=True))): + return construction_3_4, (k,nn,mm,r,s) + +def construction_3_4(k,n,m,r,s): + r""" + Returns a `OA(k,nm+rs)`. + + This is Wilson's construction applied to an incomplete `OA(k+r+1,n)` with + `k` columns of size 1 and a column of size `s`. + + The the unique elements of the `k` columns are picked so that a block `B_0` + contains them all. + + - If there exists an `OA(k,m+r+1)` the column of size `s` is truncated in + order to intersect `B_0`. + + - If there exists an `OA(k,m+r+1)`, the last column must not intersect `B_0` + + This is construction 3.4 from [AC07]_. + + INPUT: + + - ``k,n,m,r,s`` (integers) -- we assume that `s= n or + not orthogonal_array(k,mm+1,existence=True) or + not orthogonal_array(k,mm+2,existence=True) or + not orthogonal_array(k,mm+3,existence=True)): + continue + + for nn in range(2,n//mm+1): + i = n-nn*mm + if i<=0: + continue + + if not orthogonal_array(k+3,nn,existence=True): + continue + + for r,s,t in Compositions(i+3,length=3,max_part=nn): + # avoid warning on Compositions(...,min_part=0) + r = r-1 + s = s-1 + t = t-1 + if (r <= s and + (nn-r-1)*(nn-s) < t and + (r==0 or orthogonal_array(k,r,existence=True)) and + (s==0 or orthogonal_array(k,s,existence=True)) and + (t==0 or orthogonal_array(k,t,existence=True))): + return construction_3_5, (k,nn,mm,r,s,t) + +def construction_3_5(k,n,m,r,s,t): + r""" + Returns an `OA(k,nm+r+s+t)`. + + This is exactly Wilson's construction with three truncated groups + except we make sure that all blocks have size `>k`, so we don't + need a `OA(k,m+0)` but only `OA(k,m+1),OA(k,m+2),OA(k,m+3)`. + + This is construction 3.5 from [AC07]_. + + INPUT: + + - ``k,n,m`` (integers) + + - ``r,s,t`` (integers) -- sizes of the three truncated groups, + such that `r\leq s` and `(q-r-1)(q-s) \geq (q-s-1)*(q-r)`. + + The following designs must be available : + `OA(k,n),OA(k,r),OA(k,s),OA(k,t),OA(k,m+1),OA(k,m+2),OA(k,m+3)`. + + .. SEEALSO:: + + :func:`find_construction_3_5` + + EXAMPLES:: + + sage: from sage.combinat.designs.orthogonal_arrays_recursive import find_construction_3_5 + sage: from sage.combinat.designs.orthogonal_arrays_recursive import construction_3_5 + sage: from sage.combinat.designs.orthogonal_arrays import is_orthogonal_array + sage: k=8;n=111 + sage: is_orthogonal_array(construction_3_5(*find_construction_3_5(k,n)[1]),k,n,2) + True + """ + from orthogonal_arrays import OA_relabel + assert r <= s + q = n + assert (q-r-1)*(q-s) >= (q-s-1)*(q-r) + master_design = orthogonal_array(k+3,q) + + # group k+1 has cardinality r + # group k+2 has cardinality s + # group k+3 has cardinality t + + # Taking q-s blocks going through 0 in the last block + blocks_crossing_0 = [B[-3:] for B in master_design if B[-1] == 0][:q-s] + + # defining the undeleted points of the groups k+1,k+2 + group_k_1 = [x[0] for x in blocks_crossing_0] + group_k_1 = [x for x in range(q) if x not in group_k_1][:r] + + group_k_2 = [x[1] for x in blocks_crossing_0] + group_k_2 = [x for x in range(q) if x not in group_k_2][:s] + + # All blocks that have a deleted point in groups k+1 and k+2 MUST contain a + # point in group k+3 + group_k_3 = [B[-1] for B in master_design if B[-3] not in group_k_1 and B[-2] not in group_k_2] + group_k_3 = list(set(group_k_3)) + assert len(group_k_3) <= t + group_k_3.extend([x for x in range(q) if x not in group_k_3]) + group_k_3 = group_k_3[:t] + + # Relabelling the OA + r1 = [None]*q + r2 = [None]*q + r3 = [None]*q + for i,x in enumerate(group_k_1): + r1[x] = i + for i,x in enumerate(group_k_2): + r2[x] = i + for i,x in enumerate(group_k_3): + r3[x] = i + + OA = OA_relabel(master_design, k+3,q, matrix=[range(q)]*k+[r1,r2,r3]) + OA = wilson_construction(OA,k,q,m,3,[r,s,t], check=False) + return OA + +@cached_function +def find_construction_3_6(k,n): + r""" + Finds a decomposition for construction 3.6 + + INPUT: + + - ``k,n`` (integers) + + .. SEEALSO:: + + :func:`construction_3_6` + + OUTPUT: + + A pair ``f,args`` such that ``f(*args)`` returns the requested OA. + + EXAMPLES:: + + sage: from sage.combinat.designs.orthogonal_arrays_recursive import find_construction_3_6 + sage: find_construction_3_6(8,95)[1] + (8, 13, 7, 4) + sage: find_construction_3_6(8,98) + """ + from sage.rings.arith import is_prime_power + + for mm in range(2,n//2+1): + if (not orthogonal_array(k,mm+0,existence=True) or + not orthogonal_array(k,mm+1,existence=True) or + not orthogonal_array(k,mm+2,existence=True)): + continue + + for nn in range(2,n//mm+1): + i = n-nn*mm + if i<=0: + continue + + if (is_prime_power(nn) and + orthogonal_array(k+i,nn,existence=True)): + return construction_3_6, (k,nn,mm,i) + +def construction_3_6(k,n,m,i): + r""" + Returns a `OA(k,nm+i)` + + This is Wilson's construction with `r` columns of order `1`, in which every + block intersects at most two truncated columns. Such a design exists when + `n` is a prime power and is returned by :func:`OA_and_oval`. + + INPUT: + + - ``k,n,m,i`` (integers) -- `n` must be a prime power. The following designs + must be available: `OA(k+r,q),OA(k,m),OA(k,m+1),OA(k,m+2)`. + + This is construction 3.6 from [AC07]_. + + .. SEEALSO:: + + - :func:`construction_3_6` + - :func:`OA_and_oval` + + EXAMPLES:: + + sage: from sage.combinat.designs.orthogonal_arrays_recursive import find_construction_3_6 + sage: from sage.combinat.designs.orthogonal_arrays_recursive import construction_3_6 + sage: from sage.combinat.designs.orthogonal_arrays import is_orthogonal_array + sage: k=8;n=95 + sage: is_orthogonal_array(construction_3_6(*find_construction_3_6(k,n)[1]),k,n,2) + True + """ + OA = OA_and_oval(n) + OA = [B[:k+i] for B in OA] + OA = [B[:k] + [x if x==0 else None for x in B[k:]] for B in OA] + OA = wilson_construction(OA,k,n,m,i,[1]*i) + assert is_orthogonal_array(OA,k,n*m+i) + return OA + +def OA_and_oval(q): + r""" + Returns a `OA(q+1,q)` whose blocks contains `\leq 2` zeroes in the last `q` + columns. + + This `OA` is build from a projective plane of order `q`, in which there + exists an oval `O` of size `q+1` (i.e. a set of `q+1` points no three of which + are [colinear/contained in a common set of the projective plane]). + + Removing an element `x\in O` and all sets that contain it, we obtain a + `TD(q+1,q)` in which `O` intersects all columns except one. As `O` is an + oval, no block of the `TD` intersects it more than twice. + + INPUT: + + - ``q`` -- a prime power + + .. NOTE:: + + This function is called by :func:`construction_3_6`, an + implementation of Construction 3.6 from [AC07]_. + + EXAMPLES:: + + sage: from sage.combinat.designs.orthogonal_arrays_recursive import OA_and_oval + sage: _ = OA_and_oval + + REFERENCES: + + .. [AC07] Concerning eight mutually orthogonal latin squares + Julian R. Abel, Nicholas Cavenagh + Journal of Combinatorial Designs + Vol. 15, n.3, pp. 255-261 + 2007 + """ + from sage.rings.arith import is_prime_power + from sage.combinat.designs.block_design import projective_plane + from orthogonal_arrays import OA_relabel + + assert is_prime_power(q) + B = projective_plane(q) + + # We compute the oval with a linear program + from sage.numerical.mip import MixedIntegerLinearProgram + p = MixedIntegerLinearProgram() + b = p.new_variable(binary=True) + V = B.points() + p.add_constraint(p.sum([b[i] for i in V]) == q+1) + for bl in B: + p.add_constraint(p.sum([b[i] for i in bl]) <= 2) + p.solve() + b = p.get_values(b) + oval = [x for x,i in b.items() if i] + assert len(oval) == q+1 + + # We remove one element from the oval + x = oval.pop() + oval.sort() + + # We build the TD by relabelling the point set, and removing those which + # contain x. + r = {} + B = list(B) + # (this is to make sure that the first set containing x in B is the one + # which contains no other oval point) + + B.sort(key=lambda b:int(any([xx in oval for xx in b]))) + BB = [] + for b in B: + if x in b: + for xx in b: + if xx == x: + continue + r[xx] = len(r) + else: + BB.append(b) + + assert len(r) == (q+1)*q # all points except x have an image + assert len(set(r.values())) == len(r) # the images are different + + # Relabelling/sorting the blocks and the oval + BB = [[r[xx] for xx in b] for b in BB] + oval = [r[xx] for xx in oval] + + for b in BB: + b.sort() + oval.sort() + + # Turning the TD into an OA + BB = [[xx%q for xx in b] for b in BB] + oval = [xx%q for xx in oval] + assert len(oval) == q + + # We relabel the "oval" as relabelled as [0,...,0] + OA = OA_relabel(BB+([[0]+oval]),q+1,q,blocks=[[0]+oval]) + OA = [[(x+1)%q for x in B] for B in OA] + OA.remove([0]*(q+1)) + + assert all(sum([xx == 0 for xx in b[1:]]) <= 2 for b in OA) + return OA From 41c50d5a256d9e746d8acfb33a4ff7c58e05789b Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Wed, 25 Jun 2014 17:32:12 +0200 Subject: [PATCH 368/546] trac #16500: Simplified find_recursive_construction --- .../combinat/designs/orthogonal_arrays.py | 5 ++-- .../designs/orthogonal_arrays_recursive.py | 30 ++++++++----------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 290d7debe0f..2f0ac5c113b 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -1024,11 +1024,12 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): OA = OA_relabel(OA,k+2,r,matrix=[range(r)]*k+[range(u1)+[None]*(r-u1),range(u2)+[None]*(r-u2)]) OA = wilson_construction(OA,k,r,m,2,[u1,u2],check=False) - elif may_be_available and find_recursive_construction(k,n,existence=True): + elif may_be_available and find_recursive_construction(k,n): _OA_cache_set(k,n,True) if existence: return True - OA = find_recursive_construction(k,n) + f,args = find_recursive_construction(k,n) + OA = f(*args) elif (transversal_design not in who_asked and transversal_design(k,n,existence=True,who_asked=who_asked+(orthogonal_array,)) is not Unknown): diff --git a/src/sage/combinat/designs/orthogonal_arrays_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_recursive.py index 3a768b2f6f6..24f4240d4e4 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_recursive.py @@ -19,11 +19,12 @@ from orthogonal_arrays import orthogonal_array, wilson_construction from sage.combinat.designs.orthogonal_arrays import is_orthogonal_array -def find_recursive_construction(k,n,existence=False): +@cached_function +def find_recursive_construction(k,n): r""" Find a recursive construction of a `OA(k,n)` - This method attempts to build an `OA(k,n)` through the following + This determines whether an `OA(k,n)` can be built through the following constructions: - :func:`construction_3_3` @@ -35,8 +36,10 @@ def find_recursive_construction(k,n,existence=False): - ``k,n`` (integers) - - ``existence`` (boolean) -- whether to return the actual OA, or only - indicate whether it can be built. + OUTPUT: + + Returns a pair ``f,args`` such that ``f(*args)`` returns the requested `OA` + if possible, and ``Unknown`` otherwise. EXAMPLES:: @@ -45,9 +48,10 @@ def find_recursive_construction(k,n,existence=False): sage: count = 0 sage: for n in range(10,150): ....: k = designs.orthogonal_array(None,n,existence=True) - ....: if find_recursive_construction(k,n,existence=True): + ....: if find_recursive_construction(k,n): ....: count = count + 1 - ....: OA = find_recursive_construction(k,n,existence=False) + ....: f,args = find_recursive_construction(k,n) + ....: OA = f(*args) ....: assert is_orthogonal_array(OA,k,n,2,verbose=True) sage: print count 40 @@ -56,17 +60,12 @@ def find_recursive_construction(k,n,existence=False): find_construction_3_4, find_construction_3_5, find_construction_3_6]: - - if find_c(k,n): - if existence: - return True - else: - f,args = find_c(k,n) - return f(*args) + res = find_c(k,n) + if res: + return res return Unknown -@cached_function def find_construction_3_3(k,n): r""" Finds a decomposition for construction 3.3 @@ -148,7 +147,6 @@ def construction_3_3(k,n,m,i): assert is_orthogonal_array(OA,k,n*m+i) return OA -@cached_function def find_construction_3_4(k,n): r""" Finds a decomposition for construction 3.4 @@ -253,7 +251,6 @@ def construction_3_4(k,n,m,r,s): OA = wilson_construction(OA,k,n,m,r+1,[1]*r+[s],check=False) return OA -@cached_function def find_construction_3_5(k,n): r""" Finds a decomposition for construction 3.5 @@ -382,7 +379,6 @@ def construction_3_5(k,n,m,r,s,t): OA = wilson_construction(OA,k,q,m,3,[r,s,t], check=False) return OA -@cached_function def find_construction_3_6(k,n): r""" Finds a decomposition for construction 3.6 From 7c2fb38a2ae24e8c59c5fce8dc0c42967a6ef7c2 Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Wed, 25 Jun 2014 17:47:40 +0200 Subject: [PATCH 369/546] 16497 : fix the integer case for gurobi and cplex --- src/sage/numerical/backends/coin_backend.pyx | 6 +++--- src/sage/numerical/backends/gurobi_backend.pyx | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/sage/numerical/backends/coin_backend.pyx b/src/sage/numerical/backends/coin_backend.pyx index 66d40e2c1d9..2c50b509268 100644 --- a/src/sage/numerical/backends/coin_backend.pyx +++ b/src/sage/numerical/backends/coin_backend.pyx @@ -835,10 +835,10 @@ cdef class CoinBackend(GenericBackend): v = 0.0 else: v = solution[variable] - if self.is_variable_binary(variable): - return round(v) - else: + if self.is_variable_continuous(variable): return v + else: + return round(v) cpdef int ncols(self): r""" diff --git a/src/sage/numerical/backends/gurobi_backend.pyx b/src/sage/numerical/backends/gurobi_backend.pyx index 2fee2dd8dae..857bebee674 100644 --- a/src/sage/numerical/backends/gurobi_backend.pyx +++ b/src/sage/numerical/backends/gurobi_backend.pyx @@ -756,7 +756,10 @@ cdef class GurobiBackend(GenericBackend): cdef double value[1] check(self.env,GRBgetdblattrelement(self.model, "X", variable, value)) - return round(value[0]) if self.is_variable_binary(variable) else value[0] + if self.is_variable_continuous(variable): + return value[0] + else: + return round(value[0]) cpdef int ncols(self): """ From 601121d0b891041e0ffe9dc12edbb87f053e870c Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Thu, 19 Jul 2012 22:49:39 +0000 Subject: [PATCH 370/546] Trac 13277: Added _factor_univariate_polynomial() for ComplexField and RealField --- src/sage/rings/complex_field.py | 50 +++++++++++++++++++ .../rings/polynomial/polynomial_element.pyx | 13 ----- src/sage/rings/real_mpfr.pyx | 46 +++++++++++++++++ 3 files changed, 96 insertions(+), 13 deletions(-) diff --git a/src/sage/rings/complex_field.py b/src/sage/rings/complex_field.py index 5e549c8e6c8..61b229f5cca 100644 --- a/src/sage/rings/complex_field.py +++ b/src/sage/rings/complex_field.py @@ -677,3 +677,53 @@ def algebraic_closure(self): """ return self + def _factor_univariate_polynomial(self, f): + """ + Factor the univariate polynomial ``f``. + + INPUT: + + - ``f`` -- a univariate polynomial defined over the complex numbers + + OUTPUT: + + - A factorization of ``f`` over the complex numbers into a unit and + monic irreducible factors + + .. NOTE:: + + This is a helper method for + :meth:`sage.rings.polynomial.polynomial_element.Polynomial.factor`. + + This method calls PARI to compute the factorization. + + TESTS:: + + sage: k = ComplexField(100) + sage: R. = k[] + sage: k._factor_univariate_polynomial( x ) + x + sage: k._factor_univariate_polynomial( 2*x ) + (2.0000000000000000000000000000) * x + sage: k._factor_univariate_polynomial( x^2 ) + x^2 + sage: k._factor_univariate_polynomial( x^2 + 3 ) + (x - 1.7320508075688772935274463415*I) * (x + 1.7320508075688772935274463415*I) + sage: k._factor_univariate_polynomial( x^2 + 1 ) + (x - I) * (x + I) + sage: k._factor_univariate_polynomial( k(I) * (x^2 + 1) ) + (1.0000000000000000000000000000*I) * (x - I) * (x + I) + + """ + R = f.parent() + + # if the polynomial does not have complex coefficients, PARI will + # factor it over the reals. To make sure it has complex coefficients we + # multiply with I. + I = R.base_ring().gen() + g = f*I if f.leading_coefficient()!=I else f + + F = list(g._pari_with_name().factor()) + + from sage.structure.factorization import Factorization + return Factorization([(R(g).monic(),e) for g,e in zip(*F)], f.leading_coefficient()) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index cda897efafa..db252e6c152 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -3441,19 +3441,6 @@ cdef class Polynomial(CommutativeAlgebraElement): # adds back the unit of the factorization. return self._factor_pari_helper(G) - elif is_RealField(R): - n = pari.set_real_precision(int(3.5*R.prec()) + 1) - G = list(self._pari_with_name().factor()) - - elif sage.rings.complex_field.is_ComplexField(R): - # This is a hack to make the polynomial have complex coefficients, since - # otherwise PARI will factor over RR. - n = pari.set_real_precision(int(3.5*R.prec()) + 1) - if self.leading_coefficient() != R.gen(): - G = list((pari(R.gen())*self._pari_with_name()).factor()) - else: - G = self._pari_with_name().factor() - if G is None: raise NotImplementedError diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 52386921bbc..92618c0e2ce 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -1213,6 +1213,52 @@ cdef class RealField_class(sage.rings.ring.Field): return self(-1) raise ValueError, "No %sth root of unity in self"%n + def _factor_univariate_polynomial(self, f): + """ + Factor the univariate polynomial ``f``. + + INPUT: + + - ``f`` -- a univariate polynomial defined over the real numbers + + OUTPUT: + + - A factorization of ``f`` over the real numbers into a unit and monic + irreducible factors + + .. NOTE:: + + This is a helper method for + :meth:`sage.rings.polynomial.polynomial_element.Polynomial.factor`. + + This method calls PARI to compute the factorization. + + TESTS:: + + sage: k = RealField(100) + sage: R. = k[] + sage: k._factor_univariate_polynomial( x ) + x + sage: k._factor_univariate_polynomial( 2*x ) + (2.0000000000000000000000000000) * x + sage: k._factor_univariate_polynomial( x^2 ) + x^2 + sage: k._factor_univariate_polynomial( x^2 + 1 ) + x^2 + 1.0000000000000000000000000000 + sage: k._factor_univariate_polynomial( x^2 - 1 ) + (x - 1.0000000000000000000000000000) * (x + 1.0000000000000000000000000000) + sage: k._factor_univariate_polynomial( (x - 1)^3 ) + (x - 1.0000000000000000000000000000)^3 + sage: k._factor_univariate_polynomial( x^2 - 3 ) + (x - 1.7320508075688772935274463415) * (x + 1.7320508075688772935274463415) + + """ + R = f.parent() + F = list(f._pari_with_name().factor()) + + from sage.structure.factorization import Factorization + return Factorization([(R(g).monic(),e) for g,e in zip(*F)], f.leading_coefficient()) + #***************************************************************************** # # RealNumber -- element of Real Field From f071a8db1316a7bf8503f6c6c9227fcaa6b953f7 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 25 Jun 2014 20:45:05 +0200 Subject: [PATCH 371/546] sum of squares: micro improvement + random tests --- src/sage/rings/arith.py | 42 ++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/arith.py b/src/sage/rings/arith.py index f3f8f13c0b2..004690b649f 100644 --- a/src/sage/rings/arith.py +++ b/src/sage/rings/arith.py @@ -4941,6 +4941,15 @@ def two_squares(n): ... ValueError: -1 is not a sum of 2 squares + TESTS:: + + sage: for _ in xrange(100): + ....: a = ZZ.random_element(2**16, 2**20) + ....: b = ZZ.random_element(2**16, 2**20) + ....: n = a**2 + b**2 + ....: aa,bb = two_squares(n) + ....: assert aa**2 + bb**2 == n + ALGORITHM: See http://www.schorn.ch/howto.html @@ -4971,8 +4980,8 @@ def two_squares(n): # We run over all factors of n, write each factor p^e as # a sum of 2 squares and accumulate the product # (using multiplication in Z[I]) in a^2 + b^2. - a = Integer(1) - b = Integer(0) + a = ZZ.one() + b = ZZ.zero() for (p,e) in F: if e >= 2: m = p ** (e//2) @@ -5046,6 +5055,16 @@ def three_squares(n): ... ValueError: -1 is not a sum of 3 squares + TESTS:: + + sage: for _ in xrange(100): + ....: a = ZZ.random_element(2**16, 2**20) + ....: b = ZZ.random_element(2**16, 2**20) + ....: c = ZZ.random_element(2**16, 2**20) + ....: n = a**2 + b**2 + c**2 + ....: aa,bb,cc = three_squares(n) + ....: assert aa**2 + bb**2 + cc**2 == n + ALGORITHM: See http://www.schorn.ch/howto.html @@ -5065,7 +5084,7 @@ def three_squares(n): # First, remove all factors 4 from n e = n.valuation(2)//2 - m = Integer(1) << e + m = ZZ.one() << e N = n >> (2*e) # Let x be the largest integer at most sqrt(N) @@ -5128,7 +5147,6 @@ def three_squares(n): x -= 1 assert x >= 0 - x = Integer(x) if x >= b: return (a*m, b*m, x*m) elif x >= a: @@ -5162,10 +5180,17 @@ def four_squares(n): sage: for i in range(2^129, 2^129+10000): # long time ....: S = four_squares(i) ....: assert sum(x^2 for x in S) == i + + TESTS:: + + sage: for _ in xrange(100): + ....: n = ZZ.random_element(2**32,2**34) + ....: aa,bb,cc,dd = four_squares(n) + ....: assert aa**2 + bb**2 + cc**2 + dd**2 == n """ from sage.rings.all import Integer n = Integer(n) - + if n <= 0: if n == 0: z = ZZ.zero() @@ -5177,8 +5202,8 @@ def four_squares(n): return sum_of_squares.four_squares_pyx(n) # First, remove all factors 4 from n - e = n.valuation(2)//2 - m = Integer(1) << e + e = n.valuation(2) // 2 + m = ZZ.one() << e N = n >> (2*e) # Subtract a suitable x^2 such that N - x^2 is 1,2,3,5,6 mod 8, @@ -5192,7 +5217,6 @@ def four_squares(n): a,b,c = three_squares(y) # Correct sorting is guaranteed by construction - x = Integer(x) return (a*m, b*m, c*m, x*m) def sum_of_k_squares(k,n): @@ -5253,7 +5277,7 @@ def sum_of_k_squares(k,n): from sage.rings.all import Integer n = Integer(n) k = int(k) - + if k <= 4: if k == 4: return four_squares(n) From 5678ab77b990b844d1003a1272184929e7bf38f2 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Fri, 19 Oct 2012 03:03:09 +0000 Subject: [PATCH 372/546] Trac #13620: initialization of padic polynomial from empty dict --- .../padics/polynomial_padic_capped_relative_dense.py | 4 ++-- src/sage/rings/polynomial/padics/polynomial_padic_flat.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py b/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py index 8a64d895ea1..4c708131686 100644 --- a/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py +++ b/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py @@ -105,7 +105,7 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct = False, check = False elif isinstance(x, dict): zero = parentbr.zero_element() - n = max(x.keys()) if len(x) else 0 + n = max(x.keys()) if x else 0 v = [zero for _ in xrange(n + 1)] for i, z in x.iteritems(): v[i] = z @@ -1057,7 +1057,7 @@ def newton_polygon(self): ... PrecisionError: The coefficient of t^4 has not enough precision - TESTS: + TESTS:: sage: (5*f).newton_polygon() Finite Newton polygon with 4 vertices: (0, 2), (1, 1), (4, 1), (10, 3) diff --git a/src/sage/rings/polynomial/padics/polynomial_padic_flat.py b/src/sage/rings/polynomial/padics/polynomial_padic_flat.py index 78509fc4acd..a016118f06e 100644 --- a/src/sage/rings/polynomial/padics/polynomial_padic_flat.py +++ b/src/sage/rings/polynomial/padics/polynomial_padic_flat.py @@ -49,7 +49,7 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct=False, ab if check: m = infinity zero = R(0) - n = max(x.keys()) if len(x) else 0 + n = max(x.keys()) if x else 0 v = [zero for _ in xrange(n+1)] for i, z in x.iteritems(): v[i] = R(z) From 5181719ee4f44a95401d32f3baee0fad8f1433ee Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Tue, 23 Oct 2012 17:17:47 +0000 Subject: [PATCH 373/546] Trac #13647: fix initialization of padics from polynomials --- src/sage/rings/padics/padic_ZZ_pX_element.pyx | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sage/rings/padics/padic_ZZ_pX_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_element.pyx index 9cfd91538cd..d49f1e518c3 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_element.pyx @@ -523,6 +523,30 @@ cdef class pAdicZZpXElement(pAdicExtElement): """ return self.prime_pow + cdef int _pshift_self(self, long shift) except -1: + """ + Multiplies this element by ``p^shift``. + + TESTS: + + Check that :trac:`13647` has been fixed:: + + sage: K = ZpCA(3) + sage: R. = K[] + sage: L. = K.extension(u^2 + 1) + sage: L(R.gen()) + u + O(3^20) + + sage: K = ZpFM(3) + sage: R. = K[] + sage: L. = K.extension(u^2 + 1) + sage: L(R.gen()) + u + O(3^20) + + """ + if shift != 0: + raise NotImplementedError + def _test_preprocess_list(R, L): """ Given a list of elements convertible to ``ntl_ZZ_p``s, finds the From 8575938e91d7e4f67ec10764df38fe0f24cede4b Mon Sep 17 00:00:00 2001 From: Martin Albrecht Date: Wed, 25 Jun 2014 13:39:21 -0700 Subject: [PATCH 374/546] fixing #10128: vector_space_dimension() should return infinity when it is --- src/sage/rings/polynomial/multi_polynomial_ideal.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index ae57cf7d302..9781bd80209 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -1498,6 +1498,15 @@ def vector_space_dimension(self): 0 sage: I.vector_space_dimension() 4 + + When the ideal is not zero-dimensional, we return infinity:: + + sage: R. = PolynomialRing(QQ) + sage: I = R.ideal(x) + sage: I.dimension() + 1 + sage: I.vector_space_dimension() + +Infinity """ R = self.ring() gb = R.ideal(self.groebner_basis()) @@ -1507,7 +1516,8 @@ def vector_space_dimension(self): vd = Integer(vdim(gb, attributes={gb:{'isSB':1}})) if vd == -1: - raise TypeError("ideal is not zero dimensional") + from sage.rings.infinity import Infinity + return Infinity else: return vd From 95414f5844a038139157b53ec54e3b1935b5db88 Mon Sep 17 00:00:00 2001 From: Sara Kropf Date: Thu, 26 Jun 2014 08:18:38 +0200 Subject: [PATCH 375/546] NotImplementedError in _composition_explorative_ if there are final word outs --- src/sage/combinat/finite_state_machine.py | 57 +++++++++++++++++------ 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 6e4251f2787..20c41835f37 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -751,21 +751,6 @@ class FSMState(SageObject): the state is reached as the last state of some input; only for final states - .. WARNING:: - - ``final_word_out`` is not implemented everywhere. Currently it is - implemented in :meth:`FiniteStateMachine.process`, - in the LaTeX output, - :meth:`FiniteStateMachine.composition`, - :meth:`FiniteStateMachine.output_projection`, - :meth:`FiniteStateMachine.prepone_output`, - :meth:`Transducer.cartesian_product`, but not in - :meth:`FiniteStateMachine.determine_alphabets`, - :meth:`FiniteStateMachine.equivalence_classes`, - :meth:`FiniteStateMachine.transposition`, - :meth:`Transducer.intersection` and - :meth:`Transducer.simplification`. - - ``hook`` -- (default: ``None``) A function which is called when the state is reached during processing input. @@ -3796,6 +3781,10 @@ def determine_alphabets(self, reset=True): After this operation the input alphabet and the output alphabet of self are a list of letters. + TODO: + + At the moment, the letters of the alphabets need to be hashable. + EXAMPLES:: sage: T = Transducer([(1, 1, 1, 0), (1, 2, 2, 1), @@ -5542,6 +5531,17 @@ def _composition_explorative_(self, other): sage: B.determinisation() Automaton with 1 states + Transducers with non-empty final output words are not implemented:: + + sage: A = transducers.GrayCode() + sage: B = transducers.abs([0, 1]) + sage: A.composition(B, algorithm='explorative') + Traceback (most recent call last): + ... + NotImplementedError: Explorative composition is not + implemented for transducers with non-empty final output words. Try + the direct algorithm instead. + TODO: The explorative algorithm should be re-implemented using the @@ -5571,6 +5571,13 @@ def composition_transition(state, input): output += transition2.word_out return ((new_state1, new_state2), output) + if any(s.final_word_out for s in self.iter_final_states()) or \ + any(s.final_word_out for s in other.iter_final_states()): + raise NotImplementedError("Explorative composition is not " + "implemented for transducers with " + "non-empty final output words. Try " + "the direct algorithm instead.") + F = other.empty_copy() new_initial_states = [(other.initial_states()[0], self.initial_states()[0])] F.add_from_transition_function(composition_transition, @@ -7007,9 +7014,29 @@ def asymptotic_moments(self, variable=SR.symbol('n')): ....: input_alphabet=[-1, 0, 1], ....: initial_states=[0], ....: final_states=[0]) + + At the moment, we can not use composition with ``NAF``, + beacuse it has non-empty final output words:: + + sage: NAFweight = weight_transducer.composition( + ....: NAF, + ....: algorithm='explorative') + Traceback (most recent call last): + ... + NotImplementedError: Explorative composition is not + implemented for transducers with non-empty final output + words. Try the direct algorithm instead. + + + Thus, we change ``NAF``, then compose and again construct + the final output words:: + + sage: for s in NAF.final_states(): + ....: s.final_word_out = [] sage: NAFweight = weight_transducer.composition( ....: NAF, ....: algorithm='explorative').relabeled() + sage: NAFweight.construct_final_word_out(0) sage: sorted(NAFweight.transitions()) [Transition from 0 to 0: 0|0, Transition from 0 to 1: 1|-, From c3f2b335d22977e7ba2460cd9721553ca5274fbc Mon Sep 17 00:00:00 2001 From: Sara Kropf Date: Thu, 26 Jun 2014 08:20:40 +0200 Subject: [PATCH 376/546] corrected spacing --- src/sage/combinat/finite_state_machine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 20c41835f37..cb3d35284c2 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -7028,7 +7028,7 @@ def asymptotic_moments(self, variable=SR.symbol('n')): words. Try the direct algorithm instead. - Thus, we change ``NAF``, then compose and again construct + Thus, we change ``NAF``, then compose and again construct the final output words:: sage: for s in NAF.final_states(): From efc44c615d69bbfeeeb71028fb8981e4f3623880 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Thu, 26 Jun 2014 09:57:26 +0200 Subject: [PATCH 377/546] rebased import statements in doctests --- src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py b/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py index 2d08ca6f40e..4c815289d95 100644 --- a/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py +++ b/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py @@ -1848,7 +1848,7 @@ def __init__(self, Q, R=None, invert_y=True): sage: R. = QQ['x'] sage: E = HyperellipticCurve(x^5-3*x+1) - sage: from sage.schemes.elliptic_curves.monsky_washnitzer import SpecialHyperellipticQuotientRing + sage: from sage.schemes.hyperelliptic_curves.monsky_washnitzer import SpecialHyperellipticQuotientRing sage: SpecialHyperellipticQuotientRing(E) is SpecialHyperellipticQuotientRing(E) True @@ -2606,7 +2606,7 @@ def __init__(self, base_ring): sage: R. = QQ['x'] sage: E = HyperellipticCurve(x^5-3*x+1) - sage: from sage.schemes.elliptic_curves.monsky_washnitzer import SpecialHyperellipticQuotientRing, MonskyWashnitzerDifferentialRing + sage: from sage.schemes.hyperelliptic_curves.monsky_washnitzer import SpecialHyperellipticQuotientRing, MonskyWashnitzerDifferentialRing sage: S = SpecialHyperellipticQuotientRing(E) sage: MonskyWashnitzerDifferentialRing(S) is MonskyWashnitzerDifferentialRing(S) True From 14d6a6f1b4c8c145aaad0e57223d115c2e04b413 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 26 Jun 2014 10:14:44 +0200 Subject: [PATCH 378/546] trac #16551: Tell Sage 2011 is long gone --- src/doc/common/conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/doc/common/conf.py b/src/doc/common/conf.py index ff9efe3e680..28a9402bda4 100644 --- a/src/doc/common/conf.py +++ b/src/doc/common/conf.py @@ -1,5 +1,6 @@ import sys, os, sphinx from sage.env import SAGE_DOC +from datetime import date def get_doc_abspath(path): """ @@ -37,7 +38,7 @@ def get_doc_abspath(path): # General information about the project. project = u"" -copyright = u'2005--2011, The Sage Development Team' +copyright = u"2005--{}, The Sage Development Team".format(date.today().year) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the From 4add98a4e671996e859bb1c008fd61909e982c20 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Thu, 26 Jun 2014 10:45:19 +0200 Subject: [PATCH 379/546] 16007: fix documentation examples --- src/doc/de/tutorial/tour_algebra.rst | 2 +- src/doc/en/constructions/calculus.rst | 4 +- src/doc/en/tutorial/tour_algebra.rst | 2 +- src/doc/fr/tutorial/tour_algebra.rst | 4 +- src/doc/ru/tutorial/tour_algebra.rst | 4 +- src/sage/tests/french_book/recequadiff.py | 56 +++++++++++------------ 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/doc/de/tutorial/tour_algebra.rst b/src/doc/de/tutorial/tour_algebra.rst index c7bea0327dc..47b34686658 100644 --- a/src/doc/de/tutorial/tour_algebra.rst +++ b/src/doc/de/tutorial/tour_algebra.rst @@ -156,7 +156,7 @@ berechnen. Die Gleichung :math:`x'+x-1=0` berechnen Sie wie folgt: sage: x = function('x',t) # definiere x als Funktion dieser Variablen sage: DE = diff(x, t) + x - 1 sage: desolve(DE, [x,t]) - (c + e^t)*e^(-t) + (_C + e^t)*e^(-t) Dies benutzt Sages Schnittstelle zu Maxima [Max]_, daher kann sich die Ausgabe ein wenig von anderen Ausgaben in Sage unterscheiden. In diff --git a/src/doc/en/constructions/calculus.rst b/src/doc/en/constructions/calculus.rst index 5af99b9056c..2116913bec6 100644 --- a/src/doc/en/constructions/calculus.rst +++ b/src/doc/en/constructions/calculus.rst @@ -283,9 +283,9 @@ An example, how to solve ODE's symbolically in Sage using the Maxima interface sage: y=function('y',x); desolve(diff(y,x,2) + 3*x == y, dvar = y, ics = [1,1,1]) 3*x - 2*e^(x - 1) sage: desolve(diff(y,x,2) + 3*x == y, dvar = y) - k2*e^(-x) + k1*e^x + 3*x + _K2*e^(-x) + _K1*e^x + 3*x sage: desolve(diff(y,x) + 3*x == y, dvar = y) - (3*(x + 1)*e^(-x) + c)*e^x + (3*(x + 1)*e^(-x) + _C)*e^x sage: desolve(diff(y,x) + 3*x == y, dvar = y, ics = [1,1]).expand() 3*x - 5*e^(x - 1) + 3 diff --git a/src/doc/en/tutorial/tour_algebra.rst b/src/doc/en/tutorial/tour_algebra.rst index 48ede49a6fa..94cde69a09e 100644 --- a/src/doc/en/tutorial/tour_algebra.rst +++ b/src/doc/en/tutorial/tour_algebra.rst @@ -154,7 +154,7 @@ solve the equation :math:`x'+x-1=0`: sage: x = function('x',t) # define x to be a function of that variable sage: DE = diff(x, t) + x - 1 sage: desolve(DE, [x,t]) - (c + e^t)*e^(-t) + (_C + e^t)*e^(-t) This uses Sage's interface to Maxima [Max]_, and so its output may be a bit different from other Sage output. In this case, this says diff --git a/src/doc/fr/tutorial/tour_algebra.rst b/src/doc/fr/tutorial/tour_algebra.rst index c639f8571de..222a4c0e470 100644 --- a/src/doc/fr/tutorial/tour_algebra.rst +++ b/src/doc/fr/tutorial/tour_algebra.rst @@ -133,12 +133,12 @@ ordinaires. Pour résoudre l'équation :math:`x'+x-1=0` : x(t) sage: DE = lambda y: diff(y,t) + y - 1 sage: desolve(DE(x(t)), [x(t),t]) - (c + e^t)*e^(-t) + (_C + e^t)*e^(-t) Ceci utilise l'interface de Sage vers Maxima [Max]_, aussi il se peut que la sortie diffère un peu des sorties habituelles de Sage. Dans notre cas, le résultat indique que la solution générale à l'équation -différentielle est :math:`x(t) = e^{-t}(e^{t}+c)`. +différentielle est :math:`x(t) = e^{-t}(e^{t}+C)`. Il est aussi possible de calculer des transformées de Laplace. La transformée de Laplace de :math:`t^2e^t -\sin(t)` s'obtient comme suit : diff --git a/src/doc/ru/tutorial/tour_algebra.rst b/src/doc/ru/tutorial/tour_algebra.rst index 5725792128a..c3761a4ba33 100644 --- a/src/doc/ru/tutorial/tour_algebra.rst +++ b/src/doc/ru/tutorial/tour_algebra.rst @@ -150,12 +150,12 @@ Sage может использоваться для решения диффер sage: x = function('x',t) # определение функции x зависящей от t sage: DE = diff(x, t) + x - 1 sage: desolve(DE, [x,t]) - (c + e^t)*e^(-t) + (_C + e^t)*e^(-t) Для этого используется интерфейс Maxima [Max]_, поэтому результат может быть выведен в виде, отличном от обычного вывода Sage. В данном случае общее решение для данного дифференциального уравнения - -:math:`x(t) = e^{-t}(e^{t}+c)`. +:math:`x(t) = e^{-t}(e^{t}+C)`. Преобразования Лапласа также могут быть вычислены. Преобразование Лапласа для :math:`t^2e^t -\sin(t)` вычисляется следующим образом: diff --git a/src/sage/tests/french_book/recequadiff.py b/src/sage/tests/french_book/recequadiff.py index 692f1fc012e..c78d266cc03 100755 --- a/src/sage/tests/french_book/recequadiff.py +++ b/src/sage/tests/french_book/recequadiff.py @@ -20,33 +20,33 @@ Sage example in ./recequadiff.tex, line 182:: sage: desolve(diff(y,x) + 3*y == exp(x), y, show_method=True) - [1/4*(4*c + e^(4*x))*e^(-3*x), 'linear'] + [1/4*(4*_C + e^(4*x))*e^(-3*x), 'linear'] Sage example in ./recequadiff.tex, line 194:: sage: desolve(y*diff(y,x) == x, y, show_method=True) - [1/2*y(x)^2 == 1/2*x^2 + c, 'separable'] + [1/2*y(x)^2 == 1/2*x^2 + _C, 'separable'] Sage example in ./recequadiff.tex, line 204:: sage: desolve(diff(y,x) == exp(x+y), y, show_method=True) - [-(e^(x + y(x)) + 1)*e^(-y(x)) == c, 'exact'] + [-(e^(x + y(x)) + 1)*e^(-y(x)) == _C, 'exact'] Sage example in ./recequadiff.tex, line 215:: sage: desolve(diff(y,x)-y == x*y^4, y, show_method=True) - [e^x/(-1/3*(3*x - 1)*e^(3*x) + c)^(1/3), 'bernoulli'] + [e^x/(-1/3*(3*x - 1)*e^(3*x) + _C)^(1/3), 'bernoulli'] Sage example in ./recequadiff.tex, line 227:: sage: desolve(x^2*diff(y,x) == y^2+x*y+x^2, y, show_method=True) - [c*x == e^(arctan(y(x)/x)), 'homogeneous'] + [_C*x == e^(arctan(y(x)/x)), 'homogeneous'] Sage example in ./recequadiff.tex, line 244:: sage: desolve(diff(y,x) == (cos(y)-2*x)/(y+x*sin(y)), y, ... show_method=True) - [x^2 - x*cos(y(x)) + 1/2*y(x)^2 == c, 'exact'] + [x^2 - x*cos(y(x)) + 1/2*y(x)^2 == _C, 'exact'] Sage example in ./recequadiff.tex, line 263:: @@ -58,7 +58,7 @@ sage: desolve(y == x*diff(y,x)-diff(y,x)^2, y, ... contrib_ode=True, show_method=True) - [[y(x) == -c^2 + c*x, y(x) == 1/4*x^2], 'clairault'] + [[y(x) == -_C^2 + _C*x, y(x) == 1/4*x^2], 'clairault'] Sage example in ./recequadiff.tex, line 293:: @@ -68,13 +68,13 @@ sage: DE = diff(y,x)+2*y == x**2-2*x+3 sage: desolve(DE, y) - 1/4*((2*x^2 - 2*x + 1)*e^(2*x) - 2*(2*x - 1)*e^(2*x) + 4*c + 1/4*((2*x^2 - 2*x + 1)*e^(2*x) - 2*(2*x - 1)*e^(2*x) + 4*_C + 6*e^(2*x))*e^(-2*x) Sage example in ./recequadiff.tex, line 305:: sage: desolve(DE, y).expand() - 1/2*x^2 + c*e^(-2*x) - 3/2*x + 9/4 + 1/2*x^2 + _C*e^(-2*x) - 3/2*x + 9/4 Sage example in ./recequadiff.tex, line 321:: @@ -90,29 +90,29 @@ sage: x = var('x'); y = function('y', x) sage: desolve(diff(y,x)*log(y) == y*sin(x), y, show_method=True) - [1/2*log(y(x))^2 == c - cos(x), 'separable'] + [1/2*log(y(x))^2 == _C - cos(x), 'separable'] Sage example in ./recequadiff.tex, line 348:: sage: ed(x) = desolve(diff(y,x)*log(y) == y*sin(x), y); ed(x) - 1/2*log(y(x))^2 == c - cos(x) + 1/2*log(y(x))^2 == _C - cos(x) Sage example in ./recequadiff.tex, line 356:: sage: solve(ed, y) - [y(x) == e^(-sqrt(2*c - 2*cos(x))), y(x) == e^(sqrt(2*c - 2*cos(x)))] + [y(x) == e^(-sqrt(2*_C - 2*cos(x))), y(x) == e^(sqrt(2*_C - 2*cos(x)))] Sage example in ./recequadiff.tex, line 367:: - sage: solve(ed, y)[0].subs_expr(c==5).rhs() + sage: solve(ed, y)[0].subs_expr(_C==5).rhs() Traceback (most recent call last): ... - NameError: name 'c' is not defined + NameError: name '_C' is not defined Sage example in ./recequadiff.tex, line 377:: sage: ed.variables() - (c, x) + (_C, x) Sage example in ./recequadiff.tex, line 384:: @@ -154,24 +154,24 @@ sage: assume(x>0) sage: desolve(DE, u) - x == c*e^(arcsinh(x^2*u(x)/sqrt(x^4))) + x == _C*e^(arcsinh(x^2*u(x)/sqrt(x^4))) Sage example in ./recequadiff.tex, line 505:: sage: S = desolve(DE,u)._maxima_().ev(logarc=True).sage().solve(u); S - [u(x) == -(sqrt(u(x)^2 + 1)*c - x)/c] + [u(x) == -(sqrt(u(x)^2 + 1)*_C - x)/_C] Sage example in ./recequadiff.tex, line 519:: sage: solu = (x-S[0]*c)^2; solu - (c*u(x) - x)^2 == (u(x)^2 + 1)*c^2 + (_C*u(x) - x)^2 == (u(x)^2 + 1)*_C^2 sage: sol = solu.solve(u); sol - [u(x) == -1/2*(c^2 - x^2)/(c*x)] + [u(x) == -1/2*(_C^2 - x^2)/(_C*x)] Sage example in ./recequadiff.tex, line 526:: sage: y(x) = x*sol[0].rhs(); y(x) - -1/2*(c^2 - x^2)/c + -1/2*(_C^2 - x^2)/_C Sage example in ./recequadiff.tex, line 535:: @@ -185,28 +185,28 @@ sage: x = var('x'); y = function('y', x); a, b = var('a, b') sage: DE = diff(y,x) - a*y == -b*y**2 sage: sol(x) = desolve(DE,[y,x]); sol(x) - -(log(b*y(x) - a) - log(y(x)))/a == c + x + -(log(b*y(x) - a) - log(y(x)))/a == _C + x Sage example in ./recequadiff.tex, line 575:: sage: Sol(x) = solve(sol, y)[0]; Sol(x) - log(y(x)) == a*(c + x) + log(b*y(x) - a) + log(y(x)) == (_C + x)*a + log(b*y(x) - a) Sage example in ./recequadiff.tex, line 582:: sage: Sol(x) = Sol(x).lhs()-Sol(x).rhs(); Sol(x) - -a*(c + x) - log(b*y(x) - a) + log(y(x)) + -(_C + x)*a - log(b*y(x) - a) + log(y(x)) sage: Sol = Sol.simplify_log(); Sol(x) - -a*(c + x) + log(y(x)/(b*y(x) - a)) + -(_C + x)*a + log(y(x)/(b*y(x) - a)) sage: solve(Sol, y)[0].simplify() - y(x) == a*e^(a*c + a*x)/(b*e^(a*c + a*x) - 1) + y(x) == a*e^(_C*a + a*x)/(b*e^(_C*a + a*x) - 1) Sage example in ./recequadiff.tex, line 602:: sage: x = var('x'); y = function('y', x) sage: DE = diff(y,x,2)+3*y == x^2-7*x+31 sage: desolve(DE, y).expand() - 1/3*x^2 + k2*cos(sqrt(3)*x) + k1*sin(sqrt(3)*x) - 7/3*x + 91/9 + 1/3*x^2 + _K2*cos(sqrt(3)*x) + _K1*sin(sqrt(3)*x) - 7/3*x + 91/9 Sage example in ./recequadiff.tex, line 611:: @@ -240,7 +240,7 @@ Sage example in ./recequadiff.tex, line 709:: sage: g(t) = desolve(eq2(x,t),[g,t]); g(t) - c*e^(k*t) + _C*e^(k*t) Sage example in ./recequadiff.tex, line 717:: @@ -253,7 +253,7 @@ Sage example in ./recequadiff.tex, line 728:: sage: assume(k>0); desolve(eq1,[f,x]) - k1*e^(sqrt(k)*x) + k2*e^(-sqrt(k)*x) + _K1*e^(sqrt(k)*x) + _K2*e^(-sqrt(k)*x) Sage example in ./recequadiff.tex, line 782:: From e1992ce4c0a145ce8fd61fae0abe0809cd6ff173 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 26 Jun 2014 10:48:50 +0200 Subject: [PATCH 380/546] trac #16500: doc + speedup --- .../designs/orthogonal_arrays_recursive.py | 84 +++++++++---------- 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_recursive.py index 24f4240d4e4..7d1829551e2 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_recursive.py @@ -11,13 +11,20 @@ Sage's function :func:`~sage.combinat.designs.orthogonal_arrays.orthogonal_array`. +REFERENCES: + +.. [AC07] Concerning eight mutually orthogonal latin squares + Julian R. Abel, Nicholas Cavenagh + Journal of Combinatorial Designs + Vol. 15, n.3, pp. 255-261 + 2007 + Functions --------- """ from sage.misc.cachefunc import cached_function -from sage.misc.unknown import Unknown -from orthogonal_arrays import orthogonal_array, wilson_construction -from sage.combinat.designs.orthogonal_arrays import is_orthogonal_array +from orthogonal_arrays import orthogonal_array +from designs_pyx import is_orthogonal_array @cached_function def find_recursive_construction(k,n): @@ -39,7 +46,7 @@ def find_recursive_construction(k,n): OUTPUT: Returns a pair ``f,args`` such that ``f(*args)`` returns the requested `OA` - if possible, and ``Unknown`` otherwise. + if possible, and ``False`` otherwise. EXAMPLES:: @@ -56,6 +63,7 @@ def find_recursive_construction(k,n): sage: print count 40 """ + assert k >= 3 for find_c in [find_construction_3_3, find_construction_3_4, find_construction_3_5, @@ -64,11 +72,11 @@ def find_recursive_construction(k,n): if res: return res - return Unknown + return False def find_construction_3_3(k,n): r""" - Finds a decomposition for construction 3.3 + Finds a decomposition for construction 3.3 from [AC07]_ INPUT: @@ -89,9 +97,9 @@ def find_construction_3_3(k,n): (11, 11, 16, 1) sage: find_construction_3_3(12,11) """ - for mm in range(2,n//2+1): - if (not orthogonal_array( k , mm , existence=True) or - not orthogonal_array( k ,mm+1, existence=True)): + for mm in range(k-1,n//2+1): + if (not orthogonal_array(k ,mm , existence=True) or + not orthogonal_array(k ,mm+1, existence=True)): continue for nn in range(2,n//mm+1): @@ -99,19 +107,19 @@ def find_construction_3_3(k,n): if i<=0: continue - if (orthogonal_array(k+i, nn , existence=True) and - orthogonal_array( k ,mm+i, existence=True)): + if (orthogonal_array(k+i, nn , existence=True) and + orthogonal_array(k , mm+i, existence=True)): return construction_3_3, (k,nn,mm,i) def construction_3_3(k,n,m,i): r""" Returns an `OA(k,nm+i)`. - This is Wilson's construction with `i` truncated columns of size `1`, such - that a block `B_0` of the incomplete OA intersects all truncated - columns. There is a slight difference however, as the block `B_0` is only - considered up to its first `k` coordinates, and a `OA(k,i)` is used instead - of `iOA(k,1)` (thus there is no need for an `OA(k,m+i)`. + This is Wilson's construction with `i` truncated columns of size 1 and such + that a block `B_0` of the incomplete OA intersects all truncated columns. As + a consequence, all other blocks intersect only `0` or `1` of the last `i` + columns. This allow to consider the block `B_0` only up to its first `k` + coordinates and then use a `OA(k,i)` instead of a `OA(k,m+i) - i.OA(k,1)`. This is construction 3.3 from [AC07]_. @@ -133,7 +141,7 @@ def construction_3_3(k,n,m,i): sage: is_orthogonal_array(construction_3_3(*find_construction_3_3(k,n)[1]),k,n,2) True """ - from orthogonal_arrays import OA_relabel, incomplete_orthogonal_array + from orthogonal_arrays import wilson_construction, OA_relabel, incomplete_orthogonal_array # Builds an OA(k+i,n) containing a block [0]*(k+i) OA = incomplete_orthogonal_array(k+i,n,(1,)) OA = [[(x+1)%n for x in B] for B in OA] @@ -149,7 +157,7 @@ def construction_3_3(k,n,m,i): def find_construction_3_4(k,n): r""" - Finds a decomposition for construction 3.4 + Finds a decomposition for construction 3.4 from [AC07]_ INPUT: @@ -170,7 +178,7 @@ def find_construction_3_4(k,n): (8, 25, 7, 12, 9) sage: find_construction_3_4(9,24) """ - for mm in range(2,n//2+1): + for mm in range(k-1,n//2+1): if (not orthogonal_array(k,mm+0,existence=True) or not orthogonal_array(k,mm+1,existence=True) or not orthogonal_array(k,mm+2,existence=True)): @@ -192,11 +200,11 @@ def construction_3_4(k,n,m,r,s): r""" Returns a `OA(k,nm+rs)`. - This is Wilson's construction applied to an incomplete `OA(k+r+1,n)` with - `k` columns of size 1 and a column of size `s`. + This is Wilson's construction applied to a truncated `OA(k+r+1,n)` with `k` + columns of size `1` and one column of size `s`. - The the unique elements of the `k` columns are picked so that a block `B_0` - contains them all. + The unique elements of the `k` truncated columns are picked so that a block + `B_0` contains them all. - If there exists an `OA(k,m+r+1)` the column of size `s` is truncated in order to intersect `B_0`. @@ -226,7 +234,7 @@ def construction_3_4(k,n,m,r,s): sage: is_orthogonal_array(construction_3_4(*find_construction_3_4(k,n)[1]),k,n,2) True """ - from orthogonal_arrays import OA_relabel + from orthogonal_arrays import wilson_construction, OA_relabel assert s= n or @@ -291,11 +299,7 @@ def find_construction_3_5(k,n): if not orthogonal_array(k+3,nn,existence=True): continue - for r,s,t in Compositions(i+3,length=3,max_part=nn): - # avoid warning on Compositions(...,min_part=0) - r = r-1 - s = s-1 - t = t-1 + for r,s,t in IntegerListsLex(i,length=3,ceiling=[nn-1,nn-1,nn-1]): if (r <= s and (nn-r-1)*(nn-s) < t and (r==0 or orthogonal_array(k,r,existence=True)) and @@ -336,7 +340,7 @@ def construction_3_5(k,n,m,r,s,t): sage: is_orthogonal_array(construction_3_5(*find_construction_3_5(k,n)[1]),k,n,2) True """ - from orthogonal_arrays import OA_relabel + from orthogonal_arrays import wilson_construction, OA_relabel assert r <= s q = n assert (q-r-1)*(q-s) >= (q-s-1)*(q-r) @@ -381,7 +385,7 @@ def construction_3_5(k,n,m,r,s,t): def find_construction_3_6(k,n): r""" - Finds a decomposition for construction 3.6 + Finds a decomposition for construction 3.6 from [AC07]_ INPUT: @@ -404,7 +408,7 @@ def find_construction_3_6(k,n): """ from sage.rings.arith import is_prime_power - for mm in range(2,n//2+1): + for mm in range(k-1,n//2+1): if (not orthogonal_array(k,mm+0,existence=True) or not orthogonal_array(k,mm+1,existence=True) or not orthogonal_array(k,mm+2,existence=True)): @@ -423,7 +427,7 @@ def construction_3_6(k,n,m,i): r""" Returns a `OA(k,nm+i)` - This is Wilson's construction with `r` columns of order `1`, in which every + This is Wilson's construction with `r` columns of order `1`, in which each block intersects at most two truncated columns. Such a design exists when `n` is a prime power and is returned by :func:`OA_and_oval`. @@ -448,6 +452,7 @@ def construction_3_6(k,n,m,i): sage: is_orthogonal_array(construction_3_6(*find_construction_3_6(k,n)[1]),k,n,2) True """ + from orthogonal_arrays import wilson_construction OA = OA_and_oval(n) OA = [B[:k+i] for B in OA] OA = [B[:k] + [x if x==0 else None for x in B[k:]] for B in OA] @@ -482,20 +487,13 @@ def OA_and_oval(q): sage: from sage.combinat.designs.orthogonal_arrays_recursive import OA_and_oval sage: _ = OA_and_oval - REFERENCES: - - .. [AC07] Concerning eight mutually orthogonal latin squares - Julian R. Abel, Nicholas Cavenagh - Journal of Combinatorial Designs - Vol. 15, n.3, pp. 255-261 - 2007 """ from sage.rings.arith import is_prime_power from sage.combinat.designs.block_design import projective_plane from orthogonal_arrays import OA_relabel assert is_prime_power(q) - B = projective_plane(q) + B = projective_plane(q, check=False) # We compute the oval with a linear program from sage.numerical.mip import MixedIntegerLinearProgram From 697dd0ca8284485897b051015af74b385c345fb4 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 26 Jun 2014 12:07:32 +0200 Subject: [PATCH 381/546] trac #16500: Typoes in the doc --- src/sage/combinat/designs/orthogonal_arrays_recursive.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_recursive.py index 7d1829551e2..cc1a5a67b67 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_recursive.py @@ -200,16 +200,17 @@ def construction_3_4(k,n,m,r,s): r""" Returns a `OA(k,nm+rs)`. - This is Wilson's construction applied to a truncated `OA(k+r+1,n)` with `k` + This is Wilson's construction applied to a truncated `OA(k+r+1,n)` with `r` columns of size `1` and one column of size `s`. - The unique elements of the `k` truncated columns are picked so that a block + The unique elements of the `r` truncated columns are picked so that a block `B_0` contains them all. - If there exists an `OA(k,m+r+1)` the column of size `s` is truncated in order to intersect `B_0`. - - If there exists an `OA(k,m+r+1)`, the last column must not intersect `B_0` + - Otherwise, if there exists an `OA(k,m+r)`, the last column must not + intersect `B_0` This is construction 3.4 from [AC07]_. From 71dad5d46b3fe7b343962974a75a9c199e0904a6 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 20 Jun 2014 12:39:13 +0200 Subject: [PATCH 382/546] trac #16503: q-x construction of Orthogonal Arrays --- src/sage/combinat/designs/latin_squares.py | 4 +- .../designs/orthogonal_arrays_recursive.py | 182 +++++++++++++++++- 2 files changed, 182 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 7e124b2e3cd..3f9cf59bb65 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -31,7 +31,7 @@ 80| 9 80 8 82 6 6 6 6 7 88 6 7 6 6 6 6 7 96 6 8 100| 8 100 6 102 7 7 6 106 6 108 6 6 7 112 6 7 6 8 6 6 120| 6 120 6 6 6 124 6 126 127 7 6 130 6 7 6 6 7 136 6 138 - 140| 6 7 6 10 10 7 6 7 6 148 6 150 7 8 8 7 6 156 6 6 + 140| 6 7 6 10 10 7 6 7 6 148 6 150 7 8 8 7 6 156 7 6 160| 7 7 6 162 6 7 6 166 7 168 6 8 6 172 6 6 10 9 6 178 180| 6 180 6 6 7 8 6 10 6 7 6 190 7 192 6 7 6 196 6 198 200| 7 7 6 7 6 7 6 8 12 11 10 210 6 7 6 7 7 8 6 10 @@ -53,7 +53,7 @@ 80| 100| - 120| - - - 140| - + 140| 160| - - 180| - - 200| - - - diff --git a/src/sage/combinat/designs/orthogonal_arrays_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_recursive.py index cc1a5a67b67..52cbd05343a 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_recursive.py @@ -38,6 +38,7 @@ def find_recursive_construction(k,n): - :func:`construction_3_4` - :func:`construction_3_5` - :func:`construction_3_6` + - :func:`construction_q_x` INPUT: @@ -67,11 +68,11 @@ def find_recursive_construction(k,n): for find_c in [find_construction_3_3, find_construction_3_4, find_construction_3_5, - find_construction_3_6]: + find_construction_3_6, + find_q_x]: res = find_c(k,n) if res: return res - return False def find_construction_3_3(k,n): @@ -554,3 +555,180 @@ def OA_and_oval(q): assert all(sum([xx == 0 for xx in b[1:]]) <= 2 for b in OA) return OA + +def construction_q_x(k,q,x,check=True): + r""" + Returns an `OA(k,(q-1)*(q-x)+x+2)` using the `q-x` construction. + + Let `v=(q-1)*(q-x)+x+2`. If there exists a projective plane of order `q` + (e.g. when `q` is a prime power) and `0 Date: Thu, 26 Jun 2014 17:01:59 +0200 Subject: [PATCH 383/546] 16497 : add doctest --- src/sage/numerical/mip.pyx | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index e2d8150de99..713d85004ba 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -248,6 +248,28 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p.set_binary(b) sage: p.solve(objective_only=True) 4.0 + + TESTS: + + Check that :trac:`16497` is fixed:: + + sage: from sage.numerical.mip import MixedIntegerLinearProgram + sage: for type in ["binary", "integer"]: + ....: k = 3 + ....: items = [1/5, 1/3, 2/3, 3/4, 5/7] + ....: maximum=1 + ....: p=MixedIntegerLinearProgram() + ....: box=p.new_variable(**{type:True}) + ....: for b in range(k): + ....: p.add_constraint(p.sum([items[i]*box[i,b] for i in range(len(items))]) <= maximum) + ....: for i in range(len(items)): + ....: p.add_constraint(p.sum([box[i,b] for b in range(k)]) == 1) + ....: p.set_objective(None) + ....: _ = p.solve() + ....: box=p.get_values(box) + ....: print(all(v in ZZ for v in box.values())) + True + True """ def __init__(self, solver=None, maximization=True, From d1f3e216a8e8e1913c803ec27baadc789f7b2300 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Thu, 26 Jun 2014 18:12:26 +0200 Subject: [PATCH 384/546] trac #16191: Reviewer Patch: Move one example; spacing; typo; punctuation The example that explorative composition does not allow final output words is moved to composition instead of having it in the private function _composition_explorative_ --- src/sage/combinat/finite_state_machine.py | 41 ++++++++++++----------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index cb3d35284c2..0362e5e0dca 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -749,7 +749,7 @@ class FSMState(SageObject): - ``final_word_out`` -- (default: ``None``) a word that is written when the state is reached as the last state of some input; only for final - states + states. - ``hook`` -- (default: ``None``) A function which is called when the state is reached during processing input. @@ -3781,9 +3781,9 @@ def determine_alphabets(self, reset=True): After this operation the input alphabet and the output alphabet of self are a list of letters. - TODO: + .. TODO:: - At the moment, the letters of the alphabets need to be hashable. + At the moment, the letters of the alphabets need to be hashable. EXAMPLES:: @@ -5407,6 +5407,18 @@ def composition(self, other, algorithm=None, sage: H.transitions()[0].word_out is H.transitions()[1].word_out True + In the explorative algorithm, transducers with non-empty final + output words are currently not implemented:: + + sage: A = transducers.GrayCode() + sage: B = transducers.abs([0, 1]) + sage: A.composition(B, algorithm='explorative') + Traceback (most recent call last): + ... + NotImplementedError: Explorative composition is not + implemented for transducers with non-empty final output + words. Try the direct algorithm instead. + TESTS: Due to the limitations of the two algorithms the following @@ -5531,21 +5543,10 @@ def _composition_explorative_(self, other): sage: B.determinisation() Automaton with 1 states - Transducers with non-empty final output words are not implemented:: - - sage: A = transducers.GrayCode() - sage: B = transducers.abs([0, 1]) - sage: A.composition(B, algorithm='explorative') - Traceback (most recent call last): - ... - NotImplementedError: Explorative composition is not - implemented for transducers with non-empty final output words. Try - the direct algorithm instead. - - TODO: + .. TODO:: - The explorative algorithm should be re-implemented using the - process iterators of both finite state machines. + The explorative algorithm should be re-implemented using the + process iterators of both finite state machines. """ def composition_transition(state, input): (state1, state2) = state @@ -7016,7 +7017,7 @@ def asymptotic_moments(self, variable=SR.symbol('n')): ....: final_states=[0]) At the moment, we can not use composition with ``NAF``, - beacuse it has non-empty final output words:: + because it has non-empty final output words:: sage: NAFweight = weight_transducer.composition( ....: NAF, @@ -7028,8 +7029,8 @@ def asymptotic_moments(self, variable=SR.symbol('n')): words. Try the direct algorithm instead. - Thus, we change ``NAF``, then compose and again construct - the final output words:: + Thus, we change ``NAF``, then compose and again construct + the final output words:: sage: for s in NAF.final_states(): ....: s.final_word_out = [] From d218ba0fdaf2fac6a79324cdaad296fefb3cba1f Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Thu, 26 Jun 2014 18:37:33 +0200 Subject: [PATCH 385/546] FiniteStateMachine.composition: Only deterministic machines for explorative algorithm The explorative algorithm for composition of finite state machines only handles deterministic machines. We now raise a NotImplementedError instead of simply taking a more or less random transition. This is a similar stop-gap measure as #16539. Once #16538 is implemented, non-deterministic machines shall also be implemented in the explorative composition, cf. #16548. --- src/sage/combinat/finite_state_machine.py | 24 ++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 1b2c4a1b52a..1cab7fbc754 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -5370,6 +5370,7 @@ def composition(self, other, algorithm=None, The input alphabet of self has to be specified. This is a very limited implementation of composition. + WARNING: The output of ``other`` is fed into ``self``. If algorithm is ``None``, then the algorithm is chosen @@ -5470,11 +5471,27 @@ def composition(self, other, algorithm=None, implemented for transducers with non-empty final output words. Try the direct algorithm instead. + Similarly, the explorative algorithm cannot handle + non-deterministic finite state machines:: + + sage: A = Transducer([(0, 0, 0, 0), (0, 1, 0, 0)]) + sage: B = transducers.Identity([0]) + sage: A.composition(B, algorithm='explorative') + Traceback (most recent call last): + ... + NotImplementedError: Explorative composition is currently + not implemented for non-deterministic transducers. + sage: B.composition(A, algorithm='explorative') + Traceback (most recent call last): + ... + NotImplementedError: Explorative composition is currently + not implemented for non-deterministic transducers. + TESTS: Due to the limitations of the two algorithms the following (examples from above, but different algorithm used) does not - give a full answer or does not work + give a full answer or does not work. In the following, ``algorithm='explorative'`` is inadequate, as ``F`` has more than one initial state:: @@ -5630,6 +5647,11 @@ def composition_transition(state, input): "non-empty final output words. Try " "the direct algorithm instead.") + if not self.is_deterministic() or not other.is_deterministic(): + raise NotImplementedError("Explorative composition is " + "currently not implemented for " + "non-deterministic transducers.") + F = other.empty_copy() new_initial_states = [(other.initial_states()[0], self.initial_states()[0])] F.add_from_transition_function(composition_transition, From 190501c0fc9ea9c73ad5ee3cc7bd2db90051188b Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 26 Jun 2014 13:39:24 -0400 Subject: [PATCH 386/546] Changed version number. --- build/pkgs/lrcalc/package-version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/lrcalc/package-version.txt b/build/pkgs/lrcalc/package-version.txt index 856e05a167d..2bf1ca5f549 100644 --- a/build/pkgs/lrcalc/package-version.txt +++ b/build/pkgs/lrcalc/package-version.txt @@ -1 +1 @@ -1.1.6.p0 +1.1.7 From 2ac09bc134bde5ae44d326bd9ad2a4266fb7f2c6 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Thu, 26 Jun 2014 20:06:51 +0200 Subject: [PATCH 387/546] FiniteStateMachine - LaTeX output in notebook In #16020, graphs.setup_latex_preamble has been modified to include tikz into the mathjax avoid list so that graphs can be rendered more easily in the sage notebook. This ticket does the same for finite_state_machine.setup_latex_preamble. --- src/sage/combinat/finite_state_machine.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 30b80ea6661..80465636cc0 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -153,14 +153,11 @@ \end{tikzpicture} We can turn this into a graphical representation. Before doing this, -we have to :func:`setup the latex preamble ` and -make sure that TikZ pictures are not rendered by mathjax, but by -actually running LaTeX. +we have to :func:`setup the latex preamble `. :: sage: sage.combinat.finite_state_machine.setup_latex_preamble() - sage: latex.mathjax_avoid_list('tikzpicture') sage: view(NAF) # not tested To actually see this, use the live documentation in the Sage notebook @@ -3140,7 +3137,6 @@ def latex_options(self, sage: from sage.combinat.finite_state_machine import setup_latex_preamble sage: setup_latex_preamble() - sage: latex.mathjax_avoid_list('tikzpicture') sage: T = Transducer(initial_states=['I'], ....: final_states=[0, 3]) sage: for j in srange(4): @@ -8909,9 +8905,6 @@ def setup_latex_preamble(): Nothing. - In the Sage notebook, you probably want to use - ``latex.mathjax_avoid_list('tikzpicture')`` such that - :func:`~sage.misc.latex.view` actually shows the result. See the section on :ref:`finite_state_machine_LaTeX_output` in the introductory examples of this module. @@ -8919,9 +8912,9 @@ def setup_latex_preamble(): sage: from sage.combinat.finite_state_machine import setup_latex_preamble sage: setup_latex_preamble() - sage: latex.mathjax_avoid_list('tikzpicture') """ latex.add_package_to_preamble_if_available('tikz') + latex.add_to_mathjax_avoid_list("tikz") latex.add_to_preamble('\\usetikzlibrary{automata}') From 116a6ee4ddfa68f9a42035615dcac1ad1b424bb3 Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Thu, 26 Jun 2014 11:50:30 -0700 Subject: [PATCH 388/546] Add note about github integration to developer guide. Pull requests, including subsequent comments, are pushed to trac. --- src/doc/en/developer/walk_through.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/doc/en/developer/walk_through.rst b/src/doc/en/developer/walk_through.rst index c21d7e7ef34..e52f9bdab86 100644 --- a/src/doc/en/developer/walk_through.rst +++ b/src/doc/en/developer/walk_through.rst @@ -15,6 +15,10 @@ We also have a handy `one-page "cheat sheet" `_ of commonly used git commands that you can print out and leave on your desk. +You can alternatively fork and create a pull request at +`github `_ which will automatically fetch +your code and open a ticket on our trac server. + .. _section-walkthrough-setup-git: From 1e5c972f0811859c408e44fdaaf3029a6e251a8d Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Thu, 26 Jun 2014 20:51:20 +0200 Subject: [PATCH 389/546] Improved docstrings of SageObject._cache_key() and related methods --- src/sage/misc/cachefunc.pyx | 21 ++++++++++++------- .../rings/padics/padic_ZZ_pX_CR_element.pyx | 8 ++++--- .../rings/polynomial/polynomial_element.pyx | 7 ++----- src/sage/structure/sage_object.pyx | 15 +++++++------ 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index 7e9e5879f97..2dff72821fc 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -828,7 +828,8 @@ cdef class CachedFunction(object): True Check that :trac:`16316` has been fixed, i.e., caching works for - objects which are not hashable:: + immutable unhashable objects which define + :meth:`sage.structure.sage_object.SageObject._cache_key`. sage: @cached_function ....: def f(x): return x @@ -907,7 +908,8 @@ cdef class CachedFunction(object): TESTS: Check that :trac:`16316` has been fixed, i.e., caching works for - objects which are not hashable:: + immutable unhashable objects which define + :meth:`sage.structure.sage_object.SageObject._cache_key`. sage: @cached_function ....: def f(x): return x @@ -951,7 +953,8 @@ cdef class CachedFunction(object): TESTS: Check that :trac:`16316` has been fixed, i.e., caching works for - objects which are not hashable:: + immutable unhashable objects which define + :meth:`sage.structure.sage_object.SageObject._cache_key`. sage: @cached_function ....: def f(x): return x @@ -1174,7 +1177,8 @@ cdef class WeakCachedFunction(CachedFunction): doing a computation Check that :trac:`16316` has been fixed, i.e., caching works for - objects which are not hashable:: + immutable unhashable objects which define + :meth:`sage.structure.sage_object.SageObject._cache_key`. sage: from sage.misc.cachefunc import weak_cached_function sage: @weak_cached_function @@ -1252,7 +1256,8 @@ cdef class WeakCachedFunction(CachedFunction): TESTS: Check that :trac:`16316` has been fixed, i.e., caching works for - objects which are not hashable:: + immutable unhashable objects which define + :meth:`sage.structure.sage_object.SageObject._cache_key`. sage: from sage.misc.cachefunc import weak_cached_function sage: @weak_cached_function @@ -1298,7 +1303,8 @@ cdef class WeakCachedFunction(CachedFunction): TESTS: Check that :trac:`16316` has been fixed, i.e., caching works for - objects which are not hashable:: + immutable unhashable objects which define + :meth:`sage.structure.sage_object.SageObject._cache_key`. sage: from sage.misc.cachefunc import weak_cached_function sage: @weak_cached_function @@ -1726,7 +1732,8 @@ cdef class CachedMethodCaller(CachedFunction): 2 Check that :trac:`16316` has been fixed, i.e., caching works for - objects which are not hashable:: + immutable unhashable objects which define + :meth:`sage.structure.sage_object.SageObject._cache_key`. sage: K. = Qq(4) sage: class A(object): diff --git a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx index e2f8ea6534e..2df50cd19bb 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx @@ -476,7 +476,8 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): r""" Return a hashable key which identifies this element. - This enables caching for `p`-adic numbers which are not hashable. + This makes it possible to use this element in caches such as methods + decorated with ``@cached_method``. .. SEEALSO:: @@ -497,8 +498,9 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): ... TypeError: unhashable type: 'sage.rings.padics.padic_ZZ_pX_CR_element.pAdicZZpXCRElement' - However, they should be cacheable, therefore they define a - ``_cache_key`` which is hashable and uniquely identifies them:: + However, we want to cache computations which depend on them. Therefore + they define a ``_cache_key`` which is hashable and uniquely identifies + them:: sage: a._cache_key() (..., ((0, 1),), 0, 20) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 59ed73e8bd3..c66c66fe0d3 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -832,16 +832,13 @@ cdef class Polynomial(CommutativeAlgebraElement): def _cache_key(self): """ - Return a key which identifies this element. + Return a hashable key which identifies this element. .. SEEALSO:: :meth:`sage.structure.sage_object.SageObject._cache_key` - EXAMPLES: - - This enables caching for polynomials with unhashable coefficients such - as `p`-adics:: + EXAMPLES:: sage: K. = Qq(4) sage: R. = K[] diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index 606f5645365..8abdb63ade0 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -223,9 +223,9 @@ cdef class SageObject: def _cache_key(self): r""" - Return a key which identifies this objects for caching. The output must - be hashable itself or a tuple of objects which are hashable or define a - ``_cache_key``. + Return a hashable key which identifies this objects for caching. The + output must be hashable itself or a tuple of objects which are hashable + or define a ``_cache_key``. This method will only be called if the object itself is not hashable. @@ -249,8 +249,8 @@ cdef class SageObject: False If such objects defined a non-trivial hash function, this would break - caching in many places. However, such objects should still be - cacheable. This can be achieved by defining an appropriate + caching in many places. However, such objects should still be usable in + caches. This can be achieved by defining an appropriate ``_cache_key``:: sage: hash(b) @@ -269,7 +269,10 @@ cdef class SageObject: sage: c._cache_key() (..., ((0, 1), (1,)), 0, 20) - An implementation must make sure that for elements ``a`` and ``b``, if ``a != b``, then also ``a._cache_key() != b._cache_key()``. In pratice this means that the ``_cache_key`` should always include the ``id`` of the parent. + An implementation must make sure that for elements ``a`` and ``b``, if + ``a != b``, then also ``a._cache_key() != b._cache_key()``. In pratice + this means that the ``_cache_key`` should always include the ``id`` of + the parent. sage: S. = Qq(4) sage: d = a + O(2) From 002ceee1c2cd49be71135d710057a9dea1de81c8 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 24 Jun 2014 12:19:49 +0200 Subject: [PATCH 390/546] trac #16524: OA(9,135) --- src/sage/combinat/designs/database.py | 78 +++++++++++++++++++ src/sage/combinat/designs/latin_squares.py | 4 +- .../designs/orthogonal_arrays_recursive.py | 2 +- 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 3b2c748ecab..4bd4661f3fe 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -54,6 +54,7 @@ :func:`OA(11,80) `, :func:`OA(10,82) `, :func:`OA(10,100) `, + :func:`OA(9,135) `, :func:`OA(12,144) `, :func:`OA(10,154) `, :func:`OA(12,210) `, @@ -2310,6 +2311,82 @@ def OA_10_100(): M = OA_from_Vmt(8,11,[0,1,6,56,22,35,47,23,60]) return M +def OA_9_135(): + r""" + Returns an OA(9,135) + + Construction shared by Julian R. Abel: + + This design can be built by Wilson's method (`135 = 8.16 + 7`) applied + to an Orthogonal Array `OA(9+7,16)` with 7 groups truncated to size 1 in + such a way that a block contain 0,1, or 3 points of the truncated + groups. + + This is possible, because `PG(2,2)` is a subdesign in `PG(2,16)`; in a + cyclic `PG(2,16)` or `BIBD(273,17,1)` the points `\equiv 0 \pmod{39}` + form such a subdesign. + + EXAMPLES:: + + sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array + sage: from sage.combinat.designs.database import OA_9_135 + sage: OA = OA_9_135() + sage: print is_orthogonal_array(OA,9,135,2) + True + + The design is available from the general constructor:: + + sage: designs.orthogonal_array(9,135,existence=True) + True + """ + n=273 + B = (1,2,4,8,16,32,64,91,117,128,137,182,195,205,234,239,256) # a (273,17,1)-difference set + PG16 = [[(x+c)%n for x in B] for c in range(n)] + + # PG2 is a (7,3,1)-design (fano plane) contained in PG16. It is a set of 7 + # points that any block of PG16 intersect on 0,1, or 3 points. + # + # We build it, then check that it works + PG2 = set([x*39 for x in range(7)]) + traces = [[x for x in B if x%39 == 0] for B in PG16] + assert set(map(len,traces)) == set([0,1,3]) + + # We now build an OA(17,16) from our PG16, in such a way that all points of + # PG2 are in different columns. For this, we need to find a point p that is + # not located on any of the lines defined by the points of PG2 + + lines = [B for B in PG16 if len([x for x in B if x%39 == 0]) == 3] + union_of_the_lines = set(sum(lines,[])) + p = (set(range(237))-union_of_the_lines).pop() + + # We can now build a TD from our PG16 by removing p. + for B in PG16: + B.sort(key=lambda x:int(x not in PG2)) + PG16.sort(key=lambda B:sum(x for x in B if x in PG2)) + + r = {} + for B in PG16: + if p in B: + for x in B: + if x != p: + r[x] = len(r) + r[p] = n-1 + + # The columns containing points from PG2 will be the last 7 + assert all(r[x] >= (n-1)-16*7 for x in PG2) + # Those points are the first of each column + assert all(r[x]%16 == 0 for x in PG2) + + PG = [sorted([r[x] for x in B]) for B in PG16] + OA = [[x%16 for x in B] for B in PG if n-1 not in B] + + # We truncate the last 7 columns to size 1. We also drop the first column + truncated_OA = [B[1:-7]+[x if x==0 else None for x in B[-7:]] for B in OA] + + # And call Wilson's construction + from orthogonal_arrays import wilson_construction + return wilson_construction(truncated_OA, 9, 16, 8,7,(1,)*7,check=False) + def OA_12_144(): r""" Returns an OA(12,144) @@ -2609,6 +2686,7 @@ def OA_33_993(): 80 : (11 , OA_11_80), 82 : (10 , OA_10_82), 100 : (10 , OA_10_100), + 135 : (9 , OA_9_135), 144 : (12 , OA_12_144), 154 : (10 , OA_10_154), 210 : (12 , OA_12_210), diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 3f9cf59bb65..0d333c004c0 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -30,7 +30,7 @@ 60| 5 60 5 6 63 7 5 66 5 6 6 70 7 72 5 7 6 6 6 78 80| 9 80 8 82 6 6 6 6 7 88 6 7 6 6 6 6 7 96 6 8 100| 8 100 6 102 7 7 6 106 6 108 6 6 7 112 6 7 6 8 6 6 - 120| 6 120 6 6 6 124 6 126 127 7 6 130 6 7 6 6 7 136 6 138 + 120| 6 120 6 6 6 124 6 126 127 7 6 130 6 7 6 7 7 136 6 138 140| 6 7 6 10 10 7 6 7 6 148 6 150 7 8 8 7 6 156 7 6 160| 7 7 6 162 6 7 6 166 7 168 6 8 6 172 6 6 10 9 6 178 180| 6 180 6 6 7 8 6 10 6 7 6 190 7 192 6 7 6 196 6 198 @@ -52,7 +52,7 @@ 60| + 80| 100| - - 120| - - + 120| - 140| 160| - - 180| - - diff --git a/src/sage/combinat/designs/orthogonal_arrays_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_recursive.py index 52cbd05343a..3683e47d298 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_recursive.py @@ -62,7 +62,7 @@ def find_recursive_construction(k,n): ....: OA = f(*args) ....: assert is_orthogonal_array(OA,k,n,2,verbose=True) sage: print count - 40 + 39 """ assert k >= 3 for find_c in [find_construction_3_3, From 3487f093a5c6f8ee2f5454357276125a142c95d7 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 26 Jun 2014 23:11:05 +0200 Subject: [PATCH 391/546] trac #16528: OA(9,120) --- src/sage/combinat/designs/bibd.py | 5 + src/sage/combinat/designs/database.py | 151 ++++++++++++++++++ src/sage/combinat/designs/latin_squares.py | 4 +- .../combinat/designs/orthogonal_arrays.py | 2 +- .../designs/orthogonal_arrays_recursive.py | 2 +- 5 files changed, 160 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index 8da24bdb141..899c7c2c988 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -187,7 +187,12 @@ def BalancedIncompleteBlockDesign(v,k,existence=False,use_LJCR=False): return BlockDesign(v, v_5_1_BIBD(v), test = False) from difference_family import difference_family + from database import BIBD_constructions + if (v,k,1) in BIBD_constructions: + if existence: + return True + return BlockDesign(v,BIBD_constructions[(v,k,1)]()) if BIBD_from_TD(v,k,existence=True): if existence: return True diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 4bd4661f3fe..32cf5d0e316 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -54,6 +54,7 @@ :func:`OA(11,80) `, :func:`OA(10,82) `, :func:`OA(10,100) `, + :func:`OA(9,120) `, :func:`OA(9,135) `, :func:`OA(12,144) `, :func:`OA(10,154) `, @@ -71,6 +72,18 @@ :func:`four MOLS of order 15 `, :func:`three MOLS of order 18 ` +- :func:`DF(21,5,1) ` + :func:`DF(25,4,1) ` + :func:`DF(37,4,1) ` + :func:`DF(81,5,1) ` + :func:`DF(91,6,1) ` + :func:`DF(121,5,1) ` + :func:`DF(141,5,1) ` + :func:`DF(161,5,1) ` + :func:`DF(201,5,1) ` + :func:`DF(221,5,1) ` + +- :func:`RBIBD(120,8,1) ` **Dictionaries** @@ -2311,6 +2324,53 @@ def OA_10_100(): M = OA_from_Vmt(8,11,[0,1,6,56,22,35,47,23,60]) return M +def OA_9_120(): + r""" + Returns an OA(9,120) + + Construction shared by Julian R. Abel: + + From a resolvable `(120,8,1)-BIBD`, one can obtain 7 `MOLS(120)` or a + resolvable `TD(8,120)` by forming a resolvable `TD(8,8) - 8.TD(8,1)` on + `I_8 \times B` for each block `B` in the BIBD. This gives a `TD(8,120) + - 120 TD(8,1)` (which is resolvable as the BIBD is resolvable). + + .. SEEALSO:: + + :func:`RBIBD_120_8_1` + + EXAMPLES:: + + sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array + sage: from sage.combinat.designs.database import OA_9_120 + sage: OA = OA_9_120() + sage: print is_orthogonal_array(OA,9,120,2) + True + + The design is available from the general constructor:: + + sage: designs.orthogonal_array(9,120,existence=True) + True + """ + from incidence_structures import IncidenceStructure + RBIBD_120 = RBIBD_120_8_1() + equiv = [RBIBD_120[i*15:(i+1)*15] for i in range(17)] + + OA8 = orthogonal_array(9,8) + assert all( (len(set(B[:-1])) == 1) == (B[-1] == 0) for B in OA8) + OA = [] + + for i,classs in enumerate(equiv): + for S in classs: + for B in OA8: + if B[-1] != 0: + OA.append([S[x] for x in B[:-1]]+[i*7+B[-1]]) + + for i in range(120): + OA.append([i]*8+[0]) + + return OA + def OA_9_135(): r""" Returns an OA(9,135) @@ -2686,6 +2746,7 @@ def OA_33_993(): 80 : (11 , OA_11_80), 82 : (10 , OA_10_82), 100 : (10 , OA_10_100), + 120 : (9 , OA_9_120), 135 : (9 , OA_9_135), 144 : (12 , OA_12_144), 154 : (10 , OA_10_154), @@ -2946,3 +3007,93 @@ def CDF_221_5_1(): (201,5,1): CDF_201_5_1, (221,5,1): CDF_221_5_1 } + +def RBIBD_120_8_1(): + r""" + Returns a resolvable `BIBD(120,8,1)` + + This function output a list ``L`` of `17\times 15` blocks such that + ``L[i*15:(i+1)*15]`` is a partition of `120`. + + Construction shared by Julian R. Abel: + + Seiden's method: Start with a cyclic `(273,17,1)-BIBD` and let `B` be an + hyperoval, i.e. a set which intersects any block of the BIBD in either 0 + (153 blocks) or 2 points (120 blocks). Dualise this design and take + these last 120 blocks as points in the design; blocks in the design will + correspond to the `273-18=255` non-hyperoval points. + + The design is also resolvable. In the original `PG(2,16)` take any + point `T` in the hyperoval and consider a block `B1` containing `T`. + The `15` points in `B1` that do not belong to the hyperoval correspond + to `15` blocks forming a parallel class in the dualised design. The + other `16` parallel classes come in a similar way, by using point `T` + and the other `16` blocks containing `T`. + + .. SEEALSO:: + + :func:`OA_9_120` + + EXAMPLES:: + + sage: from sage.combinat.designs.database import RBIBD_120_8_1 + sage: from sage.combinat.designs.bibd import _check_pbd + sage: RBIBD = RBIBD_120_8_1() + sage: _ = _check_pbd(RBIBD,120,[8]) + + It is indeed resolvable, and the parallel classes are given by 17 slices of + consecutive 15 blocks:: + + sage: for i in range(17): + ....: assert len(set(sum(RBIBD[i*15:(i+1)*15],[]))) == 120 + + The BIBD is available from the constructor:: + + sage: _ = designs.BalancedIncompleteBlockDesign(120,8) + """ + from incidence_structures import IncidenceStructure + n=273 + + # Base block of a cyclic BIBD(273,16,1) + B = [1,2,4,8,16,32,64,91,117,128,137,182,195,205,234,239,256] + BIBD = [[(x+c)%n for x in B] for c in range(n)] + + # A (precomputed) set that every block of the BIBD intersects on 0 or 2 points + hyperoval = [128, 192, 194, 4, 262, 140, 175, 48, 81, 180, 245, 271, 119, 212, 249, 189, 62, 255] + for B in BIBD: + len_trace = len([x for x in B if x in hyperoval]) + assert len_trace == 0 or len_trace == 2 + + # Equivalence classes + p = hyperoval[0] + equiv = [] + new_BIBD = [] + for B in BIBD: + if any(x in hyperoval for x in B): + if p in B: + equiv.append([x for x in B if x not in hyperoval]) + else: + new_BIBD.append([x for x in B]) + + BIBD = new_BIBD + + r = {v:i for i,v in enumerate([x for x in range(n) if x not in hyperoval])} + BIBD = [[r[x] for x in B] for B in BIBD ] + equiv = [[r[x] for x in B] for B in equiv] + + BIBD = IncidenceStructure(range(255),BIBD) + Matrix = BIBD.incidence_matrix() + + equiv = [[Matrix[x].dict().keys() for x in S] for S in equiv] + return [B for S in equiv for B in S] + +# Index of the BIBD constructions +# +# Associates to triple (v,k,lambda) a function that return a +# (n,k,lambda)-BIBD family. +# +# This dictionary is used by designs.BalancedIncompleteBlockDesign + +BIBD_constructions = { + (120,8,1): RBIBD_120_8_1, +} diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 0d333c004c0..3005ed72ade 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -30,7 +30,7 @@ 60| 5 60 5 6 63 7 5 66 5 6 6 70 7 72 5 7 6 6 6 78 80| 9 80 8 82 6 6 6 6 7 88 6 7 6 6 6 6 7 96 6 8 100| 8 100 6 102 7 7 6 106 6 108 6 6 7 112 6 7 6 8 6 6 - 120| 6 120 6 6 6 124 6 126 127 7 6 130 6 7 6 7 7 136 6 138 + 120| 7 120 6 6 6 124 6 126 127 7 6 130 6 7 6 7 7 136 6 138 140| 6 7 6 10 10 7 6 7 6 148 6 150 7 8 8 7 6 156 7 6 160| 7 7 6 162 6 7 6 166 7 168 6 8 6 172 6 6 10 9 6 178 180| 6 180 6 6 7 8 6 10 6 7 6 190 7 192 6 7 6 196 6 198 @@ -52,7 +52,7 @@ 60| + 80| 100| - - 120| - + 120| 140| 160| - - 180| - - diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 2f0ac5c113b..3bb2866afe9 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -185,7 +185,7 @@ def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): sage: designs.transversal_design(None, 30, existence=True) 6 sage: designs.transversal_design(None, 120, existence=True) - 8 + 9 TESTS: diff --git a/src/sage/combinat/designs/orthogonal_arrays_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_recursive.py index 3683e47d298..8d061aa6c14 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_recursive.py @@ -62,7 +62,7 @@ def find_recursive_construction(k,n): ....: OA = f(*args) ....: assert is_orthogonal_array(OA,k,n,2,verbose=True) sage: print count - 39 + 38 """ assert k >= 3 for find_c in [find_construction_3_3, From 69d3573f7c56e309b357dd43e5b52b4cb60d0885 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 26 Jun 2014 20:24:25 -0400 Subject: [PATCH 392/546] Update SPKG.txt and checksums.ini. --- build/pkgs/lrcalc/SPKG.txt | 39 --------------------------------- build/pkgs/lrcalc/checksums.ini | 8 +++---- 2 files changed, 4 insertions(+), 43 deletions(-) diff --git a/build/pkgs/lrcalc/SPKG.txt b/build/pkgs/lrcalc/SPKG.txt index e2f888541bc..328549280df 100644 --- a/build/pkgs/lrcalc/SPKG.txt +++ b/build/pkgs/lrcalc/SPKG.txt @@ -29,42 +29,3 @@ Anders S. Buch (asbuch@math.rutgers.edu) spkg-src and the patch patches/build.diff. So, whenever you update the sources, you should use/modify the spkg-src script. -== Changelog == - -=== lrcalc-1.1.6.p0 (Jeroen Demeyer, 8 May 2013) === - * Trac #14487: fix various build and packaging issues, add spkg-src - and patches/build.diff (to be applied at packaging time). - -=== lrcalc-1.1.6 (Jean-Pierre Flori, December 2012 - February 2013) === - - * Trac #13839: - * Updated to lrcalc-sage-1.1.6: let lrcalc build a shared library on Cygwin. - * Stop tracking the src directory which is now tracked upstream. - -=== lrcalc-1.1.6beta1 (Nicolas M. Thiéry, June 2012) === - - * Updated to lrcalc-1.1.6beta1: - * Fixed segmentation fault on Open Solaris (name conflict with hash_insert) - * Added quantum and fusion calculations in the README and testsuite - * Don't install the lrcalc binaries, only the libraries and headers - -=== lrcalc-1.1.6beta.p0 (Nicolas M. Thiéry, June 2012) === - - * Fixed make -> $MAKE - -=== lrcalc-1.1.6beta (Nicolas M. Thiéry, January 2012) === - - * Upgrade to lrcalc 1.1.6beta which combines: - * lrcalc-1.1.5.tar.gz - * schmult-1.1.5.tar.gz - * an autotools build system - - * First release as optional spkg (see #10333) - -=== lrcalc-1.1.5b (Nicolas M. Thiéry, May 2011) === - - * An alpha prerelease of 1.1.6beta - -=== lrcalc-1.1.4 (Mike Hansen, May 2010) === - - * Initial version diff --git a/build/pkgs/lrcalc/checksums.ini b/build/pkgs/lrcalc/checksums.ini index 6ee89d5d190..571815181e1 100644 --- a/build/pkgs/lrcalc/checksums.ini +++ b/build/pkgs/lrcalc/checksums.ini @@ -1,4 +1,4 @@ -tarball=lrcalc-VERSION.tar.bz2 -sha1=b36a8bdfab9f8ca0cfc7f922b4f4b0064d364464 -md5=e41f12d2f1f04e868c5d651951f9b0ff -cksum=3021882451 +tarball=lrcalc-VERSION.tar.gz +sha1=dc57dc890ffdb11b9cd255fdc2c189483f61601a +md5=c9828dc59dfd7c34cb8c911299be8764 +cksum=101950293 From 7134ee179bfa1126cde66f8fc4e1e30db5e89a4a Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 26 Jun 2014 20:44:26 -0400 Subject: [PATCH 393/546] Changed checksum. --- build/pkgs/lrcalc/checksums.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/lrcalc/checksums.ini b/build/pkgs/lrcalc/checksums.ini index 571815181e1..75590e1b2b4 100644 --- a/build/pkgs/lrcalc/checksums.ini +++ b/build/pkgs/lrcalc/checksums.ini @@ -1,4 +1,4 @@ tarball=lrcalc-VERSION.tar.gz -sha1=dc57dc890ffdb11b9cd255fdc2c189483f61601a -md5=c9828dc59dfd7c34cb8c911299be8764 -cksum=101950293 +sha1=8149a4c676f4b21ce9dc23145b272a061a04240a +md5=a1d85113d9c915b41ba38fcf1741e637 +cksum=4041239855 From c0ab188bd2b54ff81eb3925d417aae7f3c6626f5 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Fri, 27 Jun 2014 02:45:21 +0200 Subject: [PATCH 394/546] fixed docstring --- src/sage/structure/sage_object.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index 7ec5e591cf9..aea5c3f5c96 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -272,7 +272,7 @@ cdef class SageObject: An implementation must make sure that for elements ``a`` and ``b``, if ``a != b``, then also ``a._cache_key() != b._cache_key()``. In practice this means that the ``_cache_key`` should always include - the ``id`` of the parent. + the ``id`` of the parent:: sage: S. = Qq(4) sage: d = a + O(2) From 22d1d8ebe8c9dbdfeee93c344e2a3c8d16325142 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Fri, 27 Jun 2014 02:53:53 +0200 Subject: [PATCH 395/546] Include parent instead of id(parent) as first entry of a cache key --- src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx | 6 +++--- src/sage/rings/polynomial/polynomial_element.pyx | 2 +- src/sage/structure/sage_object.pyx | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx index 5895621f052..67830f15375 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx @@ -518,11 +518,11 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): """ if self._is_exact_zero(): - return (id(self.parent()), 0) + return (self.parent(), 0) elif self._is_inexact_zero(): - return (id(self.parent()), 0, self.valuation()) + return (self.parent(), 0, self.valuation()) else: - return (id(self.parent()), + return (self.parent(), tuple(tuple(c) if isinstance(c, list) else c for c in self.unit_part().list()), self.valuation(), self.precision_relative()) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index c66c66fe0d3..bf499644eff 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -857,7 +857,7 @@ cdef class Polynomial(CommutativeAlgebraElement): (1 + O(2^20))*x """ - return id(self.parent()), tuple(self) + return self.parent(), tuple(self) # you may have to replicate this boilerplate code in derived classes if you override # __richcmp__. The python documentation at http://docs.python.org/api/type-structs.html diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index aea5c3f5c96..578febd0224 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -272,7 +272,7 @@ cdef class SageObject: An implementation must make sure that for elements ``a`` and ``b``, if ``a != b``, then also ``a._cache_key() != b._cache_key()``. In practice this means that the ``_cache_key`` should always include - the ``id`` of the parent:: + the parent as its first argument:: sage: S. = Qq(4) sage: d = a + O(2) From a35f3d18c57f087e99419ee47d89f4e170c2d23b Mon Sep 17 00:00:00 2001 From: Martin Albrecht Date: Fri, 27 Jun 2014 02:04:15 +0100 Subject: [PATCH 396/546] catch overflows in f.subs() where f is a libsingular polynomial --- src/sage/libs/singular/groebner_strategy.pyx | 2 +- src/sage/libs/singular/polynomial.pxd | 3 +- src/sage/libs/singular/polynomial.pyx | 39 ++++++++++++-- src/sage/libs/singular/singular.pxd | 2 +- src/sage/libs/singular/singular.pyx | 20 ++----- .../multi_polynomial_libsingular.pyx | 54 +++++++++++++++---- 6 files changed, 87 insertions(+), 33 deletions(-) diff --git a/src/sage/libs/singular/groebner_strategy.pyx b/src/sage/libs/singular/groebner_strategy.pyx index 736b79ba7e9..d8f3a72c697 100644 --- a/src/sage/libs/singular/groebner_strategy.pyx +++ b/src/sage/libs/singular/groebner_strategy.pyx @@ -280,7 +280,7 @@ cdef class GroebnerStrategy(SageObject): if unlikely(self._parent._ring != currRing): rChangeCurrRing(self._parent._ring) - cdef int max_ind + cdef int max_ind = 0 cdef poly *_p = redNF(p_Copy(p._poly, self._parent._ring), max_ind, 0, self._strat) if likely(_p!=NULL): _p = redtailBba(_p, max_ind, self._strat) diff --git a/src/sage/libs/singular/polynomial.pxd b/src/sage/libs/singular/polynomial.pxd index 7e74d4f90ef..c5cbd292cca 100644 --- a/src/sage/libs/singular/polynomial.pxd +++ b/src/sage/libs/singular/polynomial.pxd @@ -24,7 +24,7 @@ cdef int singular_polynomial_rmul (poly **ret, poly *p, RingElement q, ring *r) cdef int singular_polynomial_mul (poly **ret, poly *p, poly *q, ring *r) except -1 cdef int singular_polynomial_sub (poly **ret, poly *p, poly *q, ring *r) cdef int singular_polynomial_div_coeff (poly **ret, poly *p, poly *q, ring *r) except -1 -cdef int singular_polynomial_pow (poly **ret, poly *p, long exp, ring *r) except -1 +cdef int singular_polynomial_pow (poly **ret, poly *p, unsigned long exp, ring *r) except -1 cdef int singular_polynomial_neg(poly **ret, poly *p, ring *r) cdef object singular_polynomial_latex(poly *p, ring *r, object base, object latex_gens) @@ -34,3 +34,4 @@ cdef long singular_polynomial_deg(poly *p, poly *x, ring *r) cdef int singular_polynomial_length_bounded(poly *p, int bound) cdef int singular_vector_maximal_component(poly *v, ring *r) except -1 +cdef int singular_polynomial_subst(poly **p, int var_index, poly *value, ring *r) except -1 diff --git a/src/sage/libs/singular/polynomial.pyx b/src/sage/libs/singular/polynomial.pyx index 3d50bea0312..7a33c3e0a87 100644 --- a/src/sage/libs/singular/polynomial.pyx +++ b/src/sage/libs/singular/polynomial.pyx @@ -26,6 +26,7 @@ from sage.libs.singular.decl cimport p_GetMaxExp, pp_Mult_qq, pPower, p_String, from sage.libs.singular.decl cimport n_Delete, idInit, fast_map, id_Delete from sage.libs.singular.decl cimport omAlloc0, omStrDup, omFree from sage.libs.singular.decl cimport p_GetComp, p_SetComp +from sage.libs.singular.decl cimport pSubst from sage.libs.singular.singular cimport sa2si, si2sa, overflow_check @@ -290,7 +291,7 @@ cdef int singular_polynomial_div_coeff(poly** ret, poly *p, poly *q, ring *r) ex sig_off() return 0 -cdef int singular_polynomial_pow(poly **ret, poly *p, long exp, ring *r) except -1: +cdef int singular_polynomial_pow(poly **ret, poly *p, unsigned long exp, ring *r) except -1: """ ``ret[0] = p**exp`` where ``p`` in ``r`` and ``exp`` > 0. @@ -320,7 +321,6 @@ cdef int singular_polynomial_pow(poly **ret, poly *p, long exp, ring *r) except """ cdef unsigned long v = p_GetMaxExp(p, r) v = v * exp - overflow_check(v, r) if(r != currRing): rChangeCurrRing(r) @@ -407,7 +407,7 @@ cdef object singular_polynomial_latex(poly *p, ring *r, object base, object late \left(z + 1\right) v w - z w^{2} + z v + \left(-z - 1\right) w + z + 1 """ poly = "" - cdef long e,j + cdef unsigned long e,j cdef int n = r.N cdef int atomic_repr = base._repr_option('element_is_atomic') while p: @@ -519,11 +519,40 @@ cdef int singular_vector_maximal_component(poly *v, ring *r) except -1: returns the maximal module component of the vector ``v``. INPUT: - - ``v`` - a polynomial/vector - - ``r`` - a ring + - ``v`` - a polynomial/vector + - ``r`` - a ring """ cdef int res=0 while v!=NULL: res=max(p_GetComp(v, r), res) v = pNext(v) return res + +cdef int singular_polynomial_subst(poly **p, int var_index, poly *value, ring *r) except -1: + """ + Substitute variable ``var_index`` with ``value`` in ``p``. + + INPUT: + + - ``p`` - a polynomial + - ``var_index`` - an integer < ngens (zero based indexing) + - ``value`` - a polynomial + - ``r`` - a ring + """ + if p_IsConstant(value, r): + p[0] = pSubst(p[0], var_index+1, value) + return 0 + + cdef unsigned long exp = p_GetExp(p[0], var_index+1, r) * p_GetMaxExp(value, r) + + overflow_check(exp, r) + if(r != currRing): + rChangeCurrRing(r) + + cdef int count = singular_polynomial_length_bounded(p[0], 15) + if unlikely(count >= 15 or exp > 15): sig_on() + p[0] = pSubst(p[0], var_index+1, value) + if unlikely(count >= 15 or exp > 15): sig_off() + return 0 + + diff --git a/src/sage/libs/singular/singular.pxd b/src/sage/libs/singular/singular.pxd index d833e4d8c96..b02b53aa226 100644 --- a/src/sage/libs/singular/singular.pxd +++ b/src/sage/libs/singular/singular.pxd @@ -56,6 +56,6 @@ cdef number *sa2si(Element elem, ring * _ring) cdef int overflow_check(long e, ring *_ring) except -1 cdef init_libsingular() -cdef inline unsigned long get_max_exponent_size() + diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index 37f91e79a17..4eb807b0259 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -585,7 +585,7 @@ cdef object si2sa(number *n, ring *_ring, object base): raise ValueError, "cannot convert from SINGULAR number" cdef number *sa2si(Element elem, ring * _ring): - cdef int i + cdef int i = 0 if PY_TYPE_CHECK(elem._parent, FiniteField_prime_modn): return n_Init(int(elem),_ring) @@ -637,12 +637,9 @@ cdef extern from "dlfcn.h": cdef long RTLD_LAZY cdef long RTLD_GLOBAL -# Our attempt at avoiding exponent overflows. -cdef unsigned int max_exponent_size - cdef int overflow_check(long e, ring *_ring) except -1: """ - Raises an ``OverflowError`` if e is > ``max_exponent_size``, + Raises an ``OverflowError`` if e is > max degree per variable, or if it is not acceptable for Singular as exponent of the given ring. @@ -676,7 +673,8 @@ cdef int overflow_check(long e, ring *_ring) except -1: OverflowError: Exponent overflow (1073741824). # 32-bit """ - if unlikely(e > min(max_exponent_size,max(_ring.N,_ring.bitmask))): + # 2^31 (pPower takes ints) + if unlikely(e >= _ring.bitmask or e >= 2**31): raise OverflowError("Exponent overflow (%d)."%(e)) return 0 @@ -693,7 +691,6 @@ cdef init_libsingular(): """ global singular_options global singular_verbose_options - global max_exponent_size global WerrorS_callback global error_messages @@ -727,19 +724,10 @@ cdef init_libsingular(): On(SW_USE_EZGCD) Off(SW_USE_NTL_SORT) - if is_64_bit: - max_exponent_size = 1<<31-1; - else: - max_exponent_size = 1<<16-1; - WerrorS_callback = libsingular_error_callback error_messages = [] -cdef inline unsigned long get_max_exponent_size(): - global max_exponent_size - return max_exponent_size - # call the init routine init_libsingular() diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index 29e35313b75..7c3f2e845fa 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -177,7 +177,7 @@ from sage.libs.singular.decl cimport ( p_NSet, p_GetCoeff, p_Delete, p_GetExp, pNext, rRingVar, omAlloc0, omStrDup, omFree, pDivide, p_SetCoeff0, n_Init, p_DivisibleBy, pLcm, p_LmDivisibleBy, pDivide, p_IsConstant, p_ExpVectorEqual, p_String, p_LmInit, n_Copy, - p_IsUnit, pInvers, p_Head, pSubst, idInit, fast_map, id_Delete, + p_IsUnit, pInvers, p_Head, idInit, fast_map, id_Delete, pIsHomogeneous, pHomogen, p_Totaldegree, singclap_pdivide, singclap_factorize, delete, idLift, IDELEMS, On, Off, SW_USE_CHINREM_GCD, SW_USE_EZGCD, p_LmIsConstant, pTakeOutComp1, singclap_gcd, pp_Mult_qq, p_GetMaxExp, @@ -195,7 +195,7 @@ from sage.libs.singular.polynomial cimport ( singular_polynomial_mul, singular_polynomial_div_coeff, singular_polynomial_pow, singular_polynomial_str, singular_polynomial_latex, singular_polynomial_str_with_changed_varnames, singular_polynomial_deg, - singular_polynomial_length_bounded ) + singular_polynomial_length_bounded, singular_polynomial_subst ) # singular rings from sage.libs.singular.ring cimport singular_ring_new, singular_ring_reference, singular_ring_delete @@ -3266,9 +3266,31 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn sage: f = y sage: f.subs({y:x}).subs({x:z}) z + + We are catching overflows:: + + sage: R. = QQ[] + sage: n=1000; f = x^n + sage: try: + ....: f.subs(x = x^n) + ....: print "no overflow" + ....: except OverflowError: + ....: print "overflow" + overflow # 32-bit + no overflow # 64-bit + + sage: n=100000; + sage: try: + ....: f = x^n + ....: f.subs(x = x^n) + ....: print "no overflow" + ....: except OverflowError: + ....: print "overflow" + overflow """ cdef int mi, i, need_map, try_symbolic + cdef unsigned long degree = 0 cdef MPolynomialRing_libsingular parent = self._parent cdef ring *_ring = parent._ring @@ -3293,9 +3315,13 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn mi = i break if i > _ring.N: - raise TypeError, "key does not match" + id_Delete(&to_id, _ring) + p_Delete(&_p, _ring) + raise TypeError("key does not match") else: - raise TypeError, "keys do not match self's parent" + id_Delete(&to_id, _ring) + p_Delete(&_p, _ring) + raise TypeError("keys do not match self's parent") try: v = parent._coerce_c(v) except TypeError: @@ -3303,10 +3329,14 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn break _f = (v)._poly if p_IsConstant(_f, _ring): - if(_ring != currRing): rChangeCurrRing(_ring) - _p = pSubst(_p, mi, _f) + singular_polynomial_subst(&_p, mi-1, _f, _ring) else: need_map = 1 + degree = p_GetExp(_p, mi, _ring) * p_GetMaxExp(_f, _ring) + if degree > _ring.bitmask: + id_Delete(&to_id, _ring) + p_Delete(&_p, _ring) + raise OverflowError("Exponent overflow (%d)."%(degree)) to_id.m[mi-1] = p_Copy(_f, _ring) if not try_symbolic: @@ -3318,7 +3348,9 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn mi = i break if i > _ring.N: - raise TypeError, "key does not match" + id_Delete(&to_id, _ring) + p_Delete(&_p, _ring) + raise TypeError("key does not match") try: v = parent._coerce_c(v) except TypeError: @@ -3326,12 +3358,16 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn break _f = (v)._poly if p_IsConstant(_f, _ring): - if(_ring != currRing): rChangeCurrRing(_ring) - _p = pSubst(_p, mi, _f) + singular_polynomial_subst(&_p, mi-1, _f, _ring) else: if to_id.m[mi-1] != NULL: p_Delete(&to_id.m[mi-1],_ring) to_id.m[mi-1] = p_Copy(_f, _ring) + degree = p_GetExp(_p, mi, _ring) * p_GetMaxExp(_f, _ring) + if degree > _ring.bitmask: + id_Delete(&to_id, _ring) + p_Delete(&_p, _ring) + raise OverflowError("Exponent overflow (%d)."%(degree)) need_map = 1 if need_map: From 7a9a6b2a3e4a68bfd3c9d8d6556a883e1d1161e0 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 26 Jun 2014 22:20:03 -0400 Subject: [PATCH 397/546] Review changes. --- src/sage/misc/cachefunc.pyx | 76 +++++++++++++++---- .../rings/padics/padic_ZZ_pX_CR_element.pyx | 23 ++++-- .../rings/polynomial/polynomial_element.pyx | 6 +- src/sage/structure/sage_object.pyx | 67 ++-------------- 4 files changed, 83 insertions(+), 89 deletions(-) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index 2dff72821fc..3b2f8afd64c 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -407,6 +407,60 @@ easier method:: sage: timeit("a = Q.f(2,3)") # random 625 loops, best of 3: 931 ns per loop +Some immutable objects (such as `p`-adic numbers) cannot implement a +reasonable hash function because their ``==`` operator has been +modified to return ``True`` for objects which might behave differently +in some computations:: + + sage: K. = Qq(9) + sage: b = a + O(3) + sage: c = a + 3 + sage: b + a + O(3) + sage: c + a + 3 + O(3^20) + sage: b == c + True + sage: b == a + True + sage: c == a + False + +If such objects defined a non-trivial hash function, this would break +caching in many places. However, such objects should still be usable +in caches. This can be achieved by defining an appropriate attribute +``_cache_key``. Generally one would want to make this a ``lazy_attribute``:: + + sage: hash(b) + Traceback (most recent call last): + ... + TypeError: unhashable type: 'sage.rings.padics.padic_ZZ_pX_CR_element.pAdicZZpXCRElement' + sage: @cached_method + ....: def f(x): return x==a + sage: f(b) + True + sage: f(c) # if b and c were hashable, this would return True + False + + sage: b._cache_key + (..., ((0, 1),), 0, 1) + sage: c._cache_key + (..., ((0, 1), (1,)), 0, 20) + +.. NOTE:: + + This attribute will only be accessed if the object itself + is not hashable. + +An implementation must make sure that for elements ``a`` and ``b``, +if ``a != b``, then also ``a._cache_key != b._cache_key``. +In practice this means that the ``_cache_key`` should always include +the parent as its first argument:: + + sage: S. = Qq(4) + sage: d = a + O(2) + sage: b._cache_key == d._cache_key # this would be True if the parents were not included + False """ ######################################################################## # Copyright (C) 2008 William Stein @@ -473,7 +527,7 @@ def _cache_key(o): This function is intended for objects which are not hashable such as `p`-adic numbers. The difference from calling an object's ``_cache_key`` - method directly, is that it also works for tuples and unpacks them + attribute directly, is that it also works for tuples and unpacks them recursively (if necessary, i.e., if they are not hashable). EXAMPLES:: @@ -498,22 +552,12 @@ def _cache_key(o): sage: o = (1/2, a) sage: _cache_key(o) (1/2, (..., ((1,),), 0, 20)) - - .. SEEALSO:: - - :meth:`sage.structure.sage_object.SageObject._cache_key` - """ - try: - hash(o) - return o - except TypeError: - if isinstance(o, sage.structure.sage_object.SageObject): - o = o._cache_key() - if isinstance(o,tuple): - return tuple(_cache_key(item) for item in o) - else: - return o + if isinstance(o, sage.structure.sage_object.SageObject): + o = o._cache_key() + if isinstance(o, tuple): + return tuple(_cache_key(item) for item in o) + return o cdef class CachedFunction(object): """ diff --git a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx index 67830f15375..17b0ee8a6a0 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx @@ -476,12 +476,9 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): r""" Return a hashable key which identifies this element. - This makes it possible to use this element in caches such as methods - decorated with ``@cached_method``. - - .. SEEALSO:: - - :meth:`sage.structure.sage_object.SageObject._cache_key` + This makes it possible to use this element in caches such as + functions or methods decorated with ``@cached_function`` or + ``@cached_method`` respectively. EXAMPLE: @@ -511,17 +508,27 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): Check that zero values are handled correctly:: - sage: K.zero()._cache_key() + sage: K.zero()._cache_key (..., 0) - sage: K(0,1)._cache_key() + sage: K(0,1)._cache_key (..., 0, 1) """ + print "enter" if self._is_exact_zero(): + print "exact zero" return (self.parent(), 0) elif self._is_inexact_zero(): + print "inexact zero" return (self.parent(), 0, self.valuation()) else: + print "other" + print self.parent() + print self.valuation() + print self.precision_relative() + print self.unit_part().list() + print tuple(tuple(c) if isinstance(c, list) else c + for c in self.unit_part().list()) return (self.parent(), tuple(tuple(c) if isinstance(c, list) else c for c in self.unit_part().list()), diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index bf499644eff..6ebfb49781c 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -834,10 +834,6 @@ cdef class Polynomial(CommutativeAlgebraElement): """ Return a hashable key which identifies this element. - .. SEEALSO:: - - :meth:`sage.structure.sage_object.SageObject._cache_key` - EXAMPLES:: sage: K. = Qq(4) @@ -857,7 +853,7 @@ cdef class Polynomial(CommutativeAlgebraElement): (1 + O(2^20))*x """ - return self.parent(), tuple(self) + return (self.parent(), tuple(self)) # you may have to replicate this boilerplate code in derived classes if you override # __richcmp__. The python documentation at http://docs.python.org/api/type-structs.html diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index 578febd0224..571b5b60cb9 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -222,70 +222,17 @@ cdef class SageObject: return hash(self.__repr__()) def _cache_key(self): - r""" - Return a hashable key which identifies this objects for caching. The - output must be hashable itself, or a tuple of objects which are - hashable or define a ``_cache_key``. - - This method will only be called if the object itself is not hashable. - - Some immutable objects (such as `p`-adic numbers) cannot implement a - reasonable hash function because their ``==`` operator has been - modified to return ``True`` for objects which might behave differently - in some computations:: - - sage: K. = Qq(9) - sage: b = a + O(3) - sage: c = a + 3 - sage: b - a + O(3) - sage: c - a + 3 + O(3^20) - sage: b == c - True - sage: b == a - True - sage: c == a - False + """ + Return a hashable key which identifies ``self``. The default + implementation returns ``self``. - If such objects defined a non-trivial hash function, this would break - caching in many places. However, such objects should still be usable in - caches. This can be achieved by defining an appropriate - ``_cache_key``:: + EXAMPLES:: - sage: hash(b) - Traceback (most recent call last): - ... - TypeError: unhashable type: 'sage.rings.padics.padic_ZZ_pX_CR_element.pAdicZZpXCRElement' - sage: @cached_method - ....: def f(x): return x==a - sage: f(b) + sage: x = 5 + sage: x._cache_key() == x True - sage: f(c) # if b and c were hashable, this would return True - False - - sage: b._cache_key() - (..., ((0, 1),), 0, 1) - sage: c._cache_key() - (..., ((0, 1), (1,)), 0, 20) - - An implementation must make sure that for elements ``a`` and ``b``, - if ``a != b``, then also ``a._cache_key() != b._cache_key()``. - In practice this means that the ``_cache_key`` should always include - the parent as its first argument:: - - sage: S. = Qq(4) - sage: d = a + O(2) - sage: b._cache_key() == d._cache_key() # this would be True if the parents were not included - False - """ - try: - hash(self) - except TypeError: - raise NotImplementedError("{} is not hashable and does not implement _cache_key()".format(type(self))) - else: - assert False, "_cache_key() must not be called for hashable elements" + return self ############################################################################# # DATABASE Related code From b41c4554ddcc9e25fc293fbee386468848e6ee0d Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Fri, 27 Jun 2014 04:40:58 +0200 Subject: [PATCH 398/546] restore previous version of _cache_key() in cachefunc.pyx --- src/sage/misc/cachefunc.pyx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index 3b2f8afd64c..85e1f13265d 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -553,11 +553,16 @@ def _cache_key(o): sage: _cache_key(o) (1/2, (..., ((1,),), 0, 20)) """ - if isinstance(o, sage.structure.sage_object.SageObject): - o = o._cache_key() - if isinstance(o, tuple): - return tuple(_cache_key(item) for item in o) - return o + try: + hash(o) + return o + except TypeError: + if isinstance(o, sage.structure.sage_object.SageObject): + o = o._cache_key() + if isinstance(o,tuple): + return tuple(_cache_key(item) for item in o) + else: + return o cdef class CachedFunction(object): """ From 37aa276dbbf3b842cf6ec2718f01da46f41f7384 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Fri, 27 Jun 2014 04:46:10 +0200 Subject: [PATCH 399/546] reverted changes which treated _cache_key like an attribute --- src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx index 17b0ee8a6a0..4bb19ef44db 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx @@ -508,9 +508,9 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): Check that zero values are handled correctly:: - sage: K.zero()._cache_key + sage: K.zero()._cache_key() (..., 0) - sage: K(0,1)._cache_key + sage: K(0,1)._cache_key() (..., 0, 1) """ From 5586f567789776b2760a199eeb0fedc8e5b45f75 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Fri, 27 Jun 2014 04:46:39 +0200 Subject: [PATCH 400/546] removed debug output --- src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx index 4bb19ef44db..db7c42bc08f 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx @@ -514,21 +514,11 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): (..., 0, 1) """ - print "enter" if self._is_exact_zero(): - print "exact zero" return (self.parent(), 0) elif self._is_inexact_zero(): - print "inexact zero" return (self.parent(), 0, self.valuation()) else: - print "other" - print self.parent() - print self.valuation() - print self.precision_relative() - print self.unit_part().list() - print tuple(tuple(c) if isinstance(c, list) else c - for c in self.unit_part().list()) return (self.parent(), tuple(tuple(c) if isinstance(c, list) else c for c in self.unit_part().list()), From 83944fabe78c5561f0cc5b0845d5e8bdb30098c2 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Fri, 27 Jun 2014 04:53:23 +0200 Subject: [PATCH 401/546] Restored old version of SageObject._cache_key() but raising a TypeError instead of a NotImplementedError. --- src/sage/structure/sage_object.pyx | 67 ++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index 571b5b60cb9..0b9d80e1ed0 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -222,17 +222,70 @@ cdef class SageObject: return hash(self.__repr__()) def _cache_key(self): - """ - Return a hashable key which identifies ``self``. The default - implementation returns ``self``. + r""" + Return a hashable key which identifies this objects for caching. The + output must be hashable itself, or a tuple of objects which are + hashable or define a ``_cache_key``. + + This method will only be called if the object itself is not hashable. + + Some immutable objects (such as `p`-adic numbers) cannot implement a + reasonable hash function because their ``==`` operator has been + modified to return ``True`` for objects which might behave differently + in some computations:: + + sage: K. = Qq(9) + sage: b = a + O(3) + sage: c = a + 3 + sage: b + a + O(3) + sage: c + a + 3 + O(3^20) + sage: b == c + True + sage: b == a + True + sage: c == a + False - EXAMPLES:: + If such objects defined a non-trivial hash function, this would break + caching in many places. However, such objects should still be usable in + caches. This can be achieved by defining an appropriate + ``_cache_key``:: - sage: x = 5 - sage: x._cache_key() == x + sage: hash(b) + Traceback (most recent call last): + ... + TypeError: unhashable type: 'sage.rings.padics.padic_ZZ_pX_CR_element.pAdicZZpXCRElement' + sage: @cached_method + ....: def f(x): return x==a + sage: f(b) True + sage: f(c) # if b and c were hashable, this would return True + False + + sage: b._cache_key() + (..., ((0, 1),), 0, 1) + sage: c._cache_key() + (..., ((0, 1), (1,)), 0, 20) + + An implementation must make sure that for elements ``a`` and ``b``, + if ``a != b``, then also ``a._cache_key() != b._cache_key()``. + In practice this means that the ``_cache_key`` should always include + the parent as its first argument:: + + sage: S. = Qq(4) + sage: d = a + O(2) + sage: b._cache_key() == d._cache_key() # this would be True if the parents were not included + False + """ - return self + try: + hash(self) + except TypeError: + raise TypeError("{} is not hashable and does not implement _cache_key()".format(type(self))) + else: + assert False, "_cache_key() must not be called for hashable elements" ############################################################################# # DATABASE Related code From 6f4b5b6d4dc5a1d84732f9570757fd93349e3f77 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Fri, 27 Jun 2014 10:49:55 +0200 Subject: [PATCH 402/546] Improve printing of function field morphisms They should print like any other morphism; in particular they now show they domain and codomain. --- .../rings/function_field/function_field.py | 46 +++++++++++---- src/sage/rings/function_field/maps.py | 59 +++++++++++++------ 2 files changed, 76 insertions(+), 29 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 16abb0df13f..98b25b73d4b 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -463,9 +463,15 @@ def monic_integral_model(self, names): sage: A Function field in z defined by y^5 - x^12 sage: from_A - Morphism of function fields defined by z |--> x^3*y + Function Field morphism: + From: Function field in z defined by y^5 - x^12 + To: Function field in y defined by x^2*y^5 - 1/x + Defn: z |--> x^3*y sage: to_A - Morphism of function fields defined by y |--> 1/x^3*z + Function Field morphism: + From: Function field in y defined by x^2*y^5 - 1/x + To: Function field in z defined by y^5 - x^12 + Defn: y |--> 1/x^3*z sage: to_A(y) 1/x^3*z sage: from_A(to_A(y)) @@ -854,7 +860,8 @@ def hom(self, im_gens, base_morphism=None): We make the field automorphism that sends y to -y:: sage: f = L.hom(-y); f - Morphism of function fields defined by y |--> -y + Function Field endomorphism of Function field in y defined by y^2 - x^3 - 1 + Defn: y |--> -y Evaluation works:: @@ -871,7 +878,8 @@ def hom(self, im_gens, base_morphism=None): We make a morphism of the base rational function field:: sage: phi = K.hom(x+1); phi - Morphism of function fields defined by x |--> x + 1 + Function Field endomorphism of Rational function field in x over Rational Field + Defn: x |--> x + 1 sage: phi(x^3 - 3) x^3 + 3*x^2 + 3*x - 2 sage: (x+1)^3-3 @@ -881,7 +889,9 @@ def hom(self, im_gens, base_morphism=None): base generators go:: sage: L.hom([-y, x]) - Morphism of function fields defined by y |--> -y, x |--> x + Function Field endomorphism of Function field in y defined by y^2 - x^3 - 1 + Defn: y |--> -y + x |--> x The usage of the keyword base_morphism is not implemented yet:: @@ -898,7 +908,11 @@ def hom(self, im_gens, base_morphism=None): We define a morphism, by giving the images of generators:: sage: f = L.hom([4*w, t+1]); f - Morphism of function fields defined by y |--> 4*w, x |--> t + 1 + Function Field morphism: + From: Function field in y defined by y^2 - x^3 - 1 + To: Function field in w defined by 16*w^2 - t^3 - 3*t^2 - 3*t - 2 + Defn: y |--> 4*w + x |--> t + 1 Evaluation works, as expected:: @@ -915,7 +929,12 @@ def hom(self, im_gens, base_morphism=None): This is the function field L with the generators exchanged. We define a morphism to L:: sage: g = L3.hom([x,y]); g - Morphism of function fields defined by xx |--> x, yy |--> y + Function Field morphism: + From: Function field in xx defined by -xx^3 + yy^2 - 1 + To: Function field in y defined by y^2 - x^3 - 1 + Defn: xx |--> x + yy |--> y + """ if base_morphism is not None: raise NotImplementedError("Function field homorphisms with optional argument base_morphism are not implemented yet. Please specify the images of the generators of the base fields manually.") @@ -1018,7 +1037,10 @@ class RationalFunctionField(FunctionField): sage: K. = FunctionField(QQ) sage: L = FunctionField(QQ, 'tbar') # give variable name as second input sage: K.hom(L.gen()) - Morphism of function fields defined by t |--> tbar + Function Field morphism: + From: Rational function field in t over Rational Field + To: Rational function field in tbar over Rational Field + Defn: t |--> tbar """ def __init__(self, constant_field, names, element_class = FunctionFieldElement_rational, @@ -1354,7 +1376,8 @@ def hom(self, im_gens, base_morphism=None): sage: K. = FunctionField(GF(7)) sage: K.hom( (x^4 + 2)/x) - Morphism of function fields defined by x |--> (x^4 + 2)/x + Function Field endomorphism of Rational function field in x over Finite Field of size 7 + Defn: x |--> (x^4 + 2)/x We construct a map from a rational function field into a non-rational extension field:: @@ -1362,7 +1385,10 @@ def hom(self, im_gens, base_morphism=None): sage: K. = FunctionField(GF(7)); R. = K[] sage: L. = K.extension(y^3 + 6*x^3 + x) sage: f = K.hom(y^2 + y + 2); f - Morphism of function fields defined by x |--> y^2 + y + 2 + Function Field morphism: + From: Rational function field in x over Finite Field of size 7 + To: Function field in y defined by y^3 + 6*x^3 + x + Defn: x |--> y^2 + y + 2 sage: f(x) y^2 + y + 2 sage: f(x^2) diff --git a/src/sage/rings/function_field/maps.py b/src/sage/rings/function_field/maps.py index 4763d1d3501..0ce6b6fa822 100644 --- a/src/sage/rings/function_field/maps.py +++ b/src/sage/rings/function_field/maps.py @@ -11,12 +11,18 @@ sage: K. = FunctionField(QQ); R. = K[] sage: K.hom(1/x) - Morphism of function fields defined by x |--> 1/x + Function Field endomorphism of Rational function field in x over Rational Field + Defn: x |--> 1/x sage: L. = K.extension(y^2-x) sage: K.hom(y) - Morphism of function fields defined by x |--> y + Function Field morphism: + From: Rational function field in x over Rational Field + To: Function field in y defined by y^2 - x + Defn: x |--> y sage: L.hom([y,x]) - Morphism of function fields defined by y |--> y, x |--> x + Function Field endomorphism of Function field in y defined by y^2 - x + Defn: y |--> y + x |--> x sage: L.hom([x,y]) Traceback (most recent call last): ... @@ -50,7 +56,8 @@ class FunctionFieldIsomorphism(Morphism): """ def _repr_type(self): """ - Return the type of this map (an isomorphism), for the purposes of printing out self. + Return the type of this map (an isomorphism), for the purposes of + printing this map. EXAMPLES:: @@ -218,6 +225,9 @@ def codomain(self): def _repr_type(self): """ + Return the type of this map (an isomorphism), for the purposes of + printing this map. + EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] @@ -253,7 +263,8 @@ def __init__(self, parent, im_gen, base_morphism): sage: K. = FunctionField(QQ) sage: f = K.hom(1/x); f - Morphism of function fields defined by x |--> 1/x + Function Field endomorphism of Rational function field in x over Rational Field + Defn: x |--> 1/x sage: isinstance(f, sage.rings.function_field.maps.FunctionFieldMorphism) True """ @@ -270,37 +281,42 @@ def is_injective(self): sage: K. = FunctionField(QQ) sage: f = K.hom(1/x); f - Morphism of function fields defined by x |--> 1/x + Function Field endomorphism of Rational function field in x over Rational Field + Defn: x |--> 1/x sage: f.is_injective() True """ return True - def __repr__(self): - """ + def _repr_type(self): + r""" + Return the type of this map (a morphism of function fields), for the + purposes of printing this map. + EXAMPLES:: sage: K. = FunctionField(GF(7)); R. = K[] sage: L. = K.extension(y^3 + 6*x^3 + x) sage: f = L.hom(y*2) - sage: f.__repr__() - 'Morphism of function fields defined by y |--> 2*y' + sage: f._repr_type() + 'Function Field' + """ - return "Morphism of function fields defined by %s"%self._short_repr() + return "Function Field" - def _short_repr(self): + def _repr_defn(self): """ EXAMPLES:: sage: K. = FunctionField(GF(7)); R. = K[] sage: L. = K.extension(y^3 + 6*x^3 + x) sage: f = L.hom(y*2) - sage: f._short_repr() + sage: f._repr_defn() 'y |--> 2*y' """ a = '%s |--> %s'%(self.domain().gen(), self._im_gen) if self._base_morphism is not None: - a += ', ' + self._base_morphism._short_repr() + a += '\n' + self._base_morphism._repr_defn() return a class FunctionFieldMorphism_polymod(FunctionFieldMorphism): @@ -312,7 +328,8 @@ class FunctionFieldMorphism_polymod(FunctionFieldMorphism): sage: K. = FunctionField(QQ); R. = K[] sage: L. = K.extension(y^2 - x) sage: f = L.hom(-y); f - Morphism of function fields defined by y |--> -y + Function Field endomorphism of Function field in y defined by y^2 - x + Defn: y |--> -y """ def __init__(self, parent, im_gen, base_morphism): """ @@ -321,7 +338,8 @@ def __init__(self, parent, im_gen, base_morphism): sage: K. = FunctionField(GF(7)); R. = K[] sage: L. = K.extension(y^3 + 6*x^3 + x) sage: f = L.hom(y*2); f - Morphism of function fields defined by y |--> 2*y + Function Field endomorphism of Function field in y defined by y^3 + 6*x^3 + x + Defn: y |--> 2*y sage: type(f) sage: factor(L.polynomial()) @@ -364,7 +382,8 @@ class FunctionFieldMorphism_rational(FunctionFieldMorphism): sage: K. = FunctionField(QQ) sage: f = K.hom(1/x); f - Morphism of function fields defined by x |--> 1/x + Function Field endomorphism of Rational function field in x over Rational Field + Defn: x |--> 1/x """ def __init__(self, parent, im_gen): """ @@ -372,7 +391,8 @@ def __init__(self, parent, im_gen): sage: K. = FunctionField(GF(7)) sage: f = K.hom(1/x); f - Morphism of function fields defined by x |--> 1/x + Function Field endomorphism of Rational function field in x over Finite Field of size 7 + Defn: x |--> 1/x sage: type(f) """ @@ -384,7 +404,8 @@ def _call_(self, x): sage: K. = FunctionField(GF(7)) sage: f = K.hom(1/x); f - Morphism of function fields defined by x |--> 1/x + Function Field endomorphism of Rational function field in x over Finite Field of size 7 + Defn: x |--> 1/x sage: f(x+1) # indirect doctest (x + 1)/x sage: 1/x + 1 From 4e0738355f228ea068bea927f3025dcccb955b1c Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Fri, 27 Jun 2014 11:21:54 +0200 Subject: [PATCH 403/546] 6882: add rules for e, i, I --- src/sage/calculus/calculus.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index 42429c906e3..5b02d00e473 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -1692,7 +1692,7 @@ def _inverse_laplace_latex_(self, *args): ####################################################### -symtable = {'%pi':'pi', '%e': 'e', '%i':'I', '%gamma':'euler_gamma'} +symtable = {'%pi':'pi', 'e':'_e', '%e': 'e', 'i':'_i', 'I':'_I', '%i':'I', '%gamma':'euler_gamma'} from sage.misc.multireplace import multiple_replace import re @@ -1766,6 +1766,18 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): sage: solve([2*x==3, x != 5], x) [[x == (3/2), (-7/2) != 0]] + Check that some variables don't end up as special constants (:trac:`6882`):: + + sage: symbolic_expression_from_maxima_string('%i')^2 + -1 + sage: symbolic_expression_from_maxima_string('I')^2 + _I^2 + sage: symbolic_expression_from_maxima_string('i')^2 + _i^2 + sage: ln(symbolic_expression_from_maxima_string('%e')) + 1 + sage: ln(symbolic_expression_from_maxima_string('e')) + log(_e) """ syms = sage.symbolic.pynac.symbol_table.get('maxima', {}).copy() From c6e78c8f358758c88fd5fe7d806c9ead5e503a33 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Fri, 27 Jun 2014 14:22:22 +0200 Subject: [PATCH 404/546] trac #16524: move the cyclic difference set to the constructor --- src/sage/combinat/designs/database.py | 42 +++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 4bd4661f3fe..d21c8e675ab 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -2319,7 +2319,7 @@ def OA_9_135(): This design can be built by Wilson's method (`135 = 8.16 + 7`) applied to an Orthogonal Array `OA(9+7,16)` with 7 groups truncated to size 1 in - such a way that a block contain 0,1, or 3 points of the truncated + such a way that a block contain 0, 1 or 3 points of the truncated groups. This is possible, because `PG(2,2)` is a subdesign in `PG(2,16)`; in a @@ -2338,10 +2338,19 @@ def OA_9_135(): sage: designs.orthogonal_array(9,135,existence=True) True + + As this orthogonal array requires a `(273,17,1)` cyclic difference set, we check that + it is available:: + + sage: G,D = designs.difference_family(273,17,1) + sage: G + Ring of integers modulo 273 """ - n=273 - B = (1,2,4,8,16,32,64,91,117,128,137,182,195,205,234,239,256) # a (273,17,1)-difference set - PG16 = [[(x+c)%n for x in B] for c in range(n)] + from bibd import BIBD_from_difference_family + G,B = CDF_273_17_1() + PG16 = BIBD_from_difference_family(G,B) + + n = 273 # PG2 is a (7,3,1)-design (fano plane) contained in PG16. It is a set of 7 # points that any block of PG16 intersect on 0,1, or 3 points. @@ -2927,7 +2936,27 @@ def CDF_221_5_1(): from sage.rings.finite_rings.integer_mod_ring import Zmod return Zmod(221), D -# Index of the (right now cyclic) difference families constructions +def CDF_273_17_1(): + r""" + A cyclic `(273,17,1)`-difference set. + + EXAMPLES:: + + sage: from sage.combinat.designs.database import CDF_273_17_1 + sage: from sage.combinat.designs.difference_family import is_difference_family + sage: G,D = CDF_273_17_1() + sage: is_difference_family(G,D,273,17,1) + True + + The difference family is available from the constructor:: + + sage: _ = designs.difference_family(273,17,1) + """ + from sage.rings.finite_rings.integer_mod_ring import Zmod + D = [(1,2,4,8,16,32,64,91,117,128,137,182,195,205,234,239,256)] + return Zmod(273), D + +# Index of the (right now cyclic or Abelian) difference families constructions # # Associates to triple (v,k,lambda) a function that return a # (n,k,lambda)-difference family. @@ -2944,5 +2973,6 @@ def CDF_221_5_1(): (141,5,1): CDF_141_5_1, (161,5,1): CDF_161_5_1, (201,5,1): CDF_201_5_1, - (221,5,1): CDF_221_5_1 + (221,5,1): CDF_221_5_1, + (273,17,1): CDF_273_17_1, } From 91eb3d2d16fa8a4576d6b5d6e2686af6f8593def Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 27 Jun 2014 14:43:44 +0200 Subject: [PATCH 405/546] trac #16503: Review --- src/sage/combinat/designs/orthogonal_arrays_recursive.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_recursive.py index 52cbd05343a..544691c9bbd 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_recursive.py @@ -653,11 +653,9 @@ def construction_q_x(k,q,x,check=True): points_to_keep = set(range(q**2+2))-points_to_delete relabel = {i:j for j,i in enumerate(points_to_keep)} - from sage.combinat.designs.bibd import _check_pbd + # PBD is a (n,[q,q-x-1,q-x+1,x+2])-PBD PBD = [[relabel[xx] for xx in B if not xx in points_to_delete] for B in TD] - # _check_pbd(PBD,n,[q,q-x-1,q-x+1,x+2]) - # Taking the unique block of size x+2 assert map(len,PBD).count(x+2)==1 for B in PBD: @@ -717,12 +715,12 @@ def find_q_x(k,n): # n = (q-1)*(q-x) + x + 2 # = q^2 - q*x - q + 2*x + 2 - for q in range(3,n): + for q in range(max(3,k+2),n): # n-q**2+q-2 = 2x-qx # = x(2-q) x = (n-q**2+q-2)//(2-q) if (x < q and - 1 < x and + 0 < x and n == (q-1)*(q-x)+x+2 and is_prime_power(q) and orthogonal_array(k+1,q-x-1,existence=True) and From a0294d3b5b6d2e1cbf6f3f5dd625df765350a478 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Fri, 27 Jun 2014 16:54:05 +0200 Subject: [PATCH 406/546] trac #16524: more comments in the doc --- src/sage/combinat/designs/database.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index d21c8e675ab..4f482de651b 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -2322,9 +2322,11 @@ def OA_9_135(): such a way that a block contain 0, 1 or 3 points of the truncated groups. - This is possible, because `PG(2,2)` is a subdesign in `PG(2,16)`; in a - cyclic `PG(2,16)` or `BIBD(273,17,1)` the points `\equiv 0 \pmod{39}` - form such a subdesign. + This is possible, because `PG(2,2)` (the projective plane over `GF(2)`) + is a subdesign in `PG(2,16)` (the projective plane over `GF(16)`); in a + cyclic `PG(2,16)` or `BIBD(273,17,1)` the points `\equiv 0 + \pmod{39}` form such a subdesign (note that `273=16^2 + 16 +1` and + `273 = 39 \times 7` and `7 = 2^2 + 2 + 1`). EXAMPLES:: From 8192e55461a3f7ac136ed5b9e5301be5043f4bd9 Mon Sep 17 00:00:00 2001 From: Release Manager Date: Fri, 27 Jun 2014 11:17:51 -0400 Subject: [PATCH 407/546] Trac #16423: Table of MOLS from the handbook and comparison with Sage With this branch we have a `MOLS_table` function which displays what Sage can handle, and also compare it with what the Handbook of Combinatorial Designs (2ed) claims. We seem to be rather far but I have several construction that somehow make the gap smaller. But there is a lot to review already `:-P` Nathan URL: http://trac.sagemath.org/16423 Reported by: ncohen Ticket author(s): Nathann Cohen Reviewer(s): Vincent Delecroix From c9a1ebcc33f1aaf1fbc2147de1a639f2c550ecb2 Mon Sep 17 00:00:00 2001 From: Martin Albrecht Date: Fri, 27 Jun 2014 09:14:29 -0700 Subject: [PATCH 408/546] fixed SIGSEGV when calling subs on 0 --- src/sage/rings/polynomial/multi_polynomial_libsingular.pyx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index ae78add4f15..da8baf419f5 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -3288,6 +3288,9 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn ....: print "overflow" overflow """ + if not self._poly: + return self + cdef int mi, i, need_map, try_symbolic cdef unsigned long degree = 0 From 38c07da8745a467c2669ee70adab4e5fbb927876 Mon Sep 17 00:00:00 2001 From: Martin Albrecht Date: Fri, 27 Jun 2014 09:15:04 -0700 Subject: [PATCH 409/546] shut up Cython warnings about complicated declarations --- .../multi_polynomial_libsingular.pyx | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index da8baf419f5..c9f673c8455 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -731,7 +731,9 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_generic): 2 """ - cdef poly *_p, *mon, *El_poly + cdef poly *_p + cdef poly *mon + cdef poly *El_poly cdef ring *_ring = self._ring cdef number *_n cdef ring *El_ring @@ -1520,7 +1522,8 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_generic): z^3 + w^3 - z*w """ cdef ring *_ring = self._ring - cdef char **_names, **_orig_names + cdef char **_names + cdef char **_orig_names cdef char *_name cdef int i @@ -1617,7 +1620,8 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_generic): """ cdef poly *res cdef ring *r = self._ring - cdef number *n, *denom + cdef number *n + cdef number *denom if not self is f._parent: f = self._coerce_c(f) @@ -1832,7 +1836,8 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_generic): """ cdef int i cdef ring *r - cdef poly *p, *q + cdef poly *p + cdef poly *q if h._parent is not g._parent: g = h._parent._coerce_c(g) @@ -3279,7 +3284,7 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn overflow # 32-bit no overflow # 64-bit - sage: n=100000; + sage: n=100000; sage: try: ....: f = x^n ....: f.subs(x = x^n) @@ -3649,7 +3654,8 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn sage: f.variables() (x, z) """ - cdef poly *p, *v + cdef poly *p + cdef poly *v cdef ring *r = self._parent_ring if(r != currRing): rChangeCurrRing(r) cdef int i @@ -3883,7 +3889,9 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn cdef ring *r = self._parent_ring if(r != currRing): rChangeCurrRing(r) cdef MPolynomial_libsingular _self, _right - cdef poly *quo, *temp, *p + cdef poly *quo + cdef poly *temp + cdef poly *p _self = self @@ -4515,7 +4523,9 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn (a^2 + a)*x^6*y + (a^3 + a - 1)*x^4*y + (-a)*x^4 """ cdef ring *_ring = self._parent_ring - cdef poly *ret, *prod, *gcd + cdef poly *ret + cdef poly *prod + cdef poly *gcd cdef MPolynomial_libsingular _g if _ring!=currRing: rChangeCurrRing(_ring) @@ -4606,7 +4616,8 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn ZeroDivisionError """ - cdef poly *quo, *rem + cdef poly *quo + cdef poly *rem cdef MPolynomialRing_libsingular parent = self._parent cdef ring *r = self._parent_ring if r!=currRing: rChangeCurrRing(r) @@ -4962,7 +4973,8 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn except ValueError: raise TypeError("not a variable in the same ring as self") - cdef poly *_p, *mon + cdef poly *_p + cdef poly *mon cdef ring *_ring = self._parent_ring if _ring != currRing: rChangeCurrRing(_ring) @@ -5272,7 +5284,8 @@ def unpickle_MPolynomial_libsingular(MPolynomialRing_libsingular R, d): True """ cdef ring *r = R._ring - cdef poly *m, *p + cdef poly *m + cdef poly *p cdef int _i, _e p = p_ISet(0,r) rChangeCurrRing(r) From c987fc794ee39f7cc20494eb2f334ababe3e1664 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Fri, 27 Jun 2014 19:13:30 +0200 Subject: [PATCH 410/546] _cache_key is not an attribute anymore --- src/sage/misc/cachefunc.pyx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index 85e1f13265d..c2143b25242 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -428,8 +428,8 @@ in some computations:: If such objects defined a non-trivial hash function, this would break caching in many places. However, such objects should still be usable -in caches. This can be achieved by defining an appropriate attribute -``_cache_key``. Generally one would want to make this a ``lazy_attribute``:: +in caches. This can be achieved by defining an appropriate method +``_cache_key``. sage: hash(b) Traceback (most recent call last): @@ -442,9 +442,9 @@ in caches. This can be achieved by defining an appropriate attribute sage: f(c) # if b and c were hashable, this would return True False - sage: b._cache_key + sage: b._cache_key() (..., ((0, 1),), 0, 1) - sage: c._cache_key + sage: c._cache_key() (..., ((0, 1), (1,)), 0, 20) .. NOTE:: @@ -453,13 +453,13 @@ in caches. This can be achieved by defining an appropriate attribute is not hashable. An implementation must make sure that for elements ``a`` and ``b``, -if ``a != b``, then also ``a._cache_key != b._cache_key``. +if ``a != b``, then also ``a._cache_key() != b._cache_key()``. In practice this means that the ``_cache_key`` should always include the parent as its first argument:: sage: S. = Qq(4) sage: d = a + O(2) - sage: b._cache_key == d._cache_key # this would be True if the parents were not included + sage: b._cache_key() == d._cache_key() # this would be True if the parents were not included False """ ######################################################################## From a4f17d1948e014d50af273ca8901bd6d465327b9 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Fri, 27 Jun 2014 19:44:40 +0200 Subject: [PATCH 411/546] fixed doctests --- src/sage/misc/cachefunc.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index c2143b25242..6400724bca7 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -413,7 +413,7 @@ modified to return ``True`` for objects which might behave differently in some computations:: sage: K. = Qq(9) - sage: b = a + O(3) + sage: b = a.add_bigoh(1) sage: c = a + 3 sage: b a + O(3) @@ -458,7 +458,7 @@ In practice this means that the ``_cache_key`` should always include the parent as its first argument:: sage: S. = Qq(4) - sage: d = a + O(2) + sage: d = a.add_bigoh(1) sage: b._cache_key() == d._cache_key() # this would be True if the parents were not included False """ From aa037de6a612d1a6cd0cd6a8b9d3cb828feaef47 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Fri, 27 Jun 2014 20:16:49 +0200 Subject: [PATCH 412/546] Fix collisions between unhashable and hashable elements in @cached_method We include an element in the hash key which safely pickles and unpickles, which is only equal to itself, and which certainly will never be used as a key for any legitimate hashable element. --- src/sage/misc/cachefunc.pyx | 60 ++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index 6400724bca7..bd2710b1ace 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -881,7 +881,7 @@ cdef class CachedFunction(object): :meth:`sage.structure.sage_object.SageObject._cache_key`. sage: @cached_function - ....: def f(x): return x + ....: def f(x): return x+x sage: K. = Qq(4) sage: x = K(1,1); x 1 + O(2) @@ -889,12 +889,10 @@ cdef class CachedFunction(object): 1 + O(2^2) sage: x==y True - sage: f(x) - 1 + O(2) - sage: f(y) - 1 + O(2^2) - sage: f.cache - {(((..., ((1,),), 0, 2),), ()): 1 + O(2^2), (((..., ((1,),), 0, 1),), ()): 1 + O(2)} + sage: f(x) is f(x) + True + sage: f(y) is not f(x) + True """ # We shortcut a common case of no arguments @@ -914,7 +912,7 @@ cdef class CachedFunction(object): try: return (self.cache)[k] except TypeError: # k is not hashable - k = _cache_key(k) + k = (_cache_key,_cache_key(k)) return (self.cache)[k] except KeyError: w = self.f(*args, **kwds) @@ -979,7 +977,8 @@ cdef class CachedFunction(object): try: return k in (self.cache) except TypeError: # k is not hashable - return _cache_key(k) in (self.cache) + k = (_cache_key,_cache_key(k)) + return k in self.cache def set_cache(self, value, *args, **kwds): """ @@ -1030,7 +1029,11 @@ cdef class CachedFunction(object): try: (self.cache)[k] = value except TypeError: # k is not hashable - (self.cache)[_cache_key(k)] = value + k = (_cache_key, _cache_key(k)) + # to make sure that this key does not get confused with the key of + # a hashable object, such keys include _cache_key which is + # certainly not stored in the dictionary otherwise. + (self.cache)[k] = value def get_key(self, *args, **kwds): """ @@ -1231,7 +1234,7 @@ cdef class WeakCachedFunction(CachedFunction): sage: from sage.misc.cachefunc import weak_cached_function sage: @weak_cached_function - ....: def f(x): return x + ....: def f(x): return x+x sage: K. = Qq(4) sage: R. = K[] sage: x = t + K(1,1); x @@ -1240,12 +1243,10 @@ cdef class WeakCachedFunction(CachedFunction): (1 + O(2^20))*t + 1 + O(2^2) sage: x==y True - sage: f(x) - (1 + O(2^20))*t + 1 + O(2) - sage: f(y) - (1 + O(2^20))*t + 1 + O(2^2) - sage: list(f.cache.keys()) - [(((..., ((..., ((1,),), 0, 1), (..., ((1,),), 0, 20))),), ()), (((..., ((..., ((1,),), 0, 2), (..., ((1,),), 0, 20))),), ())] + sage: f(x) is f(x) + True + sage: f(y) is not f(x) + True """ # We shortcut a common case of no arguments @@ -1265,7 +1266,7 @@ cdef class WeakCachedFunction(CachedFunction): try: return self.cache[k] except TypeError: # k is not hashable - k = _cache_key(k) + k = (_cache_key,_cache_key(k)) return self.cache[k] except KeyError: w = self.f(*args, **kwds) @@ -1327,7 +1328,8 @@ cdef class WeakCachedFunction(CachedFunction): try: return k in self.cache except TypeError: # k is not hashable - return _cache_key(k) in self.cache + k = (_cache_key,_cache_key(k)) + return k in self.cache def set_cache(self, value, *args, **kwds): """ @@ -1371,7 +1373,11 @@ cdef class WeakCachedFunction(CachedFunction): try: self.cache[k] = value except TypeError: # k is not hashable - self.cache[_cache_key(k)] = value + k = (_cache_key,_cache_key(k)) + # to make sure that this key does not get confused with the key of + # a hashable object, such keys include _cache_key which is + # certainly not stored in the dictionary otherwise. + self.cache[k] = value weak_cached_function = decorator_keywords(WeakCachedFunction) @@ -1787,7 +1793,7 @@ cdef class CachedMethodCaller(CachedFunction): sage: K. = Qq(4) sage: class A(object): ....: @cached_method - ....: def f(self, x): return x + ....: def f(self, x): return x+x sage: a=A() sage: x = K(1,1); x 1 + O(2) @@ -1795,12 +1801,10 @@ cdef class CachedMethodCaller(CachedFunction): 1 + O(2^2) sage: x==y True - sage: a.f(x) - 1 + O(2) - sage: a.f(y) - 1 + O(2^2) - sage: a.f.cache - {(((..., ((1,),), 0, 2),), ()): 1 + O(2^2), (((..., ((1,),), 0, 1),), ()): 1 + O(2)} + sage: a.f(x) is a.f(x) + True + sage: a.f(y) is not a.f(x) + True """ if self._instance is None: @@ -1846,7 +1850,7 @@ cdef class CachedMethodCaller(CachedFunction): try: return cache[k] except TypeError: # k is not hashable - k = _cache_key(k) + k = (_cache_key,_cache_key(k)) return cache[k] except KeyError: w = self._cachedmethod._instance_call(self._instance, *args, **kwds) From 15665e1c4601a38398c077a4f6a159839112cb21 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Fri, 27 Jun 2014 20:03:05 +0200 Subject: [PATCH 413/546] trac #16535: mv W. cons. to orthogonal_arrays_recursive.py And let Wilson construction unchanged... --- .../combinat/designs/orthogonal_arrays.py | 178 +---------------- .../designs/orthogonal_arrays_recursive.py | 180 +++++++++++++++++- 2 files changed, 182 insertions(+), 176 deletions(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 2f0ac5c113b..a1f6f3744ee 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -27,6 +27,7 @@ from sage.rings.infinity import Infinity from designs_pyx import is_orthogonal_array + def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): r""" Return a transversal design of parameters `k,n`. @@ -299,15 +300,6 @@ def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): from sage.combinat.designs.database import TD_6_12 TD = [l[:k] for l in TD_6_12()] - elif may_be_available and TD_find_product_decomposition(k,n): - _OA_cache_set(k,n,True) - if existence: - return True - n1,n2 = TD_find_product_decomposition(k,n) - TD1 = transversal_design(k,n1, check = False) - TD2 = transversal_design(k,n2, check = False) - TD = TD_product(k,TD1,n1,TD2,n2, check = False) - # Section 6.6 of [Stinson2004] elif (orthogonal_array not in who_asked and orthogonal_array(k, n, existence=True, who_asked = who_asked + (transversal_design,)) is not Unknown): @@ -369,108 +361,6 @@ def is_transversal_design(B,k,n, verbose=False): """ return is_orthogonal_array([[x%n for x in R] for R in B],k,n,verbose=verbose) -@cached_function -def find_wilson_decomposition_with_one_truncated_group(k,n): - r""" - Finds a wilson decomposition of `k,n` with one truncated group. - - This method looks for possible integers `m,t,u` satisfying that `mt+u=n` and - such that Sage knows how to build a `TD(k,m), TD(k,m+1),TD(k+1,t)` and a - `TD(k,u)`. These can then be used to feed :func:`wilson_construction`. - - INPUT: - - - `k,n` (integers) - - OUTPUT: - - Returns a 4-tuple `(n, m, t, u)` if it is found, and ``False`` otherwise. - - EXAMPLES:: - - sage: from sage.combinat.designs.orthogonal_arrays import find_wilson_decomposition_with_one_truncated_group - sage: find_wilson_decomposition_with_one_truncated_group(4,38) - (4, 7, 5, 3) - sage: find_wilson_decomposition_with_one_truncated_group(4,20) - False - """ - # If there exists a TD(k+1,t) then k+1 < t+2, i.e. k <= t - for t in range(max(1,k),n-1): - u = n%t - # We ensure that 1<=u, and that there can exists a TD(k,u), i.e k1 and k >= u+2): - continue - - m = n//t - # If there exists a TD(k,m) then k= m+2: - break - - if (orthogonal_array(k ,m , existence=True) and - orthogonal_array(k ,m+1, existence=True) and - orthogonal_array(k+1,t , existence=True) and - orthogonal_array(k ,u , existence=True)): - return k,m,t,u - - return False - -@cached_function -def find_wilson_decomposition_with_two_truncated_groups(k,n): - r""" - Helper function for Wilson's construction with two trucated columns - - Find integers `r,m,r_1,r_2` satisfying `n=rm+r_1+r_2` and `1\leq r_1,r_2= k+1 - if not orthogonal_array(k+2,r,existence=True): - continue - m_min = (n - (2*r-2))//r - m_max = (n - 2)//r - if m_min > 1: - m_values = range(max(m_min,k-1), m_max+1) - else: - m_values = [1] + range(k-1, m_max+1) - for m in m_values: - r1_p_r2 = n-r*m # the sum of r1+r2 - # it is automatically >= 2 since m <= m_max - if (r1_p_r2 > 2*r-2 or - not orthogonal_array(k,m ,existence=True) or - not orthogonal_array(k,m+1,existence=True) or - not orthogonal_array(k,m+2,existence=True)): - continue - - r1_min = r1_p_r2 - (r-1) - r1_max = min(r-1, r1_p_r2) - if r1_min > 1: - r1_values = range(max(k-1,r1_min), r1_max+1) - else: - r1_values = [1] + range(k-1, r1_max+1) - for r1 in r1_values: - if not orthogonal_array(k,r1,existence=True): - continue - r2 = r1_p_r2-r1 - if orthogonal_array(k,r2,existence=True): - assert n == r*m+r1+r2 - return k,r,m,r1,r2 - return False - def wilson_construction(OA,k,r,m,n_trunc,u,check=True): r""" Returns a `OA(k,rm+u)` from a truncated `OA(k+s,r)` by Wilson's construction. @@ -512,16 +402,14 @@ def wilson_construction(OA,k,r,m,n_trunc,u,check=True): sage: from sage.combinat.designs.orthogonal_arrays import wilson_construction sage: from sage.combinat.designs.orthogonal_arrays import OA_relabel - sage: from sage.combinat.designs.orthogonal_arrays import find_wilson_decomposition_with_one_truncated_group + sage: from sage.combinat.designs.orthogonal_arrays_recursive import find_wilson_decomposition_with_one_truncated_group sage: total = 0 sage: for k in range(3,8): ....: for n in range(1,30): ....: if find_wilson_decomposition_with_one_truncated_group(k,n): ....: total += 1 - ....: k,m,r,u = find_wilson_decomposition_with_one_truncated_group(k,n) - ....: OA = designs.orthogonal_array(k+1,r,check=False) - ....: OA = OA_relabel(OA,k+1,r,matrix=[range(r)]*k+[range(u)+[None]*(r-u)]) - ....: _ = wilson_construction(OA,k,r,m,1,[u],check=True) + ....: f, args = find_wilson_decomposition_with_one_truncated_group(k,n) + ....: _ = f(*args) sage: print total 41 """ @@ -569,42 +457,6 @@ def wilson_construction(OA,k,r,m,n_trunc,u,check=True): return OA -@cached_function -def TD_find_product_decomposition(k,n): - r""" - Attempts to find a factorization of `n` in order to build a `TD(k,n)`. - - If Sage can build a `TD(k,n_1)` and a `TD(k,n_2)` such that `n=n_1\times - n_2` then a `TD(k,n)` can be built (from the function - :func:`transversal_design`). This method returns such a pair of integers if - it exists, and ``None`` otherwise. - - INPUT: - - - ``k,n`` (integers) -- see above. - - .. SEEALSO:: - - :func:`TD_product` that actually build a product - - EXAMPLES:: - - sage: from sage.combinat.designs.orthogonal_arrays import TD_find_product_decomposition - sage: TD_find_product_decomposition(6, 84) - (7, 12) - - sage: TD1 = designs.transversal_design(6, 7) - sage: TD2 = designs.transversal_design(6, 12) - sage: from sage.combinat.designs.orthogonal_arrays import TD_product - sage: TD = TD_product(6, TD1, 7, TD2, 12) - """ - from sage.rings.arith import divisors - for n1 in divisors(n)[1:-1]: # we ignore 1 and n - n2 = n//n1 - if transversal_design(k, n1, existence = True) and transversal_design(k, n2, existence = True): - return n1,n2 - return None - def TD_product(k,TD1,n1,TD2,n2, check=True): r""" Returns the product of two transversal designs. @@ -630,10 +482,6 @@ def TD_product(k,TD1,n1,TD2,n2, check=True): guys), you may want to disable it whenever you want speed. Set to ``True`` by default. - .. SEEALSO:: - - :func:`TD_find_product_decomposition` - .. NOTE:: This function uses transversal designs with @@ -1006,24 +854,6 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): OA = OA_from_wider_OA(construction(),k) - elif may_be_available and find_wilson_decomposition_with_one_truncated_group(k,n): - _OA_cache_set(k,n,True) - if existence: - return True - k,m,r,u = find_wilson_decomposition_with_one_truncated_group(k,n) - OA = orthogonal_array(k+1,r,check=False) - OA = OA_relabel(OA,k+1,r,matrix=[range(r)]*k+[range(u)+[None]*(r-u)]) - OA = wilson_construction(OA,k,r,m,1,[u],check=False) - - elif may_be_available and find_wilson_decomposition_with_two_truncated_groups(k,n): - _OA_cache_set(k,n,True) - if existence: - return True - k,r,m,u1,u2 = find_wilson_decomposition_with_two_truncated_groups(k,n) - OA = orthogonal_array(k+2,r,check=False) - OA = OA_relabel(OA,k+2,r,matrix=[range(r)]*k+[range(u1)+[None]*(r-u1),range(u2)+[None]*(r-u2)]) - OA = wilson_construction(OA,k,r,m,2,[u1,u2],check=False) - elif may_be_available and find_recursive_construction(k,n): _OA_cache_set(k,n,True) if existence: diff --git a/src/sage/combinat/designs/orthogonal_arrays_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_recursive.py index 544691c9bbd..74b2735ee6d 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_recursive.py @@ -34,6 +34,7 @@ def find_recursive_construction(k,n): This determines whether an `OA(k,n)` can be built through the following constructions: + - Wilson constructions with 0, 1 or 2 truncated groups - :func:`construction_3_3` - :func:`construction_3_4` - :func:`construction_3_5` @@ -62,10 +63,14 @@ def find_recursive_construction(k,n): ....: OA = f(*args) ....: assert is_orthogonal_array(OA,k,n,2,verbose=True) sage: print count - 40 + 56 """ assert k >= 3 - for find_c in [find_construction_3_3, + + for find_c in [find_product_decomposition, + find_wilson_decomposition_with_one_truncated_group, + find_wilson_decomposition_with_two_truncated_groups, + find_construction_3_3, find_construction_3_4, find_construction_3_5, find_construction_3_6, @@ -75,6 +80,177 @@ def find_recursive_construction(k,n): return res return False +def find_product_decomposition(k,n): + r""" + Attempts to find a factorization of `n` in order to build a `OA(k,n)`. + + If Sage can build a `OA(k,n_1)` and a `OA(k,n_2)` such that `n=n_1\times + n_2` then a `OA(k,n)` can be built (from the function + :func:`transversal_design`). This method returns such a pair of integers if + it exists, and ``False`` otherwise. + + INPUT: + + - ``k,n`` (integers) -- see above. + + EXAMPLES:: + + sage: from sage.combinat.designs.orthogonal_arrays_recursive import find_product_decomposition + sage: f,args = find_product_decomposition(6, 84) + sage: args + (6, 7, 12, ()) + sage: _ = f(*args) + """ + from sage.rings.arith import divisors + for n1 in divisors(n)[1:-1]: # we ignore 1 and n + n2 = n//n1 + if orthogonal_array(k, n1, existence=True) and orthogonal_array(k, n2, existence=True): + return simple_wilson_construction, (k,n1,n2,()) + return False + +def find_wilson_decomposition_with_one_truncated_group(k,n): + r""" + Finds a wilson decomposition of `k,n` with one truncated group. + + This method looks for possible integers `m,t,u` satisfying that `mt+u=n` and + such that Sage knows how to build a `TD(k,m), TD(k,m+1),TD(k+1,t)` and a + `TD(k,u)`. These can then be used to feed :func:`wilson_construction`. + + INPUT: + + - `k,n` (integers) + + OUTPUT: + + Returns a 4-tuple `(k, r, m, (u,))` if it is found, and ``False`` otherwise. + + EXAMPLES:: + + sage: from sage.combinat.designs.orthogonal_arrays_recursive import find_wilson_decomposition_with_one_truncated_group + sage: f,args = find_wilson_decomposition_with_one_truncated_group(4,38) + sage: args + (4, 5, 7, (3,)) + sage: _ = f(*args) + + sage: find_wilson_decomposition_with_one_truncated_group(4,20) + False + """ + # If there exists a TD(k+1,t) then k+1 < t+2, i.e. k <= t + for r in range(max(1,k),n-1): + u = n%r + # We ensure that 1<=u, and that there can exists a TD(k,u), i.e k1 and k >= u+2): + continue + + m = n//r + # If there exists a TD(k,m) then k= m+2: + break + + if (orthogonal_array(k ,m , existence=True) and + orthogonal_array(k ,m+1, existence=True) and + orthogonal_array(k+1,r , existence=True) and + orthogonal_array(k ,u , existence=True)): + return simple_wilson_construction, (k,r,m,(u,)) + + return False + +def find_wilson_decomposition_with_two_truncated_groups(k,n): + r""" + Helper function for Wilson's construction with two trucated columns + + Find integers `r,m,r_1,r_2` satisfying `n=rm+r_1+r_2` and `1\leq r_1,r_2= k+1 + if not orthogonal_array(k+2,r,existence=True): + continue + m_min = (n - (2*r-2))//r + m_max = (n - 2)//r + if m_min > 1: + m_values = range(max(m_min,k-1), m_max+1) + else: + m_values = [1] + range(k-1, m_max+1) + for m in m_values: + r1_p_r2 = n-r*m # the sum of r1+r2 + # it is automatically >= 2 since m <= m_max + if (r1_p_r2 > 2*r-2 or + not orthogonal_array(k,m ,existence=True) or + not orthogonal_array(k,m+1,existence=True) or + not orthogonal_array(k,m+2,existence=True)): + continue + + r1_min = r1_p_r2 - (r-1) + r1_max = min(r-1, r1_p_r2) + if r1_min > 1: + r1_values = range(max(k-1,r1_min), r1_max+1) + else: + r1_values = [1] + range(k-1, r1_max+1) + for r1 in r1_values: + if not orthogonal_array(k,r1,existence=True): + continue + r2 = r1_p_r2-r1 + if orthogonal_array(k,r2,existence=True): + assert n == r*m+r1+r2 + return simple_wilson_construction, (k,r,m,(r1,r2)) + return False + +def simple_wilson_construction(k,r,m,u): + r""" + Return the Wilson construction with parameters `(k,r,m,u)`. + + .. TODO:: + + As soon as wilson construction accepts an empty master design we should + remove this intermediate functions. + + EXAMPLES:: + + sage: from sage.combinat.designs.orthogonal_arrays_recursive import simple_wilson_construction + sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array + + sage: OA = simple_wilson_construction(6,7,12,()) + sage: is_orthogonal_array(OA,6,84) + True + + sage: OA = simple_wilson_construction(4,5,7,(3,)) + sage: is_orthogonal_array(OA,4,38) + True + + sage: OA = simple_wilson_construction(5,7,7,(4,5)) + sage: is_orthogonal_array(OA,5,58) + True + """ + from sage.combinat.designs.orthogonal_arrays import wilson_construction, OA_relabel + + n = r*m + sum(u) + n_trunc = len(u) + OA = orthogonal_array(k+n_trunc,r,check=False) + matrix = [range(r)]*k + for uu in u: + matrix.append(range(uu)+[None]*(r-uu)) + OA = OA_relabel(OA,k+n_trunc,r,matrix=matrix) + + return wilson_construction(OA,k,r,m,n_trunc,u,False) + def find_construction_3_3(k,n): r""" Finds a decomposition for construction 3.3 from [AC07]_ From 9c395493c2d66798c4e6d8253aa62e29628762cd Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Fri, 27 Jun 2014 21:30:24 +0200 Subject: [PATCH 414/546] trac #16535: remove who_asked --- src/sage/combinat/designs/latin_squares.py | 19 +--- .../combinat/designs/orthogonal_arrays.py | 92 +++++++------------ 2 files changed, 38 insertions(+), 73 deletions(-) diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 3f9cf59bb65..4a5d2fc4c47 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -157,7 +157,7 @@ def are_mutually_orthogonal_latin_squares(l, verbose=False): from designs_pyx import is_orthogonal_array return is_orthogonal_array(zip(*[[x for R in M for x in R] for M in l]),k,n, verbose=verbose, terminology="MOLS") -def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, existence=False, who_asked=tuple()): +def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, existence=False): r""" Returns `k` Mutually Orthogonal `n\times n` Latin Squares (MOLS). @@ -208,11 +208,6 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi guys), you may want to disable it whenever you want speed. Set to ``True`` by default. - - ``who_asked`` (internal use only) -- because of the equivalence between - OA/TD/MOLS, each of the three constructors calls the others. We must keep - track of who calls who in order to avoid infinite loops. ``who_asked`` is - the tuple of the other functions that were called before this one. - EXAMPLES:: sage: designs.mutually_orthogonal_latin_squares(5,4) @@ -327,7 +322,7 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi if existence: return k - if existence and not who_asked and _OA_cache_get(k+2,n) is not None: + if existence and _OA_cache_get(k+2,n) is not None: return _OA_cache_get(k+2,n) may_be_available = _OA_cache_construction_available(k+2,n) is not False @@ -350,11 +345,9 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi matrices = construction()[:k] - elif (orthogonal_array not in who_asked and - orthogonal_array(k+2,n,existence=True,who_asked = who_asked+(mutually_orthogonal_latin_squares,)) is not Unknown): - + elif orthogonal_array(k+2,n,existence=True) is not Unknown: # Forwarding non-existence results - if orthogonal_array(k+2,n,existence=True,who_asked = who_asked+(mutually_orthogonal_latin_squares,)): + if orthogonal_array(k+2,n,existence=True): if existence: return True else: @@ -362,7 +355,7 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi return False raise EmptySetError("There does not exist {} MOLS of order {}!".format(k,n)) - OA = orthogonal_array(k+2,n,check=False, who_asked = who_asked+(mutually_orthogonal_latin_squares,)) + OA = orthogonal_array(k+2,n,check=False) OA.sort() # make sure that the first two columns are "11, 12, ..., 1n, 21, 22, ..." # We first define matrices as lists of n^2 values @@ -376,8 +369,6 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi matrices = [Matrix(M) for M in matrices] else: - if not who_asked: - _OA_cache_set(k+2,n,Unknown) if existence: return Unknown raise NotImplementedError("I don't know how to build {} MOLS of order {}".format(k,n)) diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index a1f6f3744ee..d9b20008d5e 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -28,7 +28,7 @@ from designs_pyx import is_orthogonal_array -def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): +def transversal_design(k,n,check=True,existence=False): r""" Return a transversal design of parameters `k,n`. @@ -72,12 +72,6 @@ def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): When ``k=None`` and ``existence=True`` the function returns an integer, i.e. the largest `k` such that we can build a `TD(k,n)`. - - ``who_asked`` (internal use only) -- because of the equivalence between - OA/TD/MOLS, each of the three constructors calls the others. We must keep - track of who calls who in order to avoid infinite loops. ``who_asked`` is - the tuple of the other functions that were called before this one on the - same input `k,n`. - OUTPUT: The kind of output depends on the input: @@ -278,7 +272,7 @@ def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): if existence: return k - if existence and not who_asked and _OA_cache_get(k,n) is not None: + if existence and _OA_cache_get(k,n) is not None: return _OA_cache_get(k,n) may_be_available = _OA_cache_construction_available(k,n) is not False @@ -301,11 +295,10 @@ def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): TD = [l[:k] for l in TD_6_12()] # Section 6.6 of [Stinson2004] - elif (orthogonal_array not in who_asked and - orthogonal_array(k, n, existence=True, who_asked = who_asked + (transversal_design,)) is not Unknown): + elif orthogonal_array(k, n, existence=True) is not Unknown: # Forwarding non-existence results - if orthogonal_array(k, n, existence=True, who_asked = who_asked + (transversal_design,)): + if orthogonal_array(k, n, existence=True): if existence: return True else: @@ -313,12 +306,10 @@ def transversal_design(k,n,check=True,existence=False, who_asked=tuple()): return False raise EmptySetError("There exists no TD({},{})!".format(k,n)) - OA = orthogonal_array(k,n, check = False, who_asked = who_asked + (transversal_design,)) + OA = orthogonal_array(k,n, check = False) TD = [[i*n+c for i,c in enumerate(l)] for l in OA] else: - if not who_asked: - _OA_cache_set(k,n,Unknown) if existence: return Unknown raise NotImplementedError("I don't know how to build a TD({},{})!".format(k,n)) @@ -623,7 +614,7 @@ def _OA_cache_construction_available(k,n): else: return Unknown -def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): +def orthogonal_array(k,n,t=2,check=True,existence=False): r""" Return an orthogonal array of parameters `k,n,t`. @@ -666,12 +657,6 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): When ``k=None`` and ``existence=True`` the function returns an integer, i.e. the largest `k` such that we can build a `TD(k,n)`. - - ``who_asked`` (internal use only) -- because of the equivalence between - OA/TD/MOLS, each of the three constructors calls the others. We must keep - track of who calls who in order to avoid infinite loops. ``who_asked`` is - the tuple of the other functions that were called before this one on the - same input `k,n`. - OUTPUT: The kind of output depends on the input: @@ -774,7 +759,7 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): """ from latin_squares import mutually_orthogonal_latin_squares - from database import OA_constructions + from database import OA_constructions, MOLS_constructions from block_design import projective_plane, projective_plane_to_OA from orthogonal_arrays_recursive import find_recursive_construction @@ -799,7 +784,7 @@ def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()): if k < t: raise ValueError("undefined for k Date: Fri, 27 Jun 2014 21:38:05 +0200 Subject: [PATCH 415/546] trac #16535: avoid calling recursive construction in OA(3,n) --- src/sage/combinat/designs/orthogonal_arrays.py | 5 +++++ src/sage/combinat/designs/orthogonal_arrays_recursive.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index d9b20008d5e..376df670a1b 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -815,6 +815,11 @@ def orthogonal_array(k,n,t=2,check=True,existence=False): return Unknown raise NotImplementedError("Only trivial orthogonal arrays are implemented for t>=2") + elif k <= 3: + if existence: + return True + return [[i,j,(i+j)%n] for i in xrange(n) for j in xrange(n)] + # projective spaces are equivalent to OA(n+1,n,2) elif (projective_plane(n, existence=True) or (k == n+1 and projective_plane(n, existence=True) is False)): diff --git a/src/sage/combinat/designs/orthogonal_arrays_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_recursive.py index 74b2735ee6d..1a28cf2fd46 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_recursive.py @@ -65,7 +65,7 @@ def find_recursive_construction(k,n): sage: print count 56 """ - assert k >= 3 + assert k > 3 for find_c in [find_product_decomposition, find_wilson_decomposition_with_one_truncated_group, From 8bee645807c831bc23f8e44e4c0d921bd75fc4f4 Mon Sep 17 00:00:00 2001 From: Miguel Marco Date: Sat, 28 Jun 2014 04:41:44 +0200 Subject: [PATCH 416/546] Add tides as package. --- build/pkgs/tides/SPKG.txt | 26 ++++++++++++++++++++++++++ build/pkgs/tides/checksums.ini | 4 ++++ build/pkgs/tides/package-version.txt | 1 + build/pkgs/tides/spkg-check | 4 ++++ build/pkgs/tides/spkg-install | 21 +++++++++++++++++++++ 5 files changed, 56 insertions(+) create mode 100644 build/pkgs/tides/SPKG.txt create mode 100644 build/pkgs/tides/checksums.ini create mode 100644 build/pkgs/tides/package-version.txt create mode 100644 build/pkgs/tides/spkg-check create mode 100644 build/pkgs/tides/spkg-install diff --git a/build/pkgs/tides/SPKG.txt b/build/pkgs/tides/SPKG.txt new file mode 100644 index 00000000000..eab13f8e65b --- /dev/null +++ b/build/pkgs/tides/SPKG.txt @@ -0,0 +1,26 @@ += TIDES = + +== Description == + +TIDES is a library for integration of ODE's with high precission. + +== License == + +GPLv3 + +== SPKG Maintainers == + +* Miguel Marco + +== Upstream Contact == + +* Marcos Rodriguez (marcos@unizar.es) + +== Dependencies == + +* gcc +* mpfr +* gmp + +== Special Update/Build Instructions == + diff --git a/build/pkgs/tides/checksums.ini b/build/pkgs/tides/checksums.ini new file mode 100644 index 00000000000..f75423db3a7 --- /dev/null +++ b/build/pkgs/tides/checksums.ini @@ -0,0 +1,4 @@ +tarball=tides-VERSION.tar.gz +sha1=2a70d8e08c364abff3314b9f15022aba2b62c1e7 +md5=200db1104c40f0c4f7bce806c773c87f +cksum=2240108013 diff --git a/build/pkgs/tides/package-version.txt b/build/pkgs/tides/package-version.txt new file mode 100644 index 00000000000..415b19fc362 --- /dev/null +++ b/build/pkgs/tides/package-version.txt @@ -0,0 +1 @@ +2.0 \ No newline at end of file diff --git a/build/pkgs/tides/spkg-check b/build/pkgs/tides/spkg-check new file mode 100644 index 00000000000..381bd0b8818 --- /dev/null +++ b/build/pkgs/tides/spkg-check @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +cd src +$MAKE check \ No newline at end of file diff --git a/build/pkgs/tides/spkg-install b/build/pkgs/tides/spkg-install new file mode 100644 index 00000000000..a15104aa70e --- /dev/null +++ b/build/pkgs/tides/spkg-install @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +cd src + +./configure --prefix="$SAGE_LOCAL" --libdir="$SAGE_LOCAL/lib" +if [ $? -ne 0 ]; then + echo >&2 "Error configuring PACKAGE_NAME." + exit 1 +fi + +$MAKE +if [ $? -ne 0 ]; then + echo >&2 "Error building PACKAGE_NAME." + exit 1 +fi + +$MAKE -j1 install +if [ $? -ne 0 ]; then + echo >&2 "Error installing PACKAGE_NAME." + exit 1 +fi \ No newline at end of file From 1284345def07593e851b64ae3d805ca7dea9e7e4 Mon Sep 17 00:00:00 2001 From: Miguel Marco Date: Sat, 28 Jun 2014 04:45:30 +0200 Subject: [PATCH 417/546] Added newlines at the end of files --- build/pkgs/tides/package-version.txt | 2 +- build/pkgs/tides/spkg-check | 2 +- build/pkgs/tides/spkg-install | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/tides/package-version.txt b/build/pkgs/tides/package-version.txt index 415b19fc362..cd5ac039d67 100644 --- a/build/pkgs/tides/package-version.txt +++ b/build/pkgs/tides/package-version.txt @@ -1 +1 @@ -2.0 \ No newline at end of file +2.0 diff --git a/build/pkgs/tides/spkg-check b/build/pkgs/tides/spkg-check index 381bd0b8818..35f04fc8862 100644 --- a/build/pkgs/tides/spkg-check +++ b/build/pkgs/tides/spkg-check @@ -1,4 +1,4 @@ #!/usr/bin/env bash cd src -$MAKE check \ No newline at end of file +$MAKE check diff --git a/build/pkgs/tides/spkg-install b/build/pkgs/tides/spkg-install index a15104aa70e..82a5f6b2639 100644 --- a/build/pkgs/tides/spkg-install +++ b/build/pkgs/tides/spkg-install @@ -18,4 +18,4 @@ $MAKE -j1 install if [ $? -ne 0 ]; then echo >&2 "Error installing PACKAGE_NAME." exit 1 -fi \ No newline at end of file +fi From 25f8b757c35da948eaa3db5d60afacd250e1b57f Mon Sep 17 00:00:00 2001 From: Miguel Marco Date: Sat, 28 Jun 2014 05:02:41 +0200 Subject: [PATCH 418/546] Interface for tides. --- src/sage/interfaces/tides.py | 845 +++++++++++++++++++++++++++++++++++ 1 file changed, 845 insertions(+) create mode 100644 src/sage/interfaces/tides.py diff --git a/src/sage/interfaces/tides.py b/src/sage/interfaces/tides.py new file mode 100644 index 00000000000..9e65382fac2 --- /dev/null +++ b/src/sage/interfaces/tides.py @@ -0,0 +1,845 @@ +r""" +This module contains tools to write the .c files needed for TIDES [TI]_ . + +Tides is an integration engine based on the Taylor method. It is implemented +as a c library. The user must translate its IVP into a pair of .c files that +will then be compiled and linked against the TIDES library. The reulting binary +will produce the desored output. The tools in this module can be used to +automate the generation of these files from the symbolic expression of the +differential equation. + +########################################################################## +# Copyright (C) 2014 Miguel Marco , Marcos Rodriguez +# +# +# Distributed under the terms of the GNU General Public License (GPL): +# +# http://www.gnu.org/licenses/ +########################################################################## + +AUTHORS: + +- Miguel Marco (06-2014) - Implementation of tides solver + +- Marcos Rodriguez (06-2014) - Implementation of tides solver + +- Alberto Abad (06-2014) - tides solver + +- Roberto Barrio (06-2014) - tides solver + +REFERENCES: + +.. [ALG924] A. Abad, R. Barrio, F. Blesa, M. Rodriguez. Algorithm 924. *ACM +Transactions on Mathematical Software*, *39*(1), 1–28. + +.. [TI](http://www.unizar.es/acz/05Publicaciones/Monografias/MonografiasPublicadas/Monografia36/IndMonogr36.htm) +A. Abad, R. Barrio, F. Blesa, M. Rodriguez. +TIDES tutorial: Integrating ODEs by using the Taylor Series Method. +""" + + + +from sage.rings.real_mpfr import RealField +import shutil +import os +from sage.calculus.all import symbolic_expression +from sage.misc.flatten import flatten +from sage.ext.fast_callable import fast_callable +from sage.misc.lazy_import import lazy_import +lazy_import('sage.rings.semirings.non_negative_integer_semiring', 'NN') +from sage.misc.functional import N +from sage.functions.other import floor, sqrt +from sage.functions.trig import sin, cos, arcsin, arctan, arccos + + +def subexpressions_list(f, parameters=[]): + """ + Construct the lists with the intermediate steps on the evaluation of the + function. + + INPUT: + + - ``f`` -- a symbollic function of several components. + + - ``paramters`` -- a list of the parameters that appear in the function + this should be the symbollic constants that appear in f but are not + arguments. + + OTUPUT: + + - a list of the intermediate subexpressions that appear in the evaluation + of f. + + - a list with the operations used to construct each of the subexpressions. + each element of this list is a tuple, formed by a string describing the + operation made, and the operands. + + For the trigonometric functions, some extra expressions will be added. + These extra expressions will be used later to compute their derivatives. + + + EXAMPLES:: + + sage: from sage.interfaces.tides import subexpressions_list + sage: var('x,y') + (x, y) + sage: f(x,y) = [x^2+y, cos(x)/log(y)] + sage: subexpressions_list(f) + ([x^2, x^2 + y, sin(x), cos(x), log(y), cos(x)/log(y)], + [('mul', x, x), + ('add', y, x^2), + ('sin', x), + ('cos', x), + ('log', y), + ('div', log(y), cos(x))]) + + :: + + sage: f(a)=[cos(a), arctan(a)] + sage: from sage.interfaces.tides import subexpressions_list + sage: subexpressions_list(f) + ([sin(a), cos(a), a^2, a^2 + 1, arctan(a)], + [('sin', a), ('cos', a), ('mul', a, a), ('add', 1, a^2), ('atan', a)]) + + + """ + variables = f[0].arguments() + varpar = list(parameters) + list(variables) + F = symbolic_expression([i(*variables) for i in f]).function(*varpar) + lis = flatten([fast_callable(i,vars=varpar).op_list() for i in F], max_level=1) + deflist = [] + stack = [] + const =[] + stackcomp=[] + detail=[] + for i in lis: + if i[0] == 'load_arg': + stack.append(varpar[i[1]]) + elif i[0] == 'ipow': + if i[1] in NN: + basis = stack[-1] + for j in range(i[1]-1): + a=stack.pop(-1) + detail.append(('mul', a, basis)) + stack.append(a*basis) + stackcomp.append(stack[-1]) + else: + detail.append(('pow',stack[-1],i[1])) + stack[-1]=stack[-1]**i[1] + stackcomp.append(stack[-1]) + + elif i[0] == 'load_const': + const.append(i[1]) + stack.append(i[1]) + elif i == 'mul': + a=stack.pop(-1) + b=stack.pop(-1) + detail.append(('mul', a, b)) + stack.append(a*b) + stackcomp.append(stack[-1]) + + elif i == 'div': + a=stack.pop(-1) + b=stack.pop(-1) + detail.append(('div', a, b)) + stack.append(b/a) + stackcomp.append(stack[-1]) + + elif i == 'add': + a=stack.pop(-1) + b=stack.pop(-1) + detail.append(('add',a,b)) + stack.append(a+b) + stackcomp.append(stack[-1]) + + elif i == 'pow': + a=stack.pop(-1) + b=stack.pop(-1) + detail.append(('pow', b, a)) + stack.append(b**a) + stackcomp.append(stack[-1]) + + elif i[0] == 'py_call' and str(i[1])=='log': + a=stack.pop(-1) + detail.append(('log', a)) + stack.append(log(a)) + stackcomp.append(stack[-1]) + + elif i[0] == 'py_call' and str(i[1])=='exp': + a=stack.pop(-1) + detail.append(('exp', a)) + stack.append(exp(a)) + stackcomp.append(stack[-1]) + + elif i[0] == 'py_call' and str(i[1])=='sin': + a=stack.pop(-1) + detail.append(('sin', a)) + detail.append(('cos', a)) + stackcomp.append(sin(a)) + stackcomp.append(cos(a)) + stack.append(sin(a)) + + elif i[0] == 'py_call' and str(i[1])=='cos': + a=stack.pop(-1) + detail.append(('sin', a)) + detail.append(('cos', a)) + stackcomp.append(sin(a)) + stackcomp.append(cos(a)) + stack.append(cos(a)) + + elif i[0] == 'py_call' and str(i[1])=='tan': + a=stack.pop(-1) + b = sin(a) + c = cos(a) + detail.append(('sin', a)) + detail.append(('cos', a)) + detail.append(('div', b, c)) + stackcomp.append(b) + stackcomp.append(c) + stackcomp.append(b/c) + stack.append(b/c) + + elif i[0] == 'py_call' and str(i[1])=='arctan': + a=stack.pop(-1) + detail.append(('mul', a, a)) + detail.append(('add', 1, a*a)) + detail.append(('atan', a)) + stackcomp.append(a*a) + stackcomp.append(1+a*a) + stackcomp.append(arctan(a)) + stack.append(arctan(a)) + + elif i[0] == 'py_call' and str(i[1])=='arcsin': + a=stack.pop(-1) + detail.append(('mul', a, a)) + detail.append(('mul', -1, a*a)) + detail.append(('add', 1, -a*a)) + detail.append(('pow', 1- a*a, 0.5)) + detail.append(('asin', a)) + stackcomp.append(a*a) + stackcomp.append(-a*a) + stackcomp.append(1-a*a) + stackcomp.append(sqrt(1-a*a)) + stackcomp.append(arcsin(a)) + stack.append(arcsin(a)) + + elif i[0] == 'py_call' and str(i[1])=='arccos': + a=stack.pop(-1) + detail.append(('mul', a, a)) + detail.append(('mul', -1, a*a)) + detail.append(('add', 1, -a*a)) + detail.append(('pow', 1- a*a, 0.5)) + detail.append(('mul', -1, sqrt(1-a*a))) + detail.append(('acos', a)) + stackcomp.append(a*a) + stackcomp.append(-a*a) + stackcomp.append(1-a*a) + stackcomp.append(sqrt(1-a*a)) + stackcomp.append(-sqrt(1-a*a)) + stackcomp.append(arccos(a)) + stack.append(arccos(a)) + + elif i[0] == 'py_call' and 'sqrt' in str(i[1]): + a=stack.pop(-1) + detail.append(('pow', a, 0.5)) + stackcomp.append(sqrt(a)) + stack.append(sqrt(a)) + + + elif i == 'neg': + a = stack.pop(-1) + detail.append(('mul', -1, a)) + stack.append(-a) + stackcomp.append(-a) + + return stackcomp,detail + + + +def remove_repeated(l1, l2): + """ + Given two lists, remove the repeated elements in l1, and the elements + in l2 that are on the same position. + positions. + + EXAMPLES:: + + from sage.interfaces.tides import subexpressions_list, remove_repeated + sage: f(a)=[1 + a^2, arcsin(a)] + sage: l1, l2 = subexpressions_list(f) + sage: l1, l2 + ([a^2, a^2 + 1, a^2, -a^2, -a^2 + 1, sqrt(-a^2 + 1), arcsin(a)], + [('mul', a, a), + ('add', 1, a^2), + ('mul', a, a), + ('mul', -1, a^2), + ('add', 1, -a^2), + ('pow', -a^2 + 1, 0.5), + ('asin', a)]) + sage: remove_repeated(l1, l2) + sage: l1, l2 + ([a^2, a^2 + 1, -a^2, -a^2 + 1, sqrt(-a^2 + 1), arcsin(a)], + [('mul', a, a), + ('add', 1, a^2), + ('mul', -1, a^2), + ('add', 1, -a^2), + ('pow', -a^2 + 1, 0.5), + ('asin', a)]) + + + """ + for i in range(len(l1)-1): + j=i+1 + while j2, l[28:]) + ['#include "minc_tides.h"\n', + 'void mincseries(double t,double *v, double *p, double **XVAR,int ORDER, int MO)\n', + '\tint VAR,PAR,TT,i,j, inext;\n', + '\tVAR = 5;\n', + '\tPAR = 0;\n', + '\tTT = 12;\n', + '\tdouble XX[TT+1][MO+1];\n', + '\tfor(j=0; j<=TT; j++)\n', + '\t\tfor(i=0; i<=ORDER; i++)\n', + '\t\t\tXX[j][i] = 0.e0;\n', + '\tXX[0][0] = t;\n', + '\tXX[0][1] = 1.e0;\n', + '\tfor(i=1;i<=VAR;i++) {\n', + '\t\tXX[i][0] = v[i-1];\n', + '\t}\n', + '\tfor(i=0;i2, l[28:]) + ['#include "minc_tides.h"\n', + 'int main() {\n', + ' int i, VARS, PARS; \n', + '\tVARS = 4 ;\n', + '\tPARS = 1;\n', + '\tdouble tolrel, tolabs, tini, tend, dt; \n', + '\tdouble v[VARS], p[PARS]; \n', + '\tv[0] = 1 ; \n', + '\tv[1] = 0 ; \n', + '\tv[2] = 0 ; \n', + '\tv[3] = 0.200000000000000 ; \n', + '\ttini = 0 ;\n', + '\ttend = 10 ;\n', + '\tdt = 0.100000000000000 ;\n', + '\ttolrel = 1e-16 ;\n', + '\ttolabs = 1e-16 ;\n', + '\textern char ofname[500];\tstrcpy(ofname, "out");\n', + '\tminc_tides(v,VARS,p,PARS,tini,tend,dt,tolrel,tolabs);\n', + '\treturn 0; \n'] + sage: shutil.rmtree(tempdir) + + + + """ + from sage.misc.misc import SAGE_ROOT + RR = RealField() + + l1, l2 = subexpressions_list(f) + + remove_repeated(l1, l2) + remove_constants(l1, l2) + #generate the corresponding c lines + + l3=[] + var = f[0].arguments() + for i in l2: + oper = i[0] + if oper in ["log", "exp", "sin", "cos"]: + a = i[1] + if a in var: + l3.append((oper, 'XX[{}]'.format(var.index(a)))) + elif a in l1: + l3.append((oper, 'XX[{}]'.format(l1.index(a)+len(var)))) + + else: + a=i[1] + b=i[2] + consta=False + constb=False + + if a in var: + aa = 'XX[{}]'.format(var.index(a)) + elif a in l1: + aa = 'XX[{}]'.format(l1.index(a)+len(var)) + else: + consta=True + aa = str(a) + if b in var: + bb = 'XX[{}]'.format(var.index(b)) + elif b in l1: + bb = 'XX[{}]'.format(l1.index(b)+len(var)) + else: + constb=True + bb = str(b) + if consta: + oper += '_c' + if not oper=='div': + bb, aa = aa, bb + elif constb: + oper += '_c' + l3.append((oper, aa, bb)) + + + + n = len(var) + res = [] + for i in range(len(l3)): + el = l3[i] + string = "XX[{}][i] = ".format(i + n) + if el[0] == 'add': + string += el[1] + "[i] + " + el[2] +"[i];" + elif el[0] == 'add_c': + string += "(i==0)? {}+".format(N(el[2])) + el[1] + "[0] : "+ el[1]+ "[i];" + elif el[0] == 'mul': + string += "mul_mc("+el[1]+","+el[2]+",i);" + elif el[0] == 'mul_c': + string += str(N(el[2])) + "*"+ el[1] + "[i];" + elif el[0] == 'pow_c': + string += "pow_mc_c("+el[1]+","+str(N(el[2]))+",XX[{}], i);".format(i+n) + elif el[0] == 'div': + string += "div_mc("+el[2]+","+el[1]+",XX[{}], i);".format(i+n) + elif el[0] == 'div_c': + string += "inv_mc("+str(N(el[2]))+","+el[1]+",XX[{}], i);".format(i+n) + elif el[0] == 'log': + string += "log_mc("+el[1]+",XX[{}], i);".format(i+n) + elif el[0] == 'exp': + string += "exp_mc("+el[1]+",XX[{}], i);".format(i+n) + elif el[0] == 'sin': + string += "sin_mc("+el[1]+",XX[{}], i);".format(i+n+1) + elif el[0] == 'cos': + string += "cos_mc("+el[1]+",XX[{}], i);".format(i+n-1) + + + res.append(string) + + l1 = list(var)+l1 + indices = [l1.index(i(*var))+n for i in f] + for i in range (1, n): + res.append("XX[{}][i+1] = XX[{}][i] / (i+1.0);".format(i,indices[i-1]-n)) + + + code = res + + + shutil.copy(SAGE_ROOT+'/src/sage/calculus/tides/seriesFile00.txt', integrator) + outfile = open(integrator, 'a') + outfile.write("\tVAR = {};\n".format(n)) + outfile.write("\tPAR = {};\n".format(0)) + outfile.write("\tTT = {};\n".format(len(res))) + infile = open(SAGE_ROOT+'/src/sage/calculus/tides/seriesFile01.txt') + for i in infile: + outfile.write(i) + infile.close() + outfile.writelines(["\t\t"+i+"\n" for i in code]) + + infile = open(SAGE_ROOT+'/src/sage/calculus/tides/seriesFile02.txt') + for i in infile: + outfile.write(i) + outfile.close() + + + shutil.copy(SAGE_ROOT+'/src/sage/calculus/tides/driverFile00.txt', driver) + outfile = open(driver, 'a') + outfile.write('\n\tVARS = {} ;\n'.format(n-1)) + outfile.write('\tPARS = 1;\n') + outfile.write('\tdouble tolrel, tolabs, tini, tend, dt; \n') + outfile.write('\tdouble v[VARS], p[PARS]; \n') + for i in range(len(ics)): + outfile.write('\tv[{}] = {} ; \n'.format(i, ics[i])) + outfile.write('\ttini = {} ;\n'.format(initial)) + outfile.write('\ttend = {} ;\n'.format(final)) + outfile.write('\tdt = {} ;\n'.format(delta)) + outfile.write('\ttolrel = {} ;\n'.format(tolrel)) + outfile.write('\ttolabs = {} ;\n'.format(tolabs)) + outfile.write('\textern char ofname[500];') + outfile.write('\tstrcpy(ofname, "'+ output +'");\n') + outfile.write('\tminc_tides(v,VARS,p,PARS,tini,tend,dt,tolrel,tolabs);\n') + outfile.write('\treturn 0; \n }') + outfile.close() + +def genfiles_mpfr(integrator, driver, f, ics, initial, final, delta, + parameters=[], parameter_values =[], dig = 20, tolrel=1e-16, + tolabs=1e-16, output = ''): + r""" + Generate the needed files for the mpfr module of the tides library. + + INPUT: + + - ``integrator`` -- the name of the integrator file. + + - ``driver`` -- the name of the driver file. + + - ``f`` -- the function that determines the differential equation. + + - ``ics`` -- a list or tuple with the initial conditions. + + - ``initial`` -- the initial time for the integration. + + - ``final`` -- the final time for the integration. + + - ``delta`` -- the step of the output. + + - ``parameters`` -- the variables inside the function that should be treated + as parameters. + + - ``parameter_values`` -- the values of the parameters for the particular IVP + + - ``dig`` -- the number of digits of precission that will be used in the integration + + - ``tolrel`` -- the relative tolerance. + + - ``tolabs`` -- the absolute tolerance. + + - ``output`` -- the name of the file that the compiled integrator will write to + + This function creates two files, integrator and driver, that can be used + later with the tides library ([TI]_). + + + TESTS:: + + sage: from tempfile import mkdtemp + sage: from sage.interfaces.tides import genfiles_mpfr + sage: import os + sage: import shutil + sage: var('t,x,y,X,Y') + (t, x, y, X, Y) + sage: f(t,x,y,X,Y)=[X, Y, -x/(x^2+y^2)^(3/2), -y/(x^2+y^2)^(3/2)] + sage: intfile = tempdir + '/integrator.c' + sage: drfile = tempdir + '/driver.c' + sage: shutil.rmtree(tempdir) + sage: tempdir = mkdtemp() + sage: drfile = tempdir + '/driver.c' + sage: intfile = tempdir + '/integrator.c' + sage: genfiles_mpfr(intfile, drfile, f, [1,0, 0, 0.2], 0, 10, 0.1, output = 'out', dig = 50) + sage: fileint = open(intfile) + sage: l = fileint.readlines() + sage: fileint.close() + sage: filter(lambda a: len(a)>2, l[28:]) + ['#include "mp_tides.h"\n', + 'long function_iteration(iteration_data *itd, mpfr_t t, mpfr_t v[], mpfr_t p[], int ORDER, mpfr_t *cvfd)\n', + '\tint i;\n', + ' int NCONST = 0;\n', + ' mpfr_t ct[0];\n', + '\tstatic int VARIABLES = 4;\n', + '\tstatic int PARAMETERS = 0;\n', + '\tstatic int LINKS = 9;\n', + '\tstatic int FUNCTIONS = 0;\n', + '\tstatic int POS_FUNCTIONS[1] = {0};\n', + '\tinitialize_mp_case();\n', + '\tfor(i=0; i<=ORDER; i++) {\n', + '\t\tmpfrts_var_t(itd, var[3], var[1], i);\n', + '\t\tmpfrts_var_t(itd, var[4], var[2], i);\n', + '\t\tmpfrts_var_t(itd, link[5], var[3], i);\n', + '\t\tmpfrts_var_t(itd, link[7], var[4], i);\n', + '\t\tmpfrts_mul_t(itd, var[1], var[1], link[0], i);\n', + '\t\tmpfrts_mul_t(itd, var[2], var[2], link[1], i);\n', + '\t\tmpfrts_add_t(itd, link[1], link[0], link[2], i);\n', + '\t\tmpfrts_pow_t_c(itd, link[2], "-1.5000000000000000000000000000000000000000000000000", link[3], i);\n', + '\t\tmpfrts_mul_t(itd, var[1], link[3], link[4], i);\n', + '\t\tmpfrts_mul_t_c(itd, "-1.0000000000000000000000000000000000000000000000000", link[4], link[5], i);\n', + '\t\tmpfrts_mul_t(itd, var[2], link[3], link[6], i);\n', + '\t\tmpfrts_mul_t_c(itd, "-1.0000000000000000000000000000000000000000000000000", link[6], link[7], i);\n', + '\t}\n', + '\twrite_mp_solution();\n', + '\tclear_vpl();\n', + '\tclear_cts();\n', + sage: filedr = open(drfile) + sage: l = filedr.readlines() + sage: filedr.close() + sage: filter(lambda a: len(a)>2, l[28:]) + [' #include "mpfr.h"\n', + ' #include "mp_tides.h"\n', + ' long function_iteration(iteration_data *itd, mpfr_t t, mpfr_t v[], mpfr_t p[], int ORDER, mpfr_t *cvfd);\n', + ' int main() {\n', + ' int i;\n', + '\tint nfun = 0;\n', + '\tset_precision_digits(50);\n', + '\tint npar = 0;\n', + '\tmpfr_t p[npar];\n', + '\tfor(i=0; i Date: Sat, 28 Jun 2014 09:00:04 +0200 Subject: [PATCH 419/546] Solved some issues in the doctests --- src/sage/interfaces/tides.py | 273 ++++++++++++++++++++++++----------- 1 file changed, 190 insertions(+), 83 deletions(-) diff --git a/src/sage/interfaces/tides.py b/src/sage/interfaces/tides.py index 9e65382fac2..77773ac14dd 100644 --- a/src/sage/interfaces/tides.py +++ b/src/sage/interfaces/tides.py @@ -48,6 +48,7 @@ from sage.misc.lazy_import import lazy_import lazy_import('sage.rings.semirings.non_negative_integer_semiring', 'NN') from sage.misc.functional import N +from sage.functions.log import log from sage.functions.other import floor, sqrt from sage.functions.trig import sin, cos, arcsin, arctan, arccos @@ -264,7 +265,7 @@ def remove_repeated(l1, l2): EXAMPLES:: - from sage.interfaces.tides import subexpressions_list, remove_repeated + sage: from sage.interfaces.tides import (subexpressions_list, remove_repeated) sage: f(a)=[1 + a^2, arcsin(a)] sage: l1, l2 = subexpressions_list(f) sage: l1, l2 @@ -365,12 +366,9 @@ def genfiles_mintides(integrator, driver, f, ics, initial, final, delta, sage: var('t,x,y,X,Y') (t, x, y, X, Y) sage: f(t,x,y,X,Y)=[X, Y, -x/(x^2+y^2)^(3/2), -y/(x^2+y^2)^(3/2)] - sage: intfile = tempdir + '/integrator.c' - sage: drfile = tempdir + '/driver.c' - sage: shutil.rmtree(tempdir) sage: tempdir = mkdtemp() - sage: drfile = tempdir + '/driver.c' sage: intfile = tempdir + '/integrator.c' + sage: drfile = tempdir + '/driver.c' sage: genfiles_mintides(intfile, drfile, f, [1,0, 0, 0.2], 0, 10, 0.1, output = 'out') sage: fileint = open(intfile) sage: l = fileint.readlines() @@ -526,25 +524,110 @@ def genfiles_mintides(integrator, driver, f, ics, initial, final, delta, code = res - shutil.copy(SAGE_ROOT+'/src/sage/calculus/tides/seriesFile00.txt', integrator) outfile = open(integrator, 'a') + outfile.write('/****************************************************************************\n') + outfile.write('\tThis file has been created by SageTIDES (1.00)\n') + outfile.write('\n') + outfile.write('\tCopyright (C) 2010 A. Abad, R. Barrio, F. Blesa, M. Marco, M. Rodriguez\n') + outfile.write('\tGrupo de Mecanica Espacial\n') + outfile.write('\tUniversity of Zaragoza\n') + outfile.write('\tSPAIN\n') + outfile.write('\n') + outfile.write('\thttp://gme.unizar.es/software/tides\n') + outfile.write('\tContact: \n') + outfile.write('\n') + outfile.write('\tThis file is part of TIDES.\n') + outfile.write('\n') + outfile.write('\tTIDES is free software: you can redistribute it and/or modify\n') + outfile.write('\tit under the terms of the GNU General Public License as published by\n') + outfile.write('\tthe Free Software Foundation, either version 3 of the License, or\n') + outfile.write('\t(at your option) any later version.\n') + outfile.write('\n') + outfile.write('\tTIDES is distributed in the hope that it will be useful,\n') + outfile.write('\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n') + outfile.write('\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n') + outfile.write('\tGNU General Public License for more details.\n') + outfile.write('\n') + outfile.write('\tYou should have received a copy of the GNU General Public License\n') + outfile.write('\talong with TIDES. If not, see .\n') + outfile.write('\n') + outfile.write('*****************************************************************************/\n') + outfile.write('\n') + outfile.write('\n') + outfile.write('\n') + outfile.write('#include "minc_tides.h"\n') + outfile.write('\n') + outfile.write('void mincseries(double t,double *v, double *p, double **XVAR,int ORDER, int MO)\n') + outfile.write('{\n') + outfile.write('\tint VAR,PAR,TT,i,j, inext;\n') + outfile.write('\n') outfile.write("\tVAR = {};\n".format(n)) outfile.write("\tPAR = {};\n".format(0)) outfile.write("\tTT = {};\n".format(len(res))) - infile = open(SAGE_ROOT+'/src/sage/calculus/tides/seriesFile01.txt') - for i in infile: - outfile.write(i) - infile.close() + + outfile.write('\tdouble XX[TT+1][MO+1];\n') + outfile.write('\n') + outfile.write('\tfor(j=0; j<=TT; j++)\n') + outfile.write('\t\tfor(i=0; i<=ORDER; i++)\n') + outfile.write('\t\t\tXX[j][i] = 0.e0;\n') + outfile.write('\tXX[0][0] = t;\n') + outfile.write('\tXX[0][1] = 1.e0;\n') + outfile.write('\tfor(i=1;i<=VAR;i++) {\n') + outfile.write('\t\tXX[i][0] = v[i-1];\n') + outfile.write('\t}\n') + outfile.write('\n') + outfile.write('\tfor(i=0;i\n') + outfile.write('\n') + outfile.write(' This file is part of TIDES.\n') + outfile.write('\n') + outfile.write(' TIDES is free software: you can redistribute it and/or modify\n') + outfile.write(' it under the terms of the GNU General Public License as published by\n') + outfile.write(' the Free Software Foundation, either version 3 of the License, or\n') + outfile.write(' (at your option) any later version.\n') + outfile.write('\n') + outfile.write(' TIDES is distributed in the hope that it will be useful,\n') + outfile.write(' but WITHOUT ANY WARRANTY; without even the implied warranty of\n') + outfile.write(' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n') + outfile.write(' GNU General Public License for more details.\n') + outfile.write('\n') + outfile.write(' You should have received a copy of the GNU General Public License\n') + outfile.write(' along with TIDES. If not, see .\n') + outfile.write('\n') + outfile.write('*****************************************************************************/\n') + outfile.write('\n') + outfile.write('#include "minc_tides.h"\n') + outfile.write('\n') + outfile.write('int main() {\n') + outfile.write('\n') + outfile.write(' int i, VARS, PARS; ') + + outfile.write('\n\tVARS = {} ;\n'.format(n-1)) outfile.write('\tPARS = 1;\n') outfile.write('\tdouble tolrel, tolabs, tini, tend, dt; \n') @@ -610,9 +693,6 @@ def genfiles_mpfr(integrator, driver, f, ics, initial, final, delta, sage: var('t,x,y,X,Y') (t, x, y, X, Y) sage: f(t,x,y,X,Y)=[X, Y, -x/(x^2+y^2)^(3/2), -y/(x^2+y^2)^(3/2)] - sage: intfile = tempdir + '/integrator.c' - sage: drfile = tempdir + '/driver.c' - sage: shutil.rmtree(tempdir) sage: tempdir = mkdtemp() sage: drfile = tempdir + '/driver.c' sage: intfile = tempdir + '/integrator.c' @@ -620,72 +700,15 @@ def genfiles_mpfr(integrator, driver, f, ics, initial, final, delta, sage: fileint = open(intfile) sage: l = fileint.readlines() sage: fileint.close() - sage: filter(lambda a: len(a)>2, l[28:]) - ['#include "mp_tides.h"\n', - 'long function_iteration(iteration_data *itd, mpfr_t t, mpfr_t v[], mpfr_t p[], int ORDER, mpfr_t *cvfd)\n', - '\tint i;\n', - ' int NCONST = 0;\n', - ' mpfr_t ct[0];\n', - '\tstatic int VARIABLES = 4;\n', - '\tstatic int PARAMETERS = 0;\n', - '\tstatic int LINKS = 9;\n', - '\tstatic int FUNCTIONS = 0;\n', - '\tstatic int POS_FUNCTIONS[1] = {0};\n', - '\tinitialize_mp_case();\n', - '\tfor(i=0; i<=ORDER; i++) {\n', - '\t\tmpfrts_var_t(itd, var[3], var[1], i);\n', - '\t\tmpfrts_var_t(itd, var[4], var[2], i);\n', - '\t\tmpfrts_var_t(itd, link[5], var[3], i);\n', - '\t\tmpfrts_var_t(itd, link[7], var[4], i);\n', - '\t\tmpfrts_mul_t(itd, var[1], var[1], link[0], i);\n', - '\t\tmpfrts_mul_t(itd, var[2], var[2], link[1], i);\n', - '\t\tmpfrts_add_t(itd, link[1], link[0], link[2], i);\n', - '\t\tmpfrts_pow_t_c(itd, link[2], "-1.5000000000000000000000000000000000000000000000000", link[3], i);\n', - '\t\tmpfrts_mul_t(itd, var[1], link[3], link[4], i);\n', - '\t\tmpfrts_mul_t_c(itd, "-1.0000000000000000000000000000000000000000000000000", link[4], link[5], i);\n', - '\t\tmpfrts_mul_t(itd, var[2], link[3], link[6], i);\n', - '\t\tmpfrts_mul_t_c(itd, "-1.0000000000000000000000000000000000000000000000000", link[6], link[7], i);\n', - '\t}\n', - '\twrite_mp_solution();\n', - '\tclear_vpl();\n', - '\tclear_cts();\n', + sage: l[56] + '\t\tmpfrts_add_t(itd, link[1], link[0], link[2], i);\n' sage: filedr = open(drfile) sage: l = filedr.readlines() sage: filedr.close() - sage: filter(lambda a: len(a)>2, l[28:]) - [' #include "mpfr.h"\n', - ' #include "mp_tides.h"\n', - ' long function_iteration(iteration_data *itd, mpfr_t t, mpfr_t v[], mpfr_t p[], int ORDER, mpfr_t *cvfd);\n', - ' int main() {\n', - ' int i;\n', - '\tint nfun = 0;\n', - '\tset_precision_digits(50);\n', - '\tint npar = 0;\n', - '\tmpfr_t p[npar];\n', - '\tfor(i=0; i\n') + outfile.write('\n') + outfile.write('\tThis file is part of TIDES.\n') + outfile.write('\n') + outfile.write('\tTIDES is free software: you can redistribute it and/or modify\n') + outfile.write('\tit under the terms of the GNU General Public License as published by\n') + outfile.write('\tthe Free Software Foundation, either version 3 of the License, or\n') + outfile.write('\t(at your option) any later version.\n') + outfile.write('\n') + outfile.write('\tTIDES is distributed in the hope that it will be useful,\n') + outfile.write('\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n') + outfile.write('\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n') + outfile.write('\tGNU General Public License for more details.\n') + outfile.write('\n') + outfile.write('\tYou should have received a copy of the GNU General Public License\n') + outfile.write('\talong with TIDES. If not, see .\n') + outfile.write('\n') + outfile.write('*****************************************************************************/\n') + outfile.write('\n') + outfile.write('#include "mp_tides.h"\n') + outfile.write('\n') + outfile.write('\n') + outfile.write('\n') + outfile.write('long function_iteration(iteration_data *itd, mpfr_t t, mpfr_t v[], mpfr_t p[], int ORDER, mpfr_t *cvfd)\n') + outfile.write('{\n') + outfile.write('\n') + outfile.write('\tint i;\n') + outfile.write(' int NCONST = 0;\n') + outfile.write(' mpfr_t ct[0];\n') + outfile.write('\n') + outfile.write('\n') + + outfile.write("\n\tstatic int VARIABLES = {};\n".format(VAR)) outfile.write("\tstatic int PARAMETERS = {};\n".format(PAR)) outfile.write("\tstatic int LINKS = {};\n".format(TT)) @@ -810,9 +877,49 @@ def genfiles_mpfr(integrator, driver, f, ics, initial, final, delta, outfile.write('\treturn NUM_COLUMNS;\n}') outfile.close() - shutil.copy(SAGE_ROOT+'/src/sage/calculus/tides/driverFileMP00.txt', driver) + npar = len(parameter_values) outfile = open(driver, 'a') + + outfile.write('/****************************************************************************\n') + outfile.write('Driver file of the mp_tides program\n') + outfile.write('This file has been created by SageTIDES (1.0)\n') + outfile.write('\n') + outfile.write(' Copyright (C) 2010 A. Abad, R. Barrio, F. Blesa, M. Rodriguez\n') + outfile.write(' Grupo de Mecanica Espacial\n') + outfile.write(' University of Zaragoza\n') + outfile.write(' SPAIN\n') + outfile.write('\n') + outfile.write(' http://gme.unizar.es/software/tides\n') + outfile.write(' Contact: \n') + outfile.write('\n') + outfile.write(' This file is part of TIDES.\n') + outfile.write('\n') + outfile.write(' TIDES is free software: you can redistribute it and/or modify\n') + outfile.write(' it under the terms of the GNU General Public License as published by\n') + outfile.write(' the Free Software Foundation, either version 3 of the License, or\n') + outfile.write(' (at your option) any later version.\n') + outfile.write('\n') + outfile.write(' TIDES is distributed in the hope that it will be useful,\n') + outfile.write(' but WITHOUT ANY WARRANTY; without even the implied warranty of\n') + outfile.write(' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n') + outfile.write(' GNU General Public License for more details.\n') + outfile.write('\n') + outfile.write(' You should have received a copy of the GNU General Public License\n') + outfile.write(' along with TIDES. If not, see .\n') + outfile.write('\n') + outfile.write(' *****************************************************************************/\n') + outfile.write('\n') + outfile.write(' #include "mpfr.h"\n') + outfile.write(' #include "mp_tides.h"\n') + outfile.write(' long function_iteration(iteration_data *itd, mpfr_t t, mpfr_t v[], mpfr_t p[], int ORDER, mpfr_t *cvfd);\n') + outfile.write('\n') + outfile.write(' int main() {\n') + outfile.write('\n') + outfile.write(' int i;\n') + outfile.write('\n') + outfile.write('\n') + outfile.write('\tint nfun = 0;\n') outfile.write('\tset_precision_digits({});'.format(dig)) outfile.write('\n\tint npar = {};\n'.format(npar)) From 8da2c73aeb5a5ca2e1561131fb3218b3c451b33c Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sat, 28 Jun 2014 10:17:13 +0200 Subject: [PATCH 420/546] trac #16503: Broken doc --- src/sage/combinat/designs/orthogonal_arrays_recursive.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_recursive.py index 544691c9bbd..e12bab4fbdd 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_recursive.py @@ -619,7 +619,7 @@ def construction_q_x(k,q,x,check=True): sage: from sage.combinat.designs.orthogonal_arrays_recursive import construction_q_x sage: _ = construction_q_x(9,16,6) - REFERENCES:: + REFERENCES: .. [Greig99] Designs from projective planes and PBD bases Malcolm Greig From 1f87955173aaad6aa501f838a5dcf467bbe6ebf6 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sat, 28 Jun 2014 10:00:07 +0200 Subject: [PATCH 421/546] trac #16535: n1 <= n2 in product decomp. + better doc --- .../designs/orthogonal_arrays_recursive.py | 54 ++++++++++++------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_recursive.py index 1a28cf2fd46..6d59ab2c99a 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_recursive.py @@ -34,7 +34,7 @@ def find_recursive_construction(k,n): This determines whether an `OA(k,n)` can be built through the following constructions: - - Wilson constructions with 0, 1 or 2 truncated groups + - :func:`simple_wilson_construction` (0, 1 or 2 truncated columns) - :func:`construction_3_3` - :func:`construction_3_4` - :func:`construction_3_5` @@ -82,17 +82,24 @@ def find_recursive_construction(k,n): def find_product_decomposition(k,n): r""" - Attempts to find a factorization of `n` in order to build a `OA(k,n)`. + Look for a factorization of `n` in order to build an `OA(k,n)`. If Sage can build a `OA(k,n_1)` and a `OA(k,n_2)` such that `n=n_1\times - n_2` then a `OA(k,n)` can be built (from the function - :func:`transversal_design`). This method returns such a pair of integers if - it exists, and ``False`` otherwise. + n_2` then a `OA(k,n)` can be built by a product construction (which + correspond to Wilson's construction with no truncated column). This + function look for a pair of integers `(n_1,n_2)` with `n1 \leq n_2`, `n_1 + \times n_2 = n` and such that both an `OA(k,n_1)` and an `OA(k,n_2)` are + available. INPUT: - ``k,n`` (integers) -- see above. + OUTPUT: + + A pair ``f,args`` such that ``f(*args)`` is an `OA(k,n)` or ``False`` if no + product decomposition was found. + EXAMPLES:: sage: from sage.combinat.designs.orthogonal_arrays_recursive import find_product_decomposition @@ -103,26 +110,29 @@ def find_product_decomposition(k,n): """ from sage.rings.arith import divisors for n1 in divisors(n)[1:-1]: # we ignore 1 and n - n2 = n//n1 + n2 = n//n1 # n2 is decreasing along the loop + if n2 < n1: + break if orthogonal_array(k, n1, existence=True) and orthogonal_array(k, n2, existence=True): return simple_wilson_construction, (k,n1,n2,()) return False def find_wilson_decomposition_with_one_truncated_group(k,n): r""" - Finds a wilson decomposition of `k,n` with one truncated group. + Helper function for Wilson's construction with one truncated column. - This method looks for possible integers `m,t,u` satisfying that `mt+u=n` and - such that Sage knows how to build a `TD(k,m), TD(k,m+1),TD(k+1,t)` and a - `TD(k,u)`. These can then be used to feed :func:`wilson_construction`. + This function looks for possible integers `m,t,u` satisfying that `mt+u=n` and + such that Sage knows how to build a `OA(k,m), OA(k,m+1),OA(k+1,t)` and a + `OA(k,u)`. INPUT: - - `k,n` (integers) + - ``k,n`` (integers) -- see above OUTPUT: - Returns a 4-tuple `(k, r, m, (u,))` if it is found, and ``False`` otherwise. + A pair `f,args` such that `f(*args)` is an `OA(k,n)` or ``False`` if no + decomposition with one truncated block was found. EXAMPLES:: @@ -158,18 +168,20 @@ def find_wilson_decomposition_with_one_truncated_group(k,n): def find_wilson_decomposition_with_two_truncated_groups(k,n): r""" - Helper function for Wilson's construction with two trucated columns + Helper function for Wilson's construction with two trucated columns. - Find integers `r,m,r_1,r_2` satisfying `n=rm+r_1+r_2` and `1\leq r_1,r_2 Date: Sat, 28 Jun 2014 12:29:31 +0200 Subject: [PATCH 422/546] trac #16528: review --- src/sage/combinat/designs/database.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 32cf5d0e316..78063821d06 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -3060,9 +3060,9 @@ def RBIBD_120_8_1(): # A (precomputed) set that every block of the BIBD intersects on 0 or 2 points hyperoval = [128, 192, 194, 4, 262, 140, 175, 48, 81, 180, 245, 271, 119, 212, 249, 189, 62, 255] - for B in BIBD: - len_trace = len([x for x in B if x in hyperoval]) - assert len_trace == 0 or len_trace == 2 + #for B in BIBD: + # len_trace = sum(x in hyperoval for x in B) + # assert len_trace == 0 or len_trace == 2 # Equivalence classes p = hyperoval[0] @@ -3077,14 +3077,14 @@ def RBIBD_120_8_1(): BIBD = new_BIBD - r = {v:i for i,v in enumerate([x for x in range(n) if x not in hyperoval])} + r = {v:i for i,v in enumerate(x for x in range(n) if x not in hyperoval)} BIBD = [[r[x] for x in B] for B in BIBD ] equiv = [[r[x] for x in B] for B in equiv] BIBD = IncidenceStructure(range(255),BIBD) - Matrix = BIBD.incidence_matrix() + M = BIBD.incidence_matrix() - equiv = [[Matrix[x].dict().keys() for x in S] for S in equiv] + equiv = [[M.nonzero_positions_in_row(x) for x in S] for S in equiv] return [B for S in equiv for B in S] # Index of the BIBD constructions From 2bd16e0fafd5bca36bc5378bf4a59d62cc79d0bf Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sat, 28 Jun 2014 15:51:14 +0200 Subject: [PATCH 423/546] trac #16528: Broken doctest --- src/sage/combinat/designs/database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 78063821d06..b75f0f6b083 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -3049,7 +3049,7 @@ def RBIBD_120_8_1(): The BIBD is available from the constructor:: - sage: _ = designs.BalancedIncompleteBlockDesign(120,8) + sage: _ = designs.balanced_incomplete_block_design(120,8) """ from incidence_structures import IncidenceStructure n=273 From 01689fdd0163748dcb407f3c36e8c36f78e83fba Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sat, 28 Jun 2014 15:12:38 +0200 Subject: [PATCH 424/546] trac #16582: MOLS: Table with n<600 and updated syntax --- src/sage/combinat/designs/database.py | 12 +-- src/sage/combinat/designs/latin_squares.py | 104 +++++++++++++-------- 2 files changed, 72 insertions(+), 44 deletions(-) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index c8777a3568e..8262347d5ed 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -177,7 +177,7 @@ def _MOLS_from_string(s,k): EXAMPLES:: - sage: _ = designs.mutually_orthogonal_latin_squares(10,2) # indirect doctest + sage: _ = designs.mutually_orthogonal_latin_squares(2,10) # indirect doctest """ from sage.matrix.constructor import Matrix matrices = [[] for _ in range(k)] @@ -203,7 +203,7 @@ def MOLS_10_2(): The design is available from the general constructor:: - sage: designs.mutually_orthogonal_latin_squares(10,2,existence=True) + sage: designs.mutually_orthogonal_latin_squares(2,10,existence=True) True """ from sage.matrix.constructor import Matrix @@ -276,7 +276,7 @@ def MOLS_14_4(): The design is available from the general constructor:: - sage: designs.mutually_orthogonal_latin_squares(14,4,existence=True) + sage: designs.mutually_orthogonal_latin_squares(4,14,existence=True) True """ M = """ @@ -314,7 +314,7 @@ def MOLS_15_4(): The design is available from the general constructor:: - sage: designs.mutually_orthogonal_latin_squares(15,4,existence=True) + sage: designs.mutually_orthogonal_latin_squares(4,15,existence=True) True """ M = """ @@ -353,7 +353,7 @@ def MOLS_18_3(): The design is available from the general constructor:: - sage: designs.mutually_orthogonal_latin_squares(18,3,existence=True) + sage: designs.mutually_orthogonal_latin_squares(3,18,existence=True) True """ M = """ @@ -383,7 +383,7 @@ def MOLS_18_3(): # # Associates to n the pair (k,f) where f() is a function that returns k MOLS of order n # -# This dictionary is used by designs.mutually_orthogonal_latin_squares(n,k). +# This dictionary is used by designs.mutually_orthogonal_latin_squares(k,n). MOLS_constructions = { 10 : (2, MOLS_10_2), diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 50b291f1e1e..f679522e4bc 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -2,26 +2,25 @@ r""" Mutually Orthogonal Latin Squares (MOLS) -A Latin square is an `n\times n` array filled with `n` different symbols, each -occurring exactly once in each row and exactly once in each column. For Sage's -methods related to Latin Squares, see the module -:mod:`sage.combinat.matrices.latin`. +This module gathers Sage's functions related to Mutually Orthogonal Latin +Squares. Its main function is :func:`mutually_orthogonal_latin_squares` which +can be used to generate MOLS:: -This module gathers constructions of Mutually Orthogonal Latin Squares, which -are equivalent to Transversal Designs and specific Orthogonal Arrays. + sage: MOLS = designs.mutually_orthogonal_latin_squares(4,8) For more information on MOLS, see the :wikipedia:`Wikipedia entry on MOLS -`. +`. If you are only +interested by latin squares, see :mod:`~sage.combinat.matrices.latin`. The following table prints the maximum number of MOLS that Sage can build for -every order `n<300`, similarly to the `table of MOLS +every order `n<600`, similarly to the `table of MOLS `_ -from the Handbook of Combinatorial Designs. +from the Handbook of Combinatorial Designs 2ed [DesignHandbook]_. :: sage: from sage.combinat.designs.latin_squares import MOLS_table - sage: MOLS_table(15) # long time + sage: MOLS_table(30) # long time 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ________________________________________________________________________________ 0| +oo +oo 1 2 3 4 1 6 7 8 2 10 5 12 4 4 15 16 5 18 @@ -39,11 +38,27 @@ 240| 7 240 6 242 6 7 6 12 7 7 6 250 6 10 7 7 255 256 6 12 260| 6 8 7 262 7 8 7 10 7 268 7 270 15 16 6 13 10 276 6 9 280| 7 280 6 282 6 12 6 7 15 288 6 6 6 292 6 6 7 10 10 12 - - -Comparison with the results from the Handbook of Combinatorial Designs (2ed):: - - sage: MOLS_table(15,compare=True) # long time + 300| 7 7 7 7 15 15 6 306 7 7 7 310 7 312 7 10 7 316 7 10 + 320| 15 15 6 16 8 12 6 7 7 9 6 330 7 8 7 6 7 336 6 7 + 340| 6 10 10 342 7 7 6 346 6 348 8 12 10 352 6 9 7 7 6 358 + 360| 7 360 6 7 7 7 6 366 15 15 7 15 7 372 7 15 7 13 7 378 + 380| 7 12 7 382 15 15 7 15 7 388 7 16 7 7 7 7 8 396 7 7 + 400| 15 400 7 15 11 8 7 15 7 408 7 13 8 12 10 9 15 15 7 418 + 420| 7 420 7 15 7 16 6 7 7 7 6 430 15 432 6 15 6 18 7 438 + 440| 7 15 7 442 7 13 7 11 15 448 7 15 7 7 7 15 7 456 7 16 + 460| 7 460 7 462 15 15 7 466 8 8 7 15 7 15 10 18 7 15 6 478 + 480| 15 15 6 15 8 7 6 486 7 15 6 490 6 16 6 7 15 15 6 498 + 500| 7 8 9 502 7 15 6 15 7 508 6 15 511 18 6 15 8 12 8 15 + 520| 7 520 6 522 7 15 8 16 15 528 7 15 8 12 7 15 8 15 10 15 + 540| 12 540 7 15 16 7 7 546 7 8 7 18 7 7 7 7 7 556 7 12 + 560| 7 7 7 562 7 7 6 7 7 568 6 570 7 7 15 22 8 576 7 7 + 580| 7 8 7 10 7 8 7 586 7 18 17 7 15 592 8 15 7 7 8 598 + + +Comparison with the results from the Handbook of Combinatorial Designs (2ed) +[DesignHandbook]_:: + + sage: MOLS_table(30,compare=True) # long time 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ________________________________________________________________________________ 0| + + @@ -61,6 +76,21 @@ 240| - - 260| - 280| + 300| + 320| - + 340| - - + 360| - - + 380| - + 400| - - + 420| - + 440| + 460| + 480| + 500| - - + 520| - - - + 540| - + 560| - + 580| TODO: @@ -110,7 +140,7 @@ def are_mutually_orthogonal_latin_squares(l, verbose=False): Squares 0 and 2 are not orthogonal False - sage: m = designs.mutually_orthogonal_latin_squares(8,7) + sage: m = designs.mutually_orthogonal_latin_squares(7,8) sage: are_mutually_orthogonal_latin_squares(m) True @@ -157,22 +187,20 @@ def are_mutually_orthogonal_latin_squares(l, verbose=False): from designs_pyx import is_orthogonal_array return is_orthogonal_array(zip(*[[x for R in M for x in R] for M in l]),k,n, verbose=verbose, terminology="MOLS") -def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, existence=False): +def mutually_orthogonal_latin_squares(k,n, partitions = False, check = True, existence=False): r""" Returns `k` Mutually Orthogonal `n\times n` Latin Squares (MOLS). - For more information on Latin Squares and MOLS, see - :mod:`~sage.combinat.designs.latin_squares` or the :wikipedia:`Latin_square`, - or even the - :wikipedia:`Wikipedia entry on MOLS `. + For more information on Mutually Orthogonal Latin Squares, see + :mod:`~sage.combinat.designs.latin_squares`. INPUT: - - ``n`` (integer) -- size of the latin square. - - ``k`` (integer) -- number of MOLS. If ``k=None`` it is set to the largest value available. + - ``n`` (integer) -- size of the latin square. + - ``partition`` (boolean) -- a Latin Square can be seen as 3 partitions of the `n^2` cells of the array into `n` sets of size `n`, respectively : @@ -210,7 +238,7 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi EXAMPLES:: - sage: designs.mutually_orthogonal_latin_squares(5,4) + sage: designs.mutually_orthogonal_latin_squares(4,5) [ [0 2 4 1 3] [0 3 1 4 2] [0 4 3 2 1] [0 1 2 3 4] [4 1 3 0 2] [3 1 4 2 0] [2 1 0 4 3] [4 0 1 2 3] @@ -219,7 +247,7 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi [1 3 0 2 4], [2 0 3 1 4], [3 2 1 0 4], [1 2 3 4 0] ] - sage: designs.mutually_orthogonal_latin_squares(7,3) + sage: designs.mutually_orthogonal_latin_squares(3,7) [ [0 2 4 6 1 3 5] [0 3 6 2 5 1 4] [0 4 1 5 2 6 3] [6 1 3 5 0 2 4] [5 1 4 0 3 6 2] [4 1 5 2 6 3 0] @@ -230,7 +258,7 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi [1 3 5 0 2 4 6], [2 5 1 4 0 3 6], [3 0 4 1 5 2 6] ] - sage: designs.mutually_orthogonal_latin_squares(5,2,partitions=True) + sage: designs.mutually_orthogonal_latin_squares(2,5,partitions=True) [[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12, 13, 14], @@ -254,7 +282,7 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi What is the maximum number of MOLS of size 8 that Sage knows how to build?:: - sage: designs.mutually_orthogonal_latin_squares(8,None,existence=True) + sage: designs.mutually_orthogonal_latin_squares(None,8,existence=True) 7 If you only want to know if Sage is able to build a given set of MOLS, just @@ -262,7 +290,7 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi sage: designs.mutually_orthogonal_latin_squares(5, 5, existence=True) False - sage: designs.mutually_orthogonal_latin_squares(6, 4, existence=True) + sage: designs.mutually_orthogonal_latin_squares(4,6, existence=True) Unknown If you ask for such a MOLS then you will respecively get an informative @@ -272,7 +300,7 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi Traceback (most recent call last): ... EmptySetError: There exist at most n-1 MOLS of size n if n>=2. - sage: designs.mutually_orthogonal_latin_squares(6, 4) + sage: designs.mutually_orthogonal_latin_squares(4,6) Traceback (most recent call last): ... NotImplementedError: I don't know how to build 4 MOLS of order 6 @@ -281,17 +309,17 @@ def mutually_orthogonal_latin_squares(n,k, partitions = False, check = True, exi The special case `n=1`:: - sage: designs.mutually_orthogonal_latin_squares(1, 3) + sage: designs.mutually_orthogonal_latin_squares(3, 1) [[0], [0], [0]] - sage: designs.mutually_orthogonal_latin_squares(1, None, existence=True) + sage: designs.mutually_orthogonal_latin_squares(None,1, existence=True) +Infinity - sage: designs.mutually_orthogonal_latin_squares(1, None) + sage: designs.mutually_orthogonal_latin_squares(None, 1) Traceback (most recent call last): ... ValueError: there are no bound on k when 0<=n<=1 - sage: designs.mutually_orthogonal_latin_squares(10,2,existence=True) + sage: designs.mutually_orthogonal_latin_squares(2,10,existence=True) True - sage: designs.mutually_orthogonal_latin_squares(10,2) + sage: designs.mutually_orthogonal_latin_squares(2,10) [ [1 8 9 0 2 4 6 3 5 7] [1 7 6 5 0 9 8 2 3 4] [7 2 8 9 0 3 5 4 6 1] [8 2 1 7 6 0 9 3 4 5] @@ -412,7 +440,7 @@ def latin_square_product(M,N,*others): EXAMPLES:: sage: from sage.combinat.designs.latin_squares import latin_square_product - sage: m=designs.mutually_orthogonal_latin_squares(4,3)[0] + sage: m=designs.mutually_orthogonal_latin_squares(3,4)[0] sage: latin_square_product(m,m,m) 64 x 64 sparse matrix over Integer Ring (use the '.str()' method to see the entries) """ @@ -449,7 +477,7 @@ def MOLS_table(number_of_lines,compare=False): EXAMPLES:: sage: from sage.combinat.designs.latin_squares import MOLS_table - sage: MOLS_table(5) # long time + sage: MOLS_table(5) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ________________________________________________________________________________ 0| +oo +oo 1 2 3 4 1 6 7 8 2 10 5 12 4 4 15 16 5 18 @@ -457,7 +485,7 @@ def MOLS_table(number_of_lines,compare=False): 40| 7 40 5 42 5 6 4 46 8 48 6 5 5 52 5 6 7 7 5 58 60| 5 60 5 6 63 7 5 66 5 6 6 70 7 72 5 7 6 6 6 78 80| 9 80 8 82 6 6 6 6 7 88 6 7 6 6 6 6 7 96 6 8 - sage: MOLS_table(5,compare=True) # long time + sage: MOLS_table(5,compare=True) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ________________________________________________________________________________ 0| + + @@ -474,7 +502,7 @@ def MOLS_table(number_of_lines,compare=False): for i in range(20*number_of_lines): if i%20==0: print "\n%3d|"%i, - k = mutually_orthogonal_latin_squares(i,None,existence=True) + k = mutually_orthogonal_latin_squares(None,i,existence=True) if compare: if i < 2 or hb[i] == k: c = "" From e5a53435bde170e774c4e1dafba8f333a9653330 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sat, 28 Jun 2014 17:26:46 +0200 Subject: [PATCH 425/546] 6882: do word search/replace for symtable keys --- src/sage/calculus/calculus.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index 5b02d00e473..97900f237e0 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -1692,9 +1692,8 @@ def _inverse_laplace_latex_(self, *args): ####################################################### -symtable = {'%pi':'pi', 'e':'_e', '%e': 'e', 'i':'_i', 'I':'_I', '%i':'I', '%gamma':'euler_gamma'} +symtable = {'%pi':'pi', '%e': 'e', '%i':'I', '%gamma':'euler_gamma'} -from sage.misc.multireplace import multiple_replace import re maxima_tick = re.compile("'[a-z|A-Z|0-9|_]*") @@ -1768,16 +1767,13 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): Check that some variables don't end up as special constants (:trac:`6882`):: - sage: symbolic_expression_from_maxima_string('%i')^2 + sage: from sage.calculus.calculus import symbolic_expression_from_maxima_string as sefms + sage: sefms('%i')^2 -1 - sage: symbolic_expression_from_maxima_string('I')^2 - _I^2 - sage: symbolic_expression_from_maxima_string('i')^2 - _i^2 - sage: ln(symbolic_expression_from_maxima_string('%e')) + sage: ln(sefms('%e')) 1 - sage: ln(symbolic_expression_from_maxima_string('e')) - log(_e) + sage: sefms('%inf') + +Infinity """ syms = sage.symbolic.pynac.symbol_table.get('maxima', {}).copy() @@ -1808,8 +1804,18 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): syms[X[2:]] = function_factory(X[2:]) s = s.replace("?%","") - s = polylog_ex.sub('polylog(\\1,',s) - s = multiple_replace(symtable, s) + # Look up every variable in the symtable keys and fill a replacement list. + cursor = 0 + l = [] + for m in maxima_var.finditer(s): + if symtable.has_key(m.group(0)): + l.append(s[cursor:m.start()]) + l.append(symtable.get(m.group(0))) + cursor = m.end() + if cursor > 0: + l.append(s[cursor:]) + s = "".join(l) + s = s.replace("%","") s = s.replace("#","!=") # a lot of this code should be refactored somewhere... From 4c1b0eb5d6ff4b2b95487440c27c9879edb0131e Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sat, 28 Jun 2014 17:50:28 +0200 Subject: [PATCH 426/546] 6882: correction to previous commit --- src/sage/calculus/calculus.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index 97900f237e0..b7b9ac66872 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -1772,8 +1772,6 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): -1 sage: ln(sefms('%e')) 1 - sage: sefms('%inf') - +Infinity """ syms = sage.symbolic.pynac.symbol_table.get('maxima', {}).copy() @@ -1820,6 +1818,7 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): s = s.replace("#","!=") # a lot of this code should be refactored somewhere... + s = polylog_ex.sub('polylog(\\1,',s) s = maxima_polygamma.sub('psi(\g<1>,',s) # this replaces psi[n](foo) with psi(n,foo), ensuring that derivatives of the digamma function are parsed properly below if equals_sub: From 518de3e62533cd209997107f903192f1a31d118c Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sat, 28 Jun 2014 18:07:49 +0200 Subject: [PATCH 427/546] 6882: add symable rules for e,i,I; fix maxima_var; add doctests --- src/sage/calculus/calculus.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index b7b9ac66872..24c7d133681 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -1692,7 +1692,8 @@ def _inverse_laplace_latex_(self, *args): ####################################################### -symtable = {'%pi':'pi', '%e': 'e', '%i':'I', '%gamma':'euler_gamma'} +symtable = {'%pi':'pi', '%e': 'e', '%i':'I', '%gamma':'euler_gamma', + 'e':'_e', 'i':'_i', 'I':'_I'} import re @@ -1700,7 +1701,7 @@ def _inverse_laplace_latex_(self, *args): maxima_qp = re.compile("\?\%[a-z|A-Z|0-9|_]*") # e.g., ?%jacobi_cd -maxima_var = re.compile("\%[a-z|A-Z|0-9|_]*") # e.g., ?%jacobi_cd +maxima_var = re.compile("[a-z|A-Z|0-9|_\%]*") # e.g., %jacobi_cd sci_not = re.compile("(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]\d+)") @@ -1765,13 +1766,21 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): sage: solve([2*x==3, x != 5], x) [[x == (3/2), (-7/2) != 0]] - Check that some variables don't end up as special constants (:trac:`6882`):: + Check that some hypothetical variables don't end up as special constants (:trac:`6882`):: sage: from sage.calculus.calculus import symbolic_expression_from_maxima_string as sefms sage: sefms('%i')^2 -1 sage: ln(sefms('%e')) 1 + sage: sefms('i')^2 + _i^2 + sage: sefms('I')^2 + _I^2 + sage: sefms('ln(e)') + ln(_e) + sage: sefms('%inf') + +Infinity """ syms = sage.symbolic.pynac.symbol_table.get('maxima', {}).copy() From efdc1fc9bafe9963ed52cff8bdfafc610723b247 Mon Sep 17 00:00:00 2001 From: Miguel Marco Date: Sat, 28 Jun 2014 18:41:22 +0200 Subject: [PATCH 428/546] Added package names in spkg-install --- build/pkgs/tides/spkg-install | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/tides/spkg-install b/build/pkgs/tides/spkg-install index 82a5f6b2639..2ef25cc797a 100644 --- a/build/pkgs/tides/spkg-install +++ b/build/pkgs/tides/spkg-install @@ -4,18 +4,18 @@ cd src ./configure --prefix="$SAGE_LOCAL" --libdir="$SAGE_LOCAL/lib" if [ $? -ne 0 ]; then - echo >&2 "Error configuring PACKAGE_NAME." + echo >&2 "Error configuring TIDES." exit 1 fi $MAKE if [ $? -ne 0 ]; then - echo >&2 "Error building PACKAGE_NAME." + echo >&2 "Error building TIDES." exit 1 fi $MAKE -j1 install if [ $? -ne 0 ]; then - echo >&2 "Error installing PACKAGE_NAME." + echo >&2 "Error installing TIDES." exit 1 fi From b4295bd123e88177b96d68965fff337225deabe3 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 28 Jun 2014 13:40:29 -0400 Subject: [PATCH 429/546] Fix for immutable graphs for canonical_label(). --- src/sage/graphs/generic_graph.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 08df666afef..89e4553fcc6 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -17950,9 +17950,16 @@ def canonical_label(self, partition=None, certify=False, verbosity=0, edge_label Graph on 5 vertices sage: G.canonical_label(edge_labels=True,certify=True) (Graph on 5 vertices, {0: 4, 1: 3, 2: 0, 3: 1, 4: 2}) + + Check for immutable graphs (:trac:`16XXX`):: + + sage: G = Graph([[1, 2], [2, 3]], immutable=True) + sage: C = G.canonical_label(); C + Graph on 3 vertices + sage: C.vertices() + [0, 1, 2] """ from sage.groups.perm_gps.partn_ref.refinement_graphs import search_tree - from copy import copy dig = (self.has_loops() or self._directed) if partition is None: @@ -17974,7 +17981,7 @@ def canonical_label(self, partition=None, certify=False, verbosity=0, edge_label partition = [[G_to[v] for v in cell] for cell in partition] a,b,c = search_tree(GC, partition, certify=True, dig=dig, verbosity=verbosity) # c is a permutation to the canonical label of G, which depends only on isomorphism class of self. - H = copy(self) + H = self.copy(immutable=False) c_new = {} for v in self.vertices(): c_new[v] = c[G_to[relabeling[v]]] @@ -17997,7 +18004,7 @@ def canonical_label(self, partition=None, certify=False, verbosity=0, edge_label GC = HB._cg partition = [[G_to[v] for v in cell] for cell in partition] a,b,c = search_tree(GC, partition, certify=True, dig=dig, verbosity=verbosity) - H = copy(self) + H = self.copy(immutable=False) c_new = {} for v in G_to: c_new[v] = c[G_to[v]] From 8902f6146662270b68fca4745284f0aa32f8818f Mon Sep 17 00:00:00 2001 From: Miguel Marco Date: Sat, 28 Jun 2014 20:33:03 +0200 Subject: [PATCH 430/546] Cleaned a little bit the header of the generated files --- src/sage/interfaces/tides.py | 353 +++++++++++++---------------------- 1 file changed, 126 insertions(+), 227 deletions(-) diff --git a/src/sage/interfaces/tides.py b/src/sage/interfaces/tides.py index 77773ac14dd..b04f3325a8c 100644 --- a/src/sage/interfaces/tides.py +++ b/src/sage/interfaces/tides.py @@ -373,62 +373,23 @@ def genfiles_mintides(integrator, driver, f, ics, initial, final, delta, sage: fileint = open(intfile) sage: l = fileint.readlines() sage: fileint.close() - sage: filter(lambda a: len(a)>2, l[28:]) - ['#include "minc_tides.h"\n', - 'void mincseries(double t,double *v, double *p, double **XVAR,int ORDER, int MO)\n', - '\tint VAR,PAR,TT,i,j, inext;\n', - '\tVAR = 5;\n', - '\tPAR = 0;\n', - '\tTT = 12;\n', - '\tdouble XX[TT+1][MO+1];\n', - '\tfor(j=0; j<=TT; j++)\n', - '\t\tfor(i=0; i<=ORDER; i++)\n', - '\t\t\tXX[j][i] = 0.e0;\n', - '\tXX[0][0] = t;\n', - '\tXX[0][1] = 1.e0;\n', - '\tfor(i=1;i<=VAR;i++) {\n', - '\t\tXX[i][0] = v[i-1];\n', - '\t}\n', - '\tfor(i=0;i2, l[28:]) - ['#include "minc_tides.h"\n', - 'int main() {\n', - ' int i, VARS, PARS; \n', - '\tVARS = 4 ;\n', - '\tPARS = 1;\n', - '\tdouble tolrel, tolabs, tini, tend, dt; \n', - '\tdouble v[VARS], p[PARS]; \n', - '\tv[0] = 1 ; \n', - '\tv[1] = 0 ; \n', - '\tv[2] = 0 ; \n', - '\tv[3] = 0.200000000000000 ; \n', - '\ttini = 0 ;\n', - '\ttend = 10 ;\n', - '\tdt = 0.100000000000000 ;\n', - '\ttolrel = 1e-16 ;\n', - '\ttolabs = 1e-16 ;\n', - '\textern char ofname[500];\tstrcpy(ofname, "out");\n', - '\tminc_tides(v,VARS,p,PARS,tini,tend,dt,tolrel,tolabs);\n', - '\treturn 0; \n'] + sage: l[6] + ' #include "minc_tides.h"\n' + sage: l[15] + ' double tolrel, tolabs, tini, tend, dt;\n' + sage: l[25] + '\ttolrel = 1e-16 ;\n' sage: shutil.rmtree(tempdir) @@ -525,60 +486,39 @@ def genfiles_mintides(integrator, driver, f, ics, initial, final, delta, outfile = open(integrator, 'a') - outfile.write('/****************************************************************************\n') - outfile.write('\tThis file has been created by SageTIDES (1.00)\n') - outfile.write('\n') - outfile.write('\tCopyright (C) 2010 A. Abad, R. Barrio, F. Blesa, M. Marco, M. Rodriguez\n') - outfile.write('\tGrupo de Mecanica Espacial\n') - outfile.write('\tUniversity of Zaragoza\n') - outfile.write('\tSPAIN\n') - outfile.write('\n') - outfile.write('\thttp://gme.unizar.es/software/tides\n') - outfile.write('\tContact: \n') - outfile.write('\n') - outfile.write('\tThis file is part of TIDES.\n') - outfile.write('\n') - outfile.write('\tTIDES is free software: you can redistribute it and/or modify\n') - outfile.write('\tit under the terms of the GNU General Public License as published by\n') - outfile.write('\tthe Free Software Foundation, either version 3 of the License, or\n') - outfile.write('\t(at your option) any later version.\n') - outfile.write('\n') - outfile.write('\tTIDES is distributed in the hope that it will be useful,\n') - outfile.write('\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n') - outfile.write('\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n') - outfile.write('\tGNU General Public License for more details.\n') - outfile.write('\n') - outfile.write('\tYou should have received a copy of the GNU General Public License\n') - outfile.write('\talong with TIDES. If not, see .\n') - outfile.write('\n') - outfile.write('*****************************************************************************/\n') - outfile.write('\n') - outfile.write('\n') - outfile.write('\n') - outfile.write('#include "minc_tides.h"\n') - outfile.write('\n') - outfile.write('void mincseries(double t,double *v, double *p, double **XVAR,int ORDER, int MO)\n') - outfile.write('{\n') - outfile.write('\tint VAR,PAR,TT,i,j, inext;\n') - outfile.write('\n') + auxstring = """ + /**************************************************************************** + This file has been created by Sage for its use with TIDES + *****************************************************************************/ + + #include "minc_tides.h" + + void mincseries(double t,double *v, double *p, double **XVAR,int ORDER, int MO) + { + int VAR,PAR,TT,i,j, inext; + """ + outfile.write(auxstring) + outfile.write("\tVAR = {};\n".format(n)) outfile.write("\tPAR = {};\n".format(0)) outfile.write("\tTT = {};\n".format(len(res))) - outfile.write('\tdouble XX[TT+1][MO+1];\n') - outfile.write('\n') - outfile.write('\tfor(j=0; j<=TT; j++)\n') - outfile.write('\t\tfor(i=0; i<=ORDER; i++)\n') - outfile.write('\t\t\tXX[j][i] = 0.e0;\n') - outfile.write('\tXX[0][0] = t;\n') - outfile.write('\tXX[0][1] = 1.e0;\n') - outfile.write('\tfor(i=1;i<=VAR;i++) {\n') - outfile.write('\t\tXX[i][0] = v[i-1];\n') - outfile.write('\t}\n') - outfile.write('\n') - outfile.write('\tfor(i=0;i\n') - outfile.write('\n') - outfile.write(' This file is part of TIDES.\n') - outfile.write('\n') - outfile.write(' TIDES is free software: you can redistribute it and/or modify\n') - outfile.write(' it under the terms of the GNU General Public License as published by\n') - outfile.write(' the Free Software Foundation, either version 3 of the License, or\n') - outfile.write(' (at your option) any later version.\n') - outfile.write('\n') - outfile.write(' TIDES is distributed in the hope that it will be useful,\n') - outfile.write(' but WITHOUT ANY WARRANTY; without even the implied warranty of\n') - outfile.write(' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n') - outfile.write(' GNU General Public License for more details.\n') - outfile.write('\n') - outfile.write(' You should have received a copy of the GNU General Public License\n') - outfile.write(' along with TIDES. If not, see .\n') - outfile.write('\n') - outfile.write('*****************************************************************************/\n') - outfile.write('\n') - outfile.write('#include "minc_tides.h"\n') - outfile.write('\n') - outfile.write('int main() {\n') - outfile.write('\n') - outfile.write(' int i, VARS, PARS; ') + auxstring = """ + /**************************************************************************** + Driver file of the minc_tides program + This file has been automatically created by Sage + *****************************************************************************/ + + #include "minc_tides.h" + + int main() { + + int i, VARS, PARS; - outfile.write('\n\tVARS = {} ;\n'.format(n-1)) - outfile.write('\tPARS = 1;\n') - outfile.write('\tdouble tolrel, tolabs, tini, tend, dt; \n') - outfile.write('\tdouble v[VARS], p[PARS]; \n') + VARS = %s ; + PARS = 1; + double tolrel, tolabs, tini, tend, dt; + double v[VARS], p[PARS]; + + """%(n-1) + outfile.write(auxstring) for i in range(len(ics)): outfile.write('\tv[{}] = {} ; \n'.format(i, ics[i])) outfile.write('\ttini = {} ;\n'.format(initial)) @@ -700,15 +620,35 @@ def genfiles_mpfr(integrator, driver, f, ics, initial, final, delta, sage: fileint = open(intfile) sage: l = fileint.readlines() sage: fileint.close() - sage: l[56] - '\t\tmpfrts_add_t(itd, link[1], link[0], link[2], i);\n' + sage: l[5] + ' #include "mp_tides.h"\n' + sage: l[15] + '\tstatic int PARAMETERS = 0;\n' + sage: l[25] + '\t\tmpfrts_var_t(itd, link[5], var[3], i);\n' + sage: l[30] + '\t\tmpfrts_pow_t_c(itd, link[2], "-1.5000000000000000000000000000000000000000000000000", link[3], i);\n' + sage: l[35] + '\n' + sage: l[36] + ' }\n' + sage: l[37] + ' write_mp_solution();\n' sage: filedr = open(drfile) sage: l = filedr.readlines() sage: filedr.close() - sage: l[45] - '\tfor(i=0; i\n') - outfile.write('\n') - outfile.write('\tThis file is part of TIDES.\n') - outfile.write('\n') - outfile.write('\tTIDES is free software: you can redistribute it and/or modify\n') - outfile.write('\tit under the terms of the GNU General Public License as published by\n') - outfile.write('\tthe Free Software Foundation, either version 3 of the License, or\n') - outfile.write('\t(at your option) any later version.\n') - outfile.write('\n') - outfile.write('\tTIDES is distributed in the hope that it will be useful,\n') - outfile.write('\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n') - outfile.write('\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n') - outfile.write('\tGNU General Public License for more details.\n') - outfile.write('\n') - outfile.write('\tYou should have received a copy of the GNU General Public License\n') - outfile.write('\talong with TIDES. If not, see .\n') - outfile.write('\n') - outfile.write('*****************************************************************************/\n') - outfile.write('\n') - outfile.write('#include "mp_tides.h"\n') - outfile.write('\n') - outfile.write('\n') - outfile.write('\n') - outfile.write('long function_iteration(iteration_data *itd, mpfr_t t, mpfr_t v[], mpfr_t p[], int ORDER, mpfr_t *cvfd)\n') - outfile.write('{\n') - outfile.write('\n') - outfile.write('\tint i;\n') - outfile.write(' int NCONST = 0;\n') - outfile.write(' mpfr_t ct[0];\n') - outfile.write('\n') - outfile.write('\n') + #include "mp_tides.h" + long function_iteration(iteration_data *itd, mpfr_t t, mpfr_t v[], mpfr_t p[], int ORDER, mpfr_t *cvfd) + { + + int i; + int NCONST = 0; + mpfr_t ct[0]; + """ + + outfile.write(auxstring) outfile.write("\n\tstatic int VARIABLES = {};\n".format(VAR)) outfile.write("\tstatic int PARAMETERS = {};\n".format(PAR)) @@ -872,55 +786,40 @@ def genfiles_mpfr(integrator, driver, f, ics, initial, final, delta, for i in code: outfile.write('\t\t'+i+'\n') - outfile.write('\t}\n\twrite_mp_solution();\n\n') - outfile.write('\tclear_vpl();\n\tclear_cts();\n') - outfile.write('\treturn NUM_COLUMNS;\n}') + auxstring = """ + } + write_mp_solution(); + clear_vpl(); + clear_cts(); + return NUM_COLUMNS; +} + """ + outfile.write(auxstring) outfile.close() npar = len(parameter_values) outfile = open(driver, 'a') - outfile.write('/****************************************************************************\n') - outfile.write('Driver file of the mp_tides program\n') - outfile.write('This file has been created by SageTIDES (1.0)\n') - outfile.write('\n') - outfile.write(' Copyright (C) 2010 A. Abad, R. Barrio, F. Blesa, M. Rodriguez\n') - outfile.write(' Grupo de Mecanica Espacial\n') - outfile.write(' University of Zaragoza\n') - outfile.write(' SPAIN\n') - outfile.write('\n') - outfile.write(' http://gme.unizar.es/software/tides\n') - outfile.write(' Contact: \n') - outfile.write('\n') - outfile.write(' This file is part of TIDES.\n') - outfile.write('\n') - outfile.write(' TIDES is free software: you can redistribute it and/or modify\n') - outfile.write(' it under the terms of the GNU General Public License as published by\n') - outfile.write(' the Free Software Foundation, either version 3 of the License, or\n') - outfile.write(' (at your option) any later version.\n') - outfile.write('\n') - outfile.write(' TIDES is distributed in the hope that it will be useful,\n') - outfile.write(' but WITHOUT ANY WARRANTY; without even the implied warranty of\n') - outfile.write(' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n') - outfile.write(' GNU General Public License for more details.\n') - outfile.write('\n') - outfile.write(' You should have received a copy of the GNU General Public License\n') - outfile.write(' along with TIDES. If not, see .\n') - outfile.write('\n') - outfile.write(' *****************************************************************************/\n') - outfile.write('\n') - outfile.write(' #include "mpfr.h"\n') - outfile.write(' #include "mp_tides.h"\n') - outfile.write(' long function_iteration(iteration_data *itd, mpfr_t t, mpfr_t v[], mpfr_t p[], int ORDER, mpfr_t *cvfd);\n') - outfile.write('\n') - outfile.write(' int main() {\n') - outfile.write('\n') - outfile.write(' int i;\n') - outfile.write('\n') - outfile.write('\n') + auxstring = """ + /**************************************************************************** + Driver file of the mp_tides program + This file has been created automatically by Sage + *****************************************************************************/ + + #include "mpfr.h" + #include "mp_tides.h" + long function_iteration(iteration_data *itd, mpfr_t t, mpfr_t v[], mpfr_t p[], int ORDER, mpfr_t *cvfd); + + int main() { - outfile.write('\tint nfun = 0;\n') + int i; + + + + int nfun = 0; + """ + outfile.write(auxstring) outfile.write('\tset_precision_digits({});'.format(dig)) outfile.write('\n\tint npar = {};\n'.format(npar)) outfile.write('\tmpfr_t p[npar];\n') From 5d2177475f90db894a7439d3bbb4f5e2c0326ea7 Mon Sep 17 00:00:00 2001 From: Miguel Marco Date: Sat, 28 Jun 2014 20:57:56 +0200 Subject: [PATCH 431/546] revision changes --- src/sage/interfaces/tides.py | 64 ++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/src/sage/interfaces/tides.py b/src/sage/interfaces/tides.py index b04f3325a8c..bb60c3c1d52 100644 --- a/src/sage/interfaces/tides.py +++ b/src/sage/interfaces/tides.py @@ -2,11 +2,11 @@ This module contains tools to write the .c files needed for TIDES [TI]_ . Tides is an integration engine based on the Taylor method. It is implemented -as a c library. The user must translate its IVP into a pair of .c files that -will then be compiled and linked against the TIDES library. The reulting binary -will produce the desored output. The tools in this module can be used to -automate the generation of these files from the symbolic expression of the -differential equation. +as a c library. The user must translate its initial value problem (IVP) into a +pair of .c files that will then be compiled and linked against the TIDES +library. The reulting binary will produce the desired output. The tools in this +module can be used to automate the generation of these files from the symbolic +expression of the differential equation. ########################################################################## # Copyright (C) 2014 Miguel Marco , Marcos Rodriguez @@ -45,25 +45,24 @@ from sage.calculus.all import symbolic_expression from sage.misc.flatten import flatten from sage.ext.fast_callable import fast_callable -from sage.misc.lazy_import import lazy_import -lazy_import('sage.rings.semirings.non_negative_integer_semiring', 'NN') +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.misc.functional import N from sage.functions.log import log from sage.functions.other import floor, sqrt -from sage.functions.trig import sin, cos, arcsin, arctan, arccos -def subexpressions_list(f, parameters=[]): + +def subexpressions_list(f, pars=None): """ Construct the lists with the intermediate steps on the evaluation of the function. INPUT: - - ``f`` -- a symbollic function of several components. + - ``f`` -- a symbolic function of several components. - - ``paramters`` -- a list of the parameters that appear in the function - this should be the symbollic constants that appear in f but are not + - ``pars`` -- a list of the parameters that appear in the function + this should be the symbolic constants that appear in f but are not arguments. OTUPUT: @@ -102,9 +101,47 @@ def subexpressions_list(f, parameters=[]): ([sin(a), cos(a), a^2, a^2 + 1, arctan(a)], [('sin', a), ('cos', a), ('mul', a, a), ('add', 1, a^2), ('atan', a)]) + :: + + sage: from sage.interfaces.tides import subexpressions_list + sage: var('s,b,r') + (s, b, r) + sage: f(t,x,y,z)= [s*(y-x),x*(r-z)-y,x*y-b*z] + sage: subexpressions_list(f,[s,b,r]) + ([-y, + x - y, + s*(x - y), + -s*(x - y), + -z, + r - z, + (r - z)*x, + -y, + (r - z)*x - y, + x*y, + b*z, + -b*z, + x*y - b*z], + [('mul', -1, y), + ('add', -y, x), + ('mul', x - y, s), + ('mul', -1, s*(x - y)), + ('mul', -1, z), + ('add', -z, r), + ('mul', x, r - z), + ('mul', -1, y), + ('add', -y, (r - z)*x), + ('mul', y, x), + ('mul', z, b), + ('mul', -1, b*z), + ('add', -b*z, x*y)]) """ + from sage.functions.trig import sin, cos, arcsin, arctan, arccos variables = f[0].arguments() + if not pars: + parameters = [] + else: + parameters = pars varpar = list(parameters) + list(variables) F = symbolic_expression([i(*variables) for i in f]).function(*varpar) lis = flatten([fast_callable(i,vars=varpar).op_list() for i in F], max_level=1) @@ -590,7 +627,8 @@ def genfiles_mpfr(integrator, driver, f, ics, initial, final, delta, - ``parameters`` -- the variables inside the function that should be treated as parameters. - - ``parameter_values`` -- the values of the parameters for the particular IVP + - ``parameter_values`` -- the values of the parameters for the particular + initial value problem. - ``dig`` -- the number of digits of precission that will be used in the integration From 4dc250afc93889e8bee051d0d2fc9ed07fae7ca6 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Fri, 27 Jun 2014 19:57:35 +0200 Subject: [PATCH 432/546] trac #16553: Clean IncidenceStructure --- src/sage/combinat/designs/all.py | 3 +- src/sage/combinat/designs/bibd.py | 32 +- src/sage/combinat/designs/block_design.py | 183 +-- src/sage/combinat/designs/design_catalog.py | 5 +- src/sage/combinat/designs/ext_rep.py | 6 +- .../combinat/designs/incidence_structures.py | 1176 +++++++++++------ 6 files changed, 804 insertions(+), 601 deletions(-) diff --git a/src/sage/combinat/designs/all.py b/src/sage/combinat/designs/all.py index c6a04de444d..7de8a9f529a 100644 --- a/src/sage/combinat/designs/all.py +++ b/src/sage/combinat/designs/all.py @@ -19,8 +19,7 @@ ["ProjectiveGeometryDesign", "AffineGeometryDesign", "WittDesign", - "HadamardDesign", - "BlockDesign_generic"], + "HadamardDesign"], ("This method soon will not be available in that " "way anymore. To use it, you can now call it by " "typing designs.%(name)s")) diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index e30f967f3ac..5ebb9e45b73 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -156,12 +156,12 @@ def balanced_incomplete_block_design(v,k,existence=False,use_LJCR=False): if v == 1: if existence: return True - return BlockDesign(v, [], test=False) + return BlockDesign(v, [], check=False) if k == v: if existence: return True - return BlockDesign(v, [range(v)], test=False) + return BlockDesign(v, [range(v)], check=False) if v < k or k < 2 or (v-1) % (k-1) != 0 or (v*(v-1)) % (k*(k-1)) != 0: if existence: @@ -172,7 +172,7 @@ def balanced_incomplete_block_design(v,k,existence=False,use_LJCR=False): if existence: return True from itertools import combinations - return BlockDesign(v, combinations(range(v),2), test = False) + return BlockDesign(v, combinations(range(v),2), check=False) if k == 3: if existence: return v%6 == 1 or v%6 == 3 @@ -180,18 +180,18 @@ def balanced_incomplete_block_design(v,k,existence=False,use_LJCR=False): if k == 4: if existence: return v%12 == 1 or v%12 == 4 - return BlockDesign(v, v_4_1_BIBD(v), test = False) + return BlockDesign(v, v_4_1_BIBD(v), check=False) if k == 5: if existence: return v%20 == 1 or v%20 == 5 - return BlockDesign(v, v_5_1_BIBD(v), test = False) + return BlockDesign(v, v_5_1_BIBD(v), check=False) from difference_family import difference_family if BIBD_from_TD(v,k,existence=True): if existence: return True - return BlockDesign(v, BIBD_from_TD(v,k)) + return BlockDesign(v, BIBD_from_TD(v,k), check=False) if v == (k-1)**2+k and is_prime_power(k-1): if existence: return True @@ -201,7 +201,7 @@ def balanced_incomplete_block_design(v,k,existence=False,use_LJCR=False): if existence: return True G,D = difference_family(v,k) - return BlockDesign(v, BIBD_from_difference_family(G,D,check=False), test=False) + return BlockDesign(v, BIBD_from_difference_family(G,D,check=False), check=False) if use_LJCR: from covering_design import best_known_covering_design_www B = best_known_covering_design_www(v,k,2) @@ -213,7 +213,7 @@ def balanced_incomplete_block_design(v,k,existence=False,use_LJCR=False): return False raise EmptySetError("No such design exists !") B = B.incidence_structure() - if len(B.blcks) == expected_n_of_blocks: + if B.num_blocks() == expected_n_of_blocks: if existence: return True else: @@ -253,12 +253,14 @@ def steiner_triple_system(n): sage: sts Incidence structure with 9 points and 12 blocks sage: list(sts) - [[0, 1, 5], [0, 2, 4], [0, 3, 6], [0, 7, 8], [1, 2, 3], [1, 4, 7], [1, 6, 8], [2, 5, 8], [2, 6, 7], [3, 4, 8], [3, 5, 7], [4, 5, 6]] + [[0, 1, 5], [0, 2, 4], [0, 3, 6], [0, 7, 8], [1, 2, 3], + [1, 4, 7], [1, 6, 8], [2, 5, 8], [2, 6, 7], [3, 4, 8], + [3, 5, 7], [4, 5, 6]] As any pair of vertices is covered once, its parameters are :: - sage: sts.parameters(t=2) - (2, 9, 3, 1) + sage: sts.is_t_design(return_parameters=True) + (True, (2, 9, 3, 1)) An exception is raised for invalid values of ``n`` :: @@ -359,21 +361,21 @@ def BIBD_from_TD(v,k,existence=False): sage: from sage.combinat.designs.bibd import BIBD_from_TD sage: BIBD_from_TD(25,5,existence=True) True - sage: _ = BlockDesign(25,BIBD_from_TD(25,5)) + sage: _ = designs.BlockDesign(25,BIBD_from_TD(25,5)) Second construction:: sage: from sage.combinat.designs.bibd import BIBD_from_TD sage: BIBD_from_TD(21,5,existence=True) True - sage: _ = BlockDesign(21,BIBD_from_TD(21,5)) + sage: _ = designs.BlockDesign(21,BIBD_from_TD(21,5)) Third construction:: sage: from sage.combinat.designs.bibd import BIBD_from_TD sage: BIBD_from_TD(85,5,existence=True) True - sage: _ = BlockDesign(85,BIBD_from_TD(85,5)) + sage: _ = designs.BlockDesign(85,BIBD_from_TD(85,5)) No idea:: @@ -638,7 +640,7 @@ def BIBD_from_PBD(PBD,v,k,check=True,base_cases={}): n = len(X) N = (k-1)*n+1 if not (n,k) in base_cases: - base_cases[n,k] = _relabel_bibd(balanced_incomplete_block_design(N,k).blcks,N) + base_cases[n,k] = _relabel_bibd(balanced_incomplete_block_design(N,k), N) for XX in base_cases[n,k]: if N-1 in XX: diff --git a/src/sage/combinat/designs/block_design.py b/src/sage/combinat/designs/block_design.py index 0d2ff31df64..041c13a3d33 100644 --- a/src/sage/combinat/designs/block_design.py +++ b/src/sage/combinat/designs/block_design.py @@ -1,23 +1,10 @@ """ Block designs. -A module to help with constructions and computations of block -designs and other incidence structures. - -A block design is an incidence structure consisting of a set of points `P` and a -set of blocks `B`, where each block is considered as a subset of `P`. More -precisely, a *block design* `B` is a class of `k`-element subsets of `P` such -that the number `r` of blocks that contain any point `x` in `P` is independent -of `x`, and the number `\lambda` of blocks that contain any given `t`-element -subset `T` is independent of the choice of `T` (see [1]_ for more). Such a block -design is also called a `t-(v,k,\lambda)`-design, and `v` (the number of -points), `b` (the number of blocks), `k`, `r`, and `\lambda` are the parameters -of the design. (In Python, ``lambda`` is reserved, so we sometimes use ``lmbda`` -or ``L`` instead.) - -In Sage, sets are replaced by (ordered) lists and the standard representation of -a block design uses `P = [0,1,..., v-1]`, so a block design is specified by -`(v,B)`. +A *block design* is a set together with a family of subsets (repeated subsets +are allowed) whose members are chosen to satisfy some set of properties that are +deemed useful for a particular application. See :wikipedia:`Block_design`. It is +an object equivalent to an incidence structure. REFERENCES: @@ -64,14 +51,17 @@ #*************************************************************************** from sage.modules.free_module import VectorSpace +from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ from sage.rings.arith import binomial, integer_floor -from sage.combinat.designs.incidence_structures import IncidenceStructure, IncidenceStructureFromMatrix +from incidence_structures import IncidenceStructure from sage.misc.decorators import rename_keyword from sage.rings.finite_rings.constructor import FiniteField from sage.categories.sets_cat import EmptySetError from sage.misc.unknown import Unknown +BlockDesign = IncidenceStructure + ### utility functions ------------------------------------------------------- def tdesign_params(t, v, k, L): @@ -81,7 +71,7 @@ def tdesign_params(t, v, k, L): EXAMPLES:: - sage: BD = BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) + sage: BD = designs.BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) sage: from sage.combinat.designs.block_design import tdesign_params sage: tdesign_params(2,7,3,1) (2, 7, 7, 3, 3, 1) @@ -94,7 +84,7 @@ def tdesign_params(t, v, k, L): r = integer_floor(L * x/y) return (t, v, b, r, k, L) -def ProjectiveGeometryDesign(n, d, F, algorithm=None): +def ProjectiveGeometryDesign(n, d, F, algorithm=None, check=True): """ Returns a projective geometry design. @@ -126,26 +116,22 @@ def ProjectiveGeometryDesign(n, d, F, algorithm=None): sage: designs.ProjectiveGeometryDesign(2, 1, GF(2)) Incidence structure with 7 points and 7 blocks sage: BD = designs.ProjectiveGeometryDesign(2, 1, GF(2), algorithm="gap") # optional - gap_packages (design package) - sage: BD.is_block_design() # optional - gap_packages (design package) - (True, [2, 7, 3, 1]) + sage: BD.is_t_design(return_parameters=True) # optional - gap_packages (design package) + (True, (2, 7, 3, 1)) """ q = F.order() - from sage.interfaces.gap import gap, GapElement - from sage.sets.set import Set if algorithm is None: V = VectorSpace(F, n+1) - points = list(V.subspaces(1)) - flats = list(V.subspaces(d+1)) - blcks = [] - for p in points: + points = {p:i for i,p in enumerate(V.subspaces(1))} + blocks = [] + for s in V.subspaces(d+1): b = [] - for i in range(len(flats)): - if p.is_subspace(flats[i]): - b.append(i) - blcks.append(b) - v = (q**(n+1)-1)/(q-1) - return BlockDesign(v, blcks, name="ProjectiveGeometryDesign") + for bb in s.subspaces(1): + b.append(points[bb]) + blocks.append(b) + return BlockDesign(len(points), blocks, name="ProjectiveGeometryDesign", check=check) if algorithm == "gap": # Requires GAP's Design + from sage.interfaces.gap import gap gap.load_package("design") gap.eval("D := PGPointFlatBlockDesign( %s, %s, %d )"%(n,q,d)) v = eval(gap.eval("D.v")) @@ -153,7 +139,7 @@ def ProjectiveGeometryDesign(n, d, F, algorithm=None): gB = [] for b in gblcks: gB.append([x-1 for x in b]) - return BlockDesign(v, gB, name="ProjectiveGeometryDesign") + return BlockDesign(v, gB, name="ProjectiveGeometryDesign", check=check) def DesarguesianProjectivePlaneDesign(n, check=True): r""" @@ -231,7 +217,7 @@ def DesarguesianProjectivePlaneDesign(n, check=True): # the line at infinity "z = 0" blcks.append(range(n2,n2+n+1)) - return BlockDesign(n2+n+1, blcks, name="Desarguesian projective plane of order %d"%n, test=check) + return BlockDesign(n2+n+1, blcks, name="Desarguesian projective plane of order %d"%n, check=check) def projective_plane_to_OA(pplane, pt=None, check=True): r""" @@ -254,12 +240,6 @@ def projective_plane_to_OA(pplane, pt=None, check=True): guys), you may want to disable it whenever you want speed. Set to ``True`` by default. - .. SEEALSO: - - The function :func:`OA_to_projective_plane` does the reverse operation. - For more on orthogonal arrays, you may have a look at - :func:`~sage.combinat.designs.orthogonal_arrays.orthogonal_array` - EXAMPLES:: sage: from sage.combinat.designs.block_design import projective_plane_to_OA @@ -284,7 +264,7 @@ def projective_plane_to_OA(pplane, pt=None, check=True): sage: _ = projective_plane_to_OA(pp, pt=7) """ from bibd import _relabel_bibd - pplane = pplane.blcks + pplane = pplane.blocks() n = len(pplane[0]) - 1 if pt is None: @@ -304,47 +284,6 @@ def projective_plane_to_OA(pplane, pt=None, check=True): return OA -def OA_to_projective_plane(OA, check=True): - r""" - Return the projective plane associated to an `OA(n+1,n,2)`. - - .. SEEALSO:: - - :func:`projective_plane_to_OA` for the function that goes the other way - around. - - EXAMPLES:: - - sage: from sage.combinat.designs.block_design import projective_plane_to_OA - sage: from sage.combinat.designs.block_design import OA_to_projective_plane - sage: p3 = designs.DesarguesianProjectivePlaneDesign(3) - sage: OA3 = projective_plane_to_OA(p3) - sage: OA_to_projective_plane(OA3) - Incidence structure with 13 points and 13 blocks - - sage: p4 = designs.DesarguesianProjectivePlaneDesign(4) - sage: OA4 = projective_plane_to_OA(p4) - sage: OA_to_projective_plane(OA4) - Incidence structure with 21 points and 21 blocks - """ - n = len(OA[0])-1 - n2 = n**2 - - assert len(OA) == n2, "the orthogonal array does not have parameters k=n+1,t=2" - - blcks = [] - - # add the n^2 lines that correspond to transversals - for l in OA: - blcks.append([i+(n+1)*j for i,j in enumerate(l)]) - - # add the n+1 lines that correspond to transversals - for i in xrange(n+1): - blcks.append(range(i*n, (i+1)*n)) - blcks[-1].append(n2+n) - - return BlockDesign(n2+n+1, blcks, name="Projective plane of order %d (built from an OA(%d,%d,2))"%(n,n+1,n), test=check) - def projective_plane(n, check=True, existence=False): r""" Returns a projective plane of order ``n`` as a 2-design. @@ -431,7 +370,6 @@ def projective_plane(n, check=True, existence=False): else: return DesarguesianProjectivePlaneDesign(n, check=check) - def AffineGeometryDesign(n, d, F): r""" Returns an Affine Geometry Design. @@ -462,27 +400,17 @@ def AffineGeometryDesign(n, d, F): EXAMPLES:: sage: BD = designs.AffineGeometryDesign(3, 1, GF(2)) - sage: BD.parameters(t=2) - (2, 8, 2, 1) - sage: BD.is_block_design() - (True, [2, 8, 2, 1]) + sage: BD.is_t_design(return_parameters=True) + (True, (2, 8, 2, 1)) sage: BD = designs.AffineGeometryDesign(3, 2, GF(2)) - sage: BD.parameters(t=3) - (3, 8, 4, 1) - sage: BD.is_block_design() - (True, [3, 8, 4, 1]) - - A 3-design:: - - sage: D = IncidenceStructure(range(32),designs.steiner_quadruple_system(32)) - sage: D.is_block_design() - (True, [3, 32, 4, 1]) + sage: BD.is_t_design(return_parameters=True) + (True, (3, 8, 4, 1)) With an integer instead of a Finite Field:: sage: BD = designs.AffineGeometryDesign(3, 2, 4) - sage: BD.parameters(t=2) - (2, 64, 16, 5) + sage: BD.is_t_design(return_parameters=True) + (True, (2, 64, 16, 5)) """ try: q = int(F) @@ -521,16 +449,13 @@ def WittDesign(n): EXAMPLES:: - sage: BD = designs.WittDesign(9) # optional - gap_packages (design package) - sage: BD.is_block_design() # optional - gap_packages (design package) - (True, [2, 9, 3, 1]) - sage: BD # optional - gap_packages (design package) + sage: BD = designs.WittDesign(9) # optional - gap_packages (design package) + sage: BD.is_t_design(return_parameters=True) # optional - gap_packages (design package) + (True, (2, 9, 3, 1)) + sage: BD # optional - gap_packages (design package) Incidence structure with 9 points and 12 blocks - sage: print BD # optional - gap_packages (design package) + sage: print BD # optional - gap_packages (design package) WittDesign - sage: BD = designs.WittDesign(12) # optional - gap_packages (design package) - sage: BD.is_block_design() # optional - gap_packages (design package) - (True, [5, 12, 6, 1]) """ from sage.interfaces.gap import gap, GapElement gap.load_package("design") @@ -540,7 +465,7 @@ def WittDesign(n): gB = [] for b in gblcks: gB.append([x-1 for x in b]) - return BlockDesign(v, gB, name="WittDesign", test=True) + return BlockDesign(v, gB, name="WittDesign", check=True) def HadamardDesign(n): """ @@ -586,7 +511,7 @@ def HadamardDesign(n): MS = J.parent() A = MS((H2+J)/2) # convert -1's to 0's; coerce entries to ZZ # A is the incidence matrix of the block design - return IncidenceStructureFromMatrix(A,name="HadamardDesign") + return IncidenceStructure(incidence_matrix=A,name="HadamardDesign") def Hadamard3Design(n): """ @@ -646,38 +571,4 @@ def Hadamard3Design(n): A1 = (H1+J)/2 A2 = (J-H1)/2 A = block_matrix(1, 2, [A1, A2]) #the incidence matrix of the design. - return IncidenceStructureFromMatrix(A, name="HadamardThreeDesign") - -def BlockDesign(max_pt, blks, name=None, test=True): - """ - Returns an instance of the :class:`IncidenceStructure` class. - - Requires each B in blks to be contained in range(max_pt). Does not check if - the result is a block design. - - EXAMPLES:: - - sage: BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]], name="Fano plane") - Incidence structure with 7 points and 7 blocks - sage: print BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]], name="Fano plane") - Fano plane - """ - nm = name - if nm is None and test: - nm = "BlockDesign" - BD = BlockDesign_generic( range(max_pt), blks, name=nm, test=test ) - if not test: - return BD - else: - pars = BD.parameters(t=2) - if BD.block_design_checker(pars[0],pars[1],pars[2],pars[3]): - return BD - else: - raise TypeError("parameters are not those of a block design.") - -# Possibly in the future there will be methods which apply to block designs and -# not incidence structures. None have been implemented yet though. The class -# name BlockDesign_generic is reserved in the name space in case more -# specialized methods are implemented later. In that case, BlockDesign_generic -# should inherit from IncidenceStructure. -BlockDesign_generic = IncidenceStructure + return IncidenceStructure(incidence_matrix=A, name="HadamardThreeDesign") diff --git a/src/sage/combinat/designs/design_catalog.py b/src/sage/combinat/designs/design_catalog.py index 8e725069e24..ffa0fdeecfa 100644 --- a/src/sage/combinat/designs/design_catalog.py +++ b/src/sage/combinat/designs/design_catalog.py @@ -65,7 +65,8 @@ .. [1] La Jolla Covering Repository, http://www.ccrwest.org/cover.html """ -from sage.combinat.designs.block_design import (ProjectiveGeometryDesign, +from sage.combinat.designs.block_design import (BlockDesign, + ProjectiveGeometryDesign, DesarguesianProjectivePlaneDesign, projective_plane, AffineGeometryDesign, @@ -84,6 +85,8 @@ from sage.combinat.designs.difference_family import difference_family +from sage.combinat.designs.incidence_structures import IncidenceStructure +BlockDesign = IncidenceStructure # just an alias from sage.combinat.designs.bibd import balanced_incomplete_block_design, steiner_triple_system # deprecated in june 2014 (#16446) diff --git a/src/sage/combinat/designs/ext_rep.py b/src/sage/combinat/designs/ext_rep.py index 6c1fc5a6d4d..1e0b1e75c8e 100644 --- a/src/sage/combinat/designs/ext_rep.py +++ b/src/sage/combinat/designs/ext_rep.py @@ -1016,8 +1016,10 @@ def designs_from_XML(fname): sage: d = BlockDesign(v, blocks) sage: d.blocks() [[0, 1], [0, 1]] - sage: d.parameters(t=2) - (2, 2, 2, 2) + sage: d.is_t_design(t=2) + True + sage: d.is_t_design(return_parameters=True) + (True, (2, 2, 2, 2)) """ proc = XTreeProcessor() diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index d9a99df92a9..8aeb9299faf 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -19,9 +19,7 @@ This is a significantly modified form of part of the module block_design.py (version 0.6) written by Peter Dobcsanyi peter@designtheory.org. - -Classes and methods -------------------- +- Vincent Delecroix (2014): major rewrite """ #*************************************************************************** # Copyright (C) 2007 # @@ -36,37 +34,18 @@ # http://www.gnu.org/licenses/ # #*************************************************************************** -from sage.rings.integer_ring import ZZ -from sage.rings.arith import binomial - -### utility functions ------------------------------------------------------- -def coordinatewise_product(L): - """ - Returns the coordinatewise product of a list of vectors. +from sage.misc.superseded import deprecated_function_alias +from sage.misc.cachefunc import cached_method - INPUT: - - - ``L`` is a list of `n`-vectors or lists all of length `n` with a common - parent. This returns the vector whose `i`-th coordinate is the product of - the `i`-th coordinates of the vectors. - - EXAMPLES:: - - sage: from sage.combinat.designs.incidence_structures import coordinatewise_product - sage: L = [[1,2,3],[-1,-1,-1],[5,7,11]] - sage: coordinatewise_product(L) - [-5, -14, -33] - """ - n = len(L[0]) - ans = [1]*n - for x in L: - ans = [ans[i]*x[i] for i in range(n)] - return ans +from sage.rings.all import ZZ +from sage.rings.integer import Integer def IncidenceStructureFromMatrix(M, name=None): """ - Builds and incidence structure from a matrix. + Deprecated function that builds an incidence structure from a matrix. + + You should now use ``designs.IncidenceStructure(incidence_matrix=M)``. INPUT: @@ -75,103 +54,204 @@ def IncidenceStructureFromMatrix(M, name=None): EXAMPLES:: - sage: from sage.combinat.designs.block_design import BlockDesign - sage: BD1 = BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) + sage: BD1 = designs.IncidenceStructure(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) sage: M = BD1.incidence_matrix() sage: BD2 = IncidenceStructureFromMatrix(M) + doctest:...: DeprecationWarning: IncidenceStructureFromMatrix is deprecated. + Please use designs.IncidenceStructure(incidence_matrix=M) instead. + See http://trac.sagemath.org/16553 for details. sage: BD1 == BD2 True """ - nm = name - v = len(M.rows()) - b = len(M.columns()) - #points = range(v) - blocks = [] - for i in range(b): - B = [] - for j in range(v): - if M[j, i] != 0: - B.append(j) - blocks.append(B) - return IncidenceStructure(range(v), blocks, name=nm) + from sage.misc.superseded import deprecation + deprecation(16553, 'IncidenceStructureFromMatrix is deprecated. Please use designs.IncidenceStructure(incidence_matrix=M) instead.') + return IncidenceStructure(incidence_matrix=M, name=name) class IncidenceStructure(object): - """ - This the base class for block designs. - """ - def __init__(self, pts, blks, inc_mat=None, name=None, test=True): - """ - INPUT: + r""" + A base class for incidence structure (or block design) with explicit ground + set and blocks. - - ``pts, blks`` -- a list of points, and a list of lists (list of blocks). + INPUT: - If each `B` in ``blks`` is contained in ``pts`` then the incidence - matrix ` inc_mat`` need not (and should not) be given. Otherwise, - ``inc_mat`` should be the ``ptsxblks`` `(0,1)`-matrix `A` for which - `A_i,j=1` iff ``blks[j]`` is incident with ``pts[i]``. + - ``points`` -- the underlying set. If it is an integer `v`, then the set is + considered to be `\{0, ..., v-1\}`. - - ``inc_mat`` (for giving the `(0,1)`-incidence matrix) + - ``blocks`` -- the blocks (might be any iterable) - - ``name`` (a string, such as "Fano plane"). + - ``incidence_matrix`` -- the incidence matrix - - ``test`` (boolean) - if ``True``, then each block must be a list of pts. + - ``name`` (a string, such as "Fano plane"). - EXAMPLES:: + - ``check`` -- whether to check the input - sage: IncidenceStructure(range(7),[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) - Incidence structure with 7 points and 7 blocks + - ``copy`` -- (use with caution) if set to ``False`` then ``blocks`` must be + a list of lists of integers. The list will not be copied but will be + modified (each block is sorted, and the whole list is sorted). + + EXAMPLES: + + An incidence structure can be constructed by giving the number of points and the list of + blocks:: + + sage: designs.IncidenceStructure(7, [[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) + Incidence structure with 7 points and 7 blocks + + Or by its adjacency matrix (a `\{0,1\}`-matrix in which rows are indexed by + points and columns by blocks):: + + sage: m = matrix([[0,1,0],[0,0,1],[1,0,1],[1,1,1]]) + sage: designs.IncidenceStructure(m) + Incidence structure with 4 points and 3 blocks + + The points need not be consecutive integers:: + + sage: V = [(0,'a'),(0,'b'),(1,'a'),(1,'b')] + sage: B = [(V[0],V[1],V[2]), (V[1],V[2]), (V[0],V[2])] + sage: I = designs.IncidenceStructure(V, B) + sage: I.points() + [(0, 'a'), (0, 'b'), (1, 'a'), (1, 'b')] + sage: I.blocks() + [[(0, 'a'), (0, 'b'), (1, 'a')], [(0, 'a'), (1, 'a')], [(0, 'b'), (1, 'a')]] + + The order of the points and blocks do not matter as they are sorted on input + (see :trac:`11333`):: - Points are sorted :: + sage: A = designs.IncidenceStructure([0,1,2], [[0],[0,2]]) + sage: B = designs.IncidenceStructure([1,0,2], [[0],[2,0]]) + sage: B == A + True + + sage: C = designs.BlockDesign(2, [[0], [1,0]]) + sage: D = designs.BlockDesign(2, [[0,1], [0]]) + sage: C == D + True + + If you care for speed, you can set ``copy`` to ``True``, but in that + case, your input must be a list of lists and the ground set must be `{0, + ..., v-1}`:: - sage: BD1 = IncidenceStructure([4,6,0,3,2,5,1],[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) - sage: BD1.points() - [0, 1, 2, 3, 4, 5, 6] + sage: blocks = [[0,1],[2,0],[1,2]] # a list of lists of integers + sage: I = designs.IncidenceStructure(3, blocks, copy=False) + sage: I.blocks(copy=False) is blocks + True + """ + def __init__(self, points=None, blocks=None, incidence_matrix=None, + name=None, check=True, test=None, copy=True): + r""" + TESTS:: - TESTS: + sage: designs.IncidenceStructure(3, [[4]]) + Traceback (most recent call last): + ... + ValueError: Block [4] not contained in the points - The following shows that :trac:`11333` is fixed. :: + sage: designs.IncidenceStructure(3, [[0,1],[0,2]], test=True) + doctest:...: DeprecationWarning: the keyword test is deprecated, + use check instead + See http://trac.sagemath.org/16553 for details. + Incidence structure with 3 points and 2 blocks - sage: A = IncidenceStructure([0,1],[[0]]) - sage: B = IncidenceStructure([1,0],[[0]]) - sage: B == A + sage: designs.IncidenceStructure(2, [[0,1,2,3,4,5]], test=False) + Incidence structure with 2 points and 1 blocks + + We avoid to convert to integers when the points are not (but compare + equal to integers because of coercion):: + + sage: V = GF(5) + sage: e0,e1,e2,e3,e4 = V + sage: [e0,e1,e2,e3,e4] == range(5) # coercion makes them equal True + sage: blocks = [[e0,e1,e2],[e0,e1],[e2,e4]] + sage: I = designs.IncidenceStructure(V, blocks) + sage: type(I.points()[0]) + + sage: type(I.blocks()[0][0]) + + """ + if test is not None: + from sage.misc.superseded import deprecation + deprecation(16553, "the keyword test is deprecated, use check instead") + check = test + + from sage.matrix.constructor import matrix + from sage.structure.element import Matrix + + if isinstance(points, Matrix): + incidence_matrix = points + points = None + + if points is None: + assert incidence_matrix is not None - REFERENCES: - - - E. Assmus, J. Key, Designs and their codes, CUP, 1992. - """ - bs = [] - self.pnts = pts - self.pnts.sort() - v, blocks = len(pts), blks - for block in blocks: - if test: - for x in block: - if not(x in self.pnts): - raise ValueError('Point %s is not in the base set.' % x) - try: - y = sorted(block[:]) - bs.append(y) - except Exception: - bs.append(block) - bs.sort(cmp) - self.v = v - self.blcks = bs - self.name = name - self._incidence_matrix = inc_mat + M = matrix(incidence_matrix) + v = M.nrows() + self._points = range(v) + self._point_to_index = None + self._blocks = sorted(M.nonzero_positions_in_column(i) for i in range(M.ncols())) + + else: + assert incidence_matrix is None + + if isinstance(points, (int,Integer)): + self._points = range(points) + self._point_to_index = None + else: + self._points = sorted(points) + if self._points == range(len(points)) and all(isinstance(x,(int,Integer)) for x in self._points): + self._point_to_index = None + else: + self._point_to_index = {e:i for i,e in enumerate(self._points)} + + if check: + for block in blocks: + if any(x not in self._points for x in block): + raise ValueError("Block {} not contained in the points".format(block)) + if len(block) != len(set(block)): + raise ValueError("Repeated element in block {}".format(block)) + + if self._point_to_index: + # translate everything to integers between 0 and v-1 + blocks = [sorted(self._point_to_index[e] for e in block) for block in blocks] + elif copy: + # create a new list made of sorted blocks + blocks = [sorted(block) for block in blocks] + else: + # sort the data but avoid copying it + for i in xrange(len(blocks)): + blocks[i].sort() + + blocks.sort() + self._blocks = blocks + + self._name = str(name) if name is not None else 'IncidenceStructure' def __iter__(self): """ Iterator over the blocks. - EXAMPLE:: + Note that it is faster to call for the method ``.blocks(copy=True)`` + (but in that case the output should not be modified). + + EXAMPLES:: sage: sts = designs.steiner_triple_system(9) sage: list(sts) - [[0, 1, 5], [0, 2, 4], [0, 3, 6], [0, 7, 8], [1, 2, 3], [1, 4, 7], [1, 6, 8], [2, 5, 8], [2, 6, 7], [3, 4, 8], [3, 5, 7], [4, 5, 6]] - """ - - return iter(self.blcks) + [[0, 1, 5], [0, 2, 4], [0, 3, 6], [0, 7, 8], [1, 2, 3], [1, 4, 7], + [1, 6, 8], [2, 5, 8], [2, 6, 7], [3, 4, 8], [3, 5, 7], [4, 5, 6]] + + sage: b = designs.IncidenceStructure('ab', ['a','ab']) + sage: it = iter(b) + sage: it.next() + ['a'] + sage: it.next() + ['a', 'b'] + """ + if self._point_to_index is None: + for b in self._blocks: yield b[:] + else: + for b in self._blocks: + yield [self._points[i] for i in b] def __repr__(self): """ @@ -179,13 +259,12 @@ def __repr__(self): EXAMPLES:: - sage: from sage.combinat.designs.block_design import BlockDesign - sage: BD = BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) + sage: BD = designs.IncidenceStructure(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) sage: BD Incidence structure with 7 points and 7 blocks """ - repr = 'Incidence structure with %s points and %s blocks' % (len(self.pnts), len(self.blcks)) - return repr + return 'Incidence structure with {} points and {} blocks'.format( + self.num_points(), self.num_blocks()) def __str__(self): """ @@ -193,185 +272,195 @@ def __str__(self): EXAMPLES:: - sage: from sage.combinat.designs.block_design import BlockDesign - sage: BD = BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) + sage: BD = designs.IncidenceStructure(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) sage: print BD - BlockDesign - sage: BD = IncidenceStructure(range(7),[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) + IncidenceStructure + sage: BD = designs.IncidenceStructure(range(7),[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) sage: print BD IncidenceStructure """ - if self.name: - repr = '%s' % (self.name, self.pnts, - self.blcks) - else: - repr = 'IncidenceStructure' % (self.pnts, - self.blcks) - return repr + return '{}'.format( + self._name, self.points(), self.blocks()) - def automorphism_group(self): + def __eq__(self, other): """ - Returns the subgroup of the automorphism group of the incidence graph - which respects the P B partition. It is (isomorphic to) the automorphism - group of the block design, although the degrees differ. + Returns true if their points and blocks are equal (and in the same + order). - EXAMPLES:: + We are extra careful in this method since not everything can be sorted! - sage: P = designs.DesarguesianProjectivePlaneDesign(2); P - Incidence structure with 7 points and 7 blocks - sage: G = P.automorphism_group() - sage: G.is_isomorphic(PGL(3,2)) - True - sage: G - Permutation Group with generators [(2,3)(4,5), (2,4)(3,5), (1,2)(4,6), (0,1)(4,5)] + TESTS:: - A non self-dual example:: + sage: blocks = [[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]] + sage: BD1 = designs.IncidenceStructure(7, blocks) + sage: M = BD1.incidence_matrix() + sage: BD2 = designs.IncidenceStructure(incidence_matrix=M) + sage: BD1 == BD2 + True - sage: from sage.combinat.designs.incidence_structures import IncidenceStructure - sage: IS = IncidenceStructure(range(4), [[0,1,2,3],[1,2,3]]) - sage: IS.automorphism_group().cardinality() - 6 - sage: IS.dual_design().automorphism_group().cardinality() - 1 + sage: e1 = frozenset([0,1]) + sage: e2 = frozenset([2]) + sage: sorted([e1,e2]) == [e1,e2] + True + sage: sorted([e2,e1]) == [e2,e1] + True + sage: I1 = designs.IncidenceStructure([e1,e2], [[e1],[e1,e2]]) + sage: I2 = designs.IncidenceStructure([e1,e2], [[e2,e1],[e1]]) + sage: I3 = designs.IncidenceStructure([e2,e1], [[e1,e2],[e1]]) + sage: I1 == I2 and I2 == I1 and I1 == I3 and I3 == I1 and I2 == I3 and I3 == I2 + True """ - from sage.groups.perm_gps.partn_ref.refinement_matrices import MatrixStruct - from sage.groups.perm_gps.permgroup import PermutationGroup - from sage.groups.perm_gps.permgroup_named import SymmetricGroup - M1 = self.incidence_matrix().transpose() - M2 = MatrixStruct(M1) - M2.run() - gens = M2.automorphism_group()[0] - return PermutationGroup(gens, domain=range(self.v)) + if not isinstance(other, IncidenceStructure): + return False - def block_design_checker(self, t, v, k, lmbda, type=None): - """ - This is *not* a wrapper for GAP Design's IsBlockDesign. The GAP - Design function IsBlockDesign - http://www.gap-system.org/Manuals/pkg/design/htm/CHAP004.htm - apparently simply checks the record structure and no mathematical - properties. Instead, the function below checks some necessary (but - not sufficient) "easy" identities arising from the identity. + if self._points == other._points: + return self._blocks == other._blocks - INPUT: + if (self.num_points() != other.num_points() or + self.num_blocks() != other.num_blocks()): + return False - - ``t`` - the t as in "t-design" + p_to_i = self._point_to_index if self._point_to_index else range(self.num_points()) - - ``v`` - the number of points + if any(p not in p_to_i for p in other.points()): + return False - - ``k`` - the number of blocks incident to a point + other_blocks = sorted(sorted(p_to_i[p] for p in b) for b in other.blocks()) + return self._blocks == other_blocks - - ``lmbda`` - each t-tuple of points should be incident with - lmbda blocks + def __ne__(self, other): + r""" + Difference test. - - ``type`` - can be 'simple' or 'binary' or 'connected' - Depending on the option, this wraps IsBinaryBlockDesign, - IsSimpleBlockDesign, or IsConnectedBlockDesign. + EXAMPLES:: - - Binary: no block has a repeated element. + sage: BD1 = designs.IncidenceStructure(7, [[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) + sage: M = BD1.incidence_matrix() + sage: BD2 = designs.IncidenceStructure(incidence_matrix=M) + sage: BD1 != BD2 + False + """ + return not self.__eq__(other) - - Simple: no block is repeated. + def points(self, copy=True): + r""" + Return the list of points. - - Connected: its incidence graph is a connected graph. + EXAMPLES:: - WARNING: This is very fast but can return false positives. + sage: designs.IncidenceStructure(3, [[0,1],[0,2]]).points() + [0, 1, 2] + """ + if copy: + return self._points[:] + return self._points + + def num_points(self): + r""" + The number of points in that design. EXAMPLES:: - sage: from sage.combinat.designs.block_design import BlockDesign - sage: BD = BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) - sage: BD.is_block_design() - (True, [2, 7, 3, 1]) - sage: BD.block_design_checker(2, 7, 3, 1) - True - sage: BD.block_design_checker(2, 7, 3, 1,"binary") - True - sage: BD.block_design_checker(2, 7, 3, 1,"connected") - True - sage: BD.block_design_checker(2, 7, 3, 1,"simple") - True + sage: designs.DesarguesianProjectivePlaneDesign(2).num_points() + 7 + sage: B = designs.IncidenceStructure(4, [[0,1],[0,2],[0,3],[1,2], [1,2,3]]) + sage: B.num_points() + 4 """ - from sage.sets.set import Set - if not(v == len(self.points())): - return False - b = lmbda*binomial(v, t)/binomial(k, t) - r = int(b*k/v) - if not(b == len(self.blocks())): - return False - if not(ZZ(v).divides(b*k)): - return False - A = self.incidence_matrix() - #k = sum(A.columns()[0]) - #r = sum(A.rows()[0]) - for i in range(b): - if not(sum(A.columns()[i]) == k): - return False - for i in range(v): - if not(sum(A.rows()[i]) == r): - return False - if type is None: - return True - if type == "binary": - for b in self.blocks(): - if len(b) != len(Set(b)): - return False - return True - if type == "simple": - B = self.blocks() - for b in B: - if B.count(b) > 1: - return False - return True - if type == "connected": - Gamma = self.incidence_graph() - if Gamma.is_connected(): - return True - else: - return False + return len(self._points) - def blocks(self): + def num_blocks(self): + r""" + The number of blocks. + + EXAMPLES:: + + sage: designs.DesarguesianProjectivePlaneDesign(2).num_blocks() + 7 + sage: B = designs.IncidenceStructure(4, [[0,1],[0,2],[0,3],[1,2], [1,2,3]]) + sage: B.num_blocks() + 5 + """ + return len(self._blocks) + + def blocks(self, copy=True): """ Return the list of blocks. + If ``copy`` is set to ``False`` then the output must not be modified as + it is a pointer to the internal list of this design. + EXAMPLES:: - sage: from sage.combinat.designs.block_design import BlockDesign - sage: BD = BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) + sage: BD = designs.IncidenceStructure(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) sage: BD.blocks() [[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]] + + What you should pay attention to:: + + sage: blocks = BD.blocks(copy=False) + sage: del blocks[0:6] + sage: BD + Incidence structure with 7 points and 1 blocks """ - B = sorted(self.blcks) - return B + if self._point_to_index is None: + if copy: + from copy import deepcopy + return deepcopy(self._blocks) + else: + return self._blocks + return [[self._points[i] for i in b] for b in self._blocks] - def __eq__(self, other): + def block_sizes(self): + r""" + Return the set of block sizes. + + EXAMPLES:: + + sage: BD = designs.IncidenceStructure(8, [[0,1,3],[1,4,5,6],[1,2],[5,6,7]]) + sage: BD.block_sizes() + [3, 2, 4, 3] + sage: BD = designs.IncidenceStructure(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) + sage: BD.block_sizes() + [3, 3, 3, 3, 3, 3, 3] """ - Returns true if their points and blocks are equal (resp.). + return map(len, self._blocks) + + def is_connected(self): + r""" + Test whether the design is connected. EXAMPLES:: - sage: from sage.combinat.designs.block_design import BlockDesign - sage: BD1 = BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) - sage: M = BD1.incidence_matrix() - sage: BD2 = IncidenceStructureFromMatrix(M) - sage: BD1 == BD2 + sage: designs.IncidenceStructure(3, [[0,1],[0,2]]).is_connected() True + sage: designs.IncidenceStructure(4, [[0,1],[2,3]]).is_connected() + False """ - bool1 = self.points() == other.points() - bool2 = self.blocks() == other.blocks() - return (bool1 and bool2) + return self.incidence_graph().is_connected() - def block_sizes(self): - """ - Return a list of block's sizes. + def is_simple(self): + r""" + Test whether this design is simple (i.e. no repeated block). EXAMPLES:: - sage: from sage.combinat.designs.block_design import BlockDesign - sage: BD = BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) - sage: BD.block_sizes() - [3, 3, 3, 3, 3, 3, 3] + sage: designs.IncidenceStructure(3, [[0,1],[1,2],[0,2]]).is_simple() + True + sage: designs.IncidenceStructure(3, [[0],[0]]).is_simple() + False + + sage: V = [(0,'a'),(0,'b'),(1,'a'),(1,'b')] + sage: B = [[V[0],V[1]], [V[1],V[2]]] + sage: I = designs.IncidenceStructure(V, B) + sage: I.is_simple() + True + sage: I2 = designs.IncidenceStructure(V, B*2) + sage: I2.is_simple() + False """ - self._block_sizes = map(len, self.blocks()) - return self._block_sizes + B = self._blocks + return all(B[i] != B[i+1] for i in xrange(len(B)-1)) def _gap_(self): """ @@ -379,23 +468,309 @@ def _gap_(self): EXAMPLES:: - sage: from sage.combinat.designs.block_design import BlockDesign - sage: BD = BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) + sage: BD = designs.IncidenceStructure(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) sage: BD._gap_() 'BlockDesign(7,[[1, 2, 3], [1, 4, 5], [1, 6, 7], [2, 4, 6], [2, 5, 7], [3, 4, 7], [3, 5, 6]])' """ B = self.blocks() - v = len(self.points()) - gB = [] - for b in B: - gB.append([x+1 for x in b]) + v = self.num_points() + gB = [[x+1 for x in b] for b in self._blocks] return "BlockDesign("+str(v)+","+str(gB)+")" - def dual_incidence_structure(self, algorithm=None): + def incidence_matrix(self): + r""" + Return the incidence matrix `A` of the design. A is a `(v \times b)` + matrix defined by: ``A[i,j] = 1`` if ``i`` is in block ``B_j`` and 0 + otherwise. + + EXAMPLES:: + + sage: BD = designs.IncidenceStructure(7, [[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) + sage: BD.block_sizes() + [3, 3, 3, 3, 3, 3, 3] + sage: BD.incidence_matrix() + [1 1 1 0 0 0 0] + [1 0 0 1 1 0 0] + [1 0 0 0 0 1 1] + [0 1 0 1 0 1 0] + [0 1 0 0 1 0 1] + [0 0 1 1 0 0 1] + [0 0 1 0 1 1 0] + + sage: I = designs.IncidenceStructure('abc', ('ab','abc','ac','c')) + sage: I.incidence_matrix() + [1 1 1 0] + [1 1 0 0] + [0 1 1 1] """ - Returns the dual of the incidence structure. + from sage.matrix.constructor import Matrix + from sage.rings.all import ZZ + A = Matrix(ZZ, self.num_points(), self.num_blocks(), sparse=True) + for j, b in enumerate(self._blocks): + for i in b: + A[i, j] = 1 + return A + + def incidence_graph(self): + """ + Returns the incidence graph of the design, where the incidence + matrix of the design is the adjacency matrix of the graph. + + EXAMPLE:: + + sage: BD = designs.IncidenceStructure(7, [[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) + sage: BD.incidence_graph() + Bipartite graph on 14 vertices + sage: A = BD.incidence_matrix() + sage: Graph(block_matrix([[A*0,A],[A.transpose(),A*0]])) == BD.incidence_graph() + True + + REFERENCE: + + - Sage Reference Manual on Graphs + """ + from sage.graphs.bipartite_graph import BipartiteGraph + A = self.incidence_matrix() + return BipartiteGraph(A) + + ##################### + # real computations # + ##################### + + def _t_design_parameters(self, t=None): + r""" + Return a 4-tuple `(t,v,k,l)` such that the design is a `t-(v,k,l)` + design with `t` maximum. If `t` is provided, then only check for + `t`-design. If ``self`` is not a `t`-design return ``None``. + + EXAMPLES:: + + Many affine geometry design are examples of `t`-designs:: + + sage: A = designs.AffineGeometryDesign(3, 1, GF(2)) + sage: A._t_design_parameters() + (2, 8, 2, 1) + sage: A = designs.AffineGeometryDesign(4, 2, GF(2)) + sage: A._t_design_parameters() + (3, 16, 4, 1) + + Bad cases:: + + sage: I = designs.IncidenceStructure(2, []) + sage: I._t_design_parameters() is None + True + + sage: I = designs.IncidenceStructure(2, [[0],[0,1]]) + sage: I._t_design_parameters() is None + True + """ + from sage.rings.arith import binomial + + v = self.num_points() + b = self.num_blocks() + + if v == 0 or b == 0: + return None + + k = len(self._blocks[0]) + if any(len(block) != k for block in self._blocks): + return None + + if t is not None and t > k: + return None + if k == v: + # this is the design (X, {X,X,X,...}) + if t is None: + return (v,v,v,b) + else: + return (t,v,v,b) + + # here we have at least a 0-design + if t == 0: + return (0,v,k,b) + elif k == 0: + # this is the design (X, {{},{},...}) + if t is None: + return (0,v,0,b) + else: + return None + + s = {} + for block in self._blocks: + for i in block: + s[i] = s.get(i,0) + 1 + K = set(s.values()) + if len(K) != 1: + if t is None or t == 0: + return (0,v,k,b) + else: + return None + l = K.pop() + + # here we have at least a 1-design + + if t == 1: + return (1,v,k,l) + if k == 1: + if t is None or t == 1: + return (1,v,1,l) + else: + return None + + # Handbook of combinatorial design theorem II.4.8: a t-(v,k,lambda) is + # also a s-(v,k,lambda_s) design with: + # lambda_s = lambda binomial(v-s,t-s) / binomial(k-s,t-s) + # so we check for increasing values of t whether we have a t-design + from itertools import combinations + for tt in (range(2,k+1) if t is None else [t]): + # is lambda an integer? + if (b*binomial(k,tt)) % binomial(v,tt) != 0: + tt -= 1 + break + + ll = b*binomial(k,tt) // binomial(v,tt) + s = {} + for block in self._blocks: + for i in combinations(block,tt): + s[i] = s.get(i,0) + 1 + K = set(s.values()) + if len(K) != 1: + tt -= 1 + break + l = ll + + if t is not None and tt != t: + return None + return (tt,v,k,l) + + def is_t_design(self, t=None, v=None, k=None, l=None, return_parameters=False): + """ + Test whether ``self`` is a ``t-(v,k,l)` design. + + A `t-(v,k,\lambda)` (sometimes called `t`-design for short) is a block + design in which: + - the underlying set has cardinality `v` + - the blocks have size `k` + - each `t`-subset of points is covered by `\lambda` blocks + + INPUT: + + - ``t``, ``v``, ``k``, ``l`` -- optional parameters + + - ``return_parameters`` -- whether to return the parameters of the + `t`-design + + EXAMPLES:: + + sage: fano_blocks = [[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]] + sage: BD = designs.BlockDesign(7, fano_blocks) + sage: BD.is_t_design() + True + sage: BD.is_t_design(return_parameters=True) + (True, (2, 7, 3, 1)) + sage: BD.is_t_design(2, 7, 3, 1) + True + sage: BD.is_t_design(1, 7, 3, 3) + True + sage: BD.is_t_design(0, 7, 3, 7) + True + + sage: BD.is_t_design(0,6,3,7) or BD.is_t_design(0,7,4,7) or BD.is_t_design(0,7,3,8) + False + + sage: BD = designs.AffineGeometryDesign(3, 1, GF(2)) + sage: BD.is_t_design(1) + True + sage: BD.is_t_design(2) + True + + Steiner triple and quadruple systems are other names for `2-(v,3,1)` and + `3-(v,4,1)` designs:: + + sage: S3_9 = designs.steiner_triple_system(9) + sage: S3_9.is_t_design(2,9,3,1) + True + + sage: blocks = designs.steiner_quadruple_system(8) + sage: S4_8 = designs.IncidenceStructure(8, blocks) + sage: S4_8.is_t_design(3,8,4,1) + True + + sage: blocks = designs.steiner_quadruple_system(14) + sage: S4_14 = designs.IncidenceStructure(14, blocks) + sage: S4_14.is_t_design(3,14,4,1) + True + + Some examples of Witt designs that need the gap database:: + + sage: BD = designs.WittDesign(9) # optional - gap_packages + sage: BD.is_t_design(2,9,3,1) # optional - gap_packages + True + sage: W12 = designs.WittDesign(12) # optional - gap_packages + sage: W12.is_t_design(5,12,6,1) # optional - gap_packages + True + sage: W12.is_t_design(4) # optional - gap_packages + True + + Further examples:: + + sage: D = designs.IncidenceStructure(4,[[],[]]) + sage: D.is_t_design(return_parameters=True) + (True, (0, 4, 0, 2)) + + sage: D = designs.IncidenceStructure(4, [[0,1],[0,2],[0,3]]) + sage: D.is_t_design(return_parameters=True) + (True, (0, 4, 2, 3)) + + sage: D = designs.IncidenceStructure(4, [[0],[1],[2],[3]]) + sage: D.is_t_design(return_parameters=True) + (True, (1, 4, 1, 1)) + + sage: D = designs.IncidenceStructure(4,[[0,1],[2,3]]) + sage: D.is_t_design(return_parameters=True) + (True, (1, 4, 2, 1)) + + sage: D = designs.IncidenceStructure(4, [range(4)]) + sage: D.is_t_design(return_parameters=True) + (True, (4, 4, 4, 1)) + + TESTS:: + + sage: blocks = designs.steiner_quadruple_system(8) + sage: S4_8 = designs.IncidenceStructure(8, blocks) + sage: R = range(15) + sage: [(v,k,l) for v in R for k in R for l in R if S4_8.is_t_design(3,v,k,l)] + [(8, 4, 1)] + sage: [(v,k,l) for v in R for k in R for l in R if S4_8.is_t_design(2,v,k,l)] + [(8, 4, 3)] + sage: [(v,k,l) for v in R for k in R for l in R if S4_8.is_t_design(1,v,k,l)] + [(8, 4, 7)] + sage: [(v,k,l) for v in R for k in R for l in R if S4_8.is_t_design(0,v,k,l)] + [(8, 4, 14)] + """ + res = self._t_design_parameters(t=t) + if res is None: + if return_parameters: + return False, (None,)*4 + else: + return False + + tt,vv,kk,ll = res + + if ((v is None or v == vv) and (k is None or k == kk) and (l is None or l == ll)): + if return_parameters: + return True, (tt,vv,kk,ll) + else: + return True + else: + if return_parameters: + return False, (None,)*4 + else: + return False - Note that the dual of a block design may not be a block design. + def dual(self, algorithm=None): + """ + Returns the dual of the incidence structure. INPUT: @@ -407,35 +782,30 @@ def dual_incidence_structure(self, algorithm=None): The ``algorithm="gap"`` option requires GAP's Design package (included in the gap_packages Sage spkg). - Also can be called with ``dual_design``. - EXAMPLES: The dual of a projective plane is a projective plane:: sage: PP = designs.DesarguesianProjectivePlaneDesign(4) - sage: PP.dual_design().is_block_design() - (True, [2, 21, 5, 1]) - sage: PP = designs.DesarguesianProjectivePlaneDesign(4) # optional - gap_packages - sage: PP.dual_design(algorithm="gap").is_block_design() # optional - gap_packages - (True, [2, 21, 5, 1]) + sage: PP.dual().is_t_design(return_parameters=True) + (True, (2, 21, 5, 1)) TESTS:: - sage: from sage.combinat.designs.block_design import BlockDesign - sage: D = BlockDesign(4, [[0,2],[1,2,3],[2,3]], test=False) + sage: D = designs.IncidenceStructure(4, [[0,2],[1,2,3],[2,3]]) sage: D Incidence structure with 4 points and 3 blocks - sage: D.dual_design() + sage: D.dual() Incidence structure with 3 points and 4 blocks - sage: print D.dual_design(algorithm="gap") # optional - gap_packages + sage: print D.dual(algorithm="gap") # optional - gap_packages IncidenceStructure - sage: BD = IncidenceStructure(range(7),[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]], name="FanoPlane") + sage: blocks = [[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]] + sage: BD = designs.IncidenceStructure(7, blocks, name="FanoPlane"); sage: BD Incidence structure with 7 points and 7 blocks - sage: print BD.dual_design(algorithm="gap") # optional - gap_packages + sage: print BD.dual(algorithm="gap") # optional - gap_packages IncidenceStructure - sage: BD.dual_incidence_structure() + sage: BD.dual() Incidence structure with 7 points and 7 blocks REFERENCE: @@ -453,219 +823,155 @@ def dual_incidence_structure(self, algorithm=None): gB = [] for b in gblcks: gB.append([x-1 for x in b]) - return IncidenceStructure(range(v), gB, name=None, test=False) + return IncidenceStructure(range(v), gB, name=None, check=False) else: - M = self.incidence_matrix() - new_blocks = [list(r.dict(copy=False)) for r in M.rows()] - return IncidenceStructure(range(M.ncols()), new_blocks, name=None, test=False) - - dual_design = dual_incidence_structure # to preserve standard terminology + return IncidenceStructure( + incidence_matrix=self.incidence_matrix().transpose(), + check=False) - def incidence_matrix(self): - """ - Return the incidence matrix `A` of the design. A is a `(v \times b)` - matrix defined by: ``A[i,j] = 1`` if ``i`` is in block ``B_j`` and 0 - otherwise. + def automorphism_group(self): + r""" + Returns the subgroup of the automorphism group of the incidence graph + which respects the P B partition. It is (isomorphic to) the automorphism + group of the block design, although the degrees differ. EXAMPLES:: - sage: BD = IncidenceStructure(range(7),[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) - sage: BD.block_sizes() - [3, 3, 3, 3, 3, 3, 3] - sage: BD.incidence_matrix() - [1 1 1 0 0 0 0] - [1 0 0 1 1 0 0] - [1 0 0 0 0 1 1] - [0 1 0 1 0 1 0] - [0 1 0 0 1 0 1] - [0 0 1 1 0 0 1] - [0 0 1 0 1 1 0] - """ - if not self._incidence_matrix is None: - return self._incidence_matrix - else: - from sage.matrix.constructor import Matrix - v = len(self.points()) - blks = self.blocks() - b = len(blks) - A = Matrix(ZZ, v, b, sparse=True) - for j, b in enumerate(blks): - for i in b: - A[i, j] = 1 - self._incidence_matrix = A - return A - - def incidence_graph(self): - """ - Returns the incidence graph of the design, where the incidence - matrix of the design is the adjacency matrix of the graph. - - EXAMPLE:: - - sage: BD = IncidenceStructure(range(7),[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) - sage: BD.incidence_graph() - Bipartite graph on 14 vertices - sage: A = BD.incidence_matrix() - sage: Graph(block_matrix([[A*0,A],[A.transpose(),A*0]])) == BD.incidence_graph() + sage: P = designs.DesarguesianProjectivePlaneDesign(2); P + Incidence structure with 7 points and 7 blocks + sage: G = P.automorphism_group() + sage: G.is_isomorphic(PGL(3,2)) True + sage: G + Permutation Group with generators [(2,3)(4,5), (2,4)(3,5), (1,2)(4,6), (0,1)(4,5)] - REFERENCE: + A non self-dual example:: - - Sage Reference Manual on Graphs - """ - from sage.graphs.bipartite_graph import BipartiteGraph - A = self.incidence_matrix() - return BipartiteGraph(A) + sage: IS = designs.IncidenceStructure(range(4), [[0,1,2,3],[1,2,3]]) + sage: IS.automorphism_group().cardinality() + 6 + sage: IS.dual().automorphism_group().cardinality() + 1 + + An example with points other than integers:: - def is_block_design(self, verbose=False): + sage: I = designs.IncidenceStructure('abc', ('ab','ac','bc')) + sage: I.automorphism_group() + Permutation Group with generators [('b','c'), ('a','b')] """ - Returns a pair ``True, pars`` if the incidence structure is a - `t`-design, for some `t`, where ``pars`` is the list of parameters `(t, - v, k, lmbda)`. The largest possible `t` is returned, provided `t=10`. + from sage.groups.perm_gps.partn_ref.refinement_matrices import MatrixStruct + from sage.groups.perm_gps.permgroup import PermutationGroup + from sage.groups.perm_gps.permgroup_named import SymmetricGroup + M1 = self.incidence_matrix().transpose() + M2 = MatrixStruct(M1) + M2.run() + gens = M2.automorphism_group()[0] + if self._point_to_index: + gens = [[self._points[i] for i in p] for p in gens] + return PermutationGroup(gens, domain=self._points) - INPUT: + ############### + # Deprecation # + ############### - - ``verbose`` (boolean) -- prints useful information when the answer is - negative. + def parameters(self): + r""" + Deprecated function. You should use :meth:`is_t_design` instead. EXAMPLES:: - sage: BD = IncidenceStructure(range(7),[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) - sage: BD.is_block_design() - (True, [2, 7, 3, 1]) - sage: BD.block_design_checker(2, 7, 3, 1) - True - sage: BD = designs.WittDesign(9) # optional - gap_packages (design package) - sage: BD.is_block_design() # optional - gap_packages (design package) - (True, [2, 9, 3, 1]) - sage: BD = designs.WittDesign(12) # optional - gap_packages (design package) - sage: BD.is_block_design() # optional - gap_packages (design package) - (True, [5, 12, 6, 1]) - sage: BD = designs.AffineGeometryDesign(3, 1, GF(2)) - sage: BD.is_block_design() - (True, [2, 8, 2, 1]) + sage: I = designs.IncidenceStructure('abc', ['ab','ac','bc']) + sage: I.parameters() + doctest:...: DeprecationWarning: .parameters() is deprecated. Use + `is_t_design` instead + See http://trac.sagemath.org/16553 for details. + (2, 3, 2, 1) """ - from sage.rings.arith import binomial - from itertools import combinations - v = len(self.points()) - b = len(self.blcks) - - # Definition and consistency of 'k' and 'r' - # - # r_list stores the degree of each point - k = len(self.blcks[0]) - r_list = [0]*v - for block in self.blcks: - if len(block) != k: - if verbose: - print "All blocks do not have the same size" - return False - for x in block: - r_list[x] += 1 - - r = r_list[0] - if any(x!=r for x in r_list): - if verbose: - print "All points do not have the same degree" - return False + from sage.misc.superseded import deprecation + deprecation(16553, ".parameters() is deprecated. Use `is_t_design` instead") + return self.is_t_design(return_parameters=True)[1] - # Definition and consistency of 'l' (lambda) and 't' - t_found_yet = False + dual_design = deprecated_function_alias(16553, dual) + dual_incidence_structure = deprecated_function_alias(16553, dual) - for t in range(2,min(v,k+1)): - # Is lambda an integer ? - if (b*binomial(k,t)) % binomial(v,t) == 0: - l = (b*binomial(k,t))/binomial(v,t) - else: - continue + def block_design_checker(self, t, v, k, lmbda, type=None): + """ + This method is deprecated and will soon be removed (see :trac:`16553`). + You could use :meth:`is_t_design` or :meth:`t_design_parameters` instead. - # Associates to every t-subset of [v] the number of its occurrences - # as a subset of a block - t_counts = {} - for block in self.blcks: - for t_set in combinations(sorted(block),t): - t_counts[t_set] = t_counts.get(t_set,0)+1 + This is *not* a wrapper for GAP Design's IsBlockDesign. The GAP + Design function IsBlockDesign + http://www.gap-system.org/Manuals/pkg/design/htm/CHAP004.htm + apparently simply checks the record structure and no mathematical + properties. Instead, the function below checks some necessary (but + not sufficient) "easy" identities arising from the identity. - # Checking the consistency of l - l_values = t_counts.values() + INPUT: - if all(l == x for x in l_values): - t_found_yet = True - t_lambda = t,l + - ``t`` - the t as in "t-design" - if t_found_yet: - t,l = t_lambda - return (True, [t,v,k,l]) - else: - return (False, [0,0,0,0]) + - ``v`` - the number of points - def parameters(self, t=None): - """ - Returns `(t,v,k,lambda)`. Does not check if the input is a block - design. + - ``k`` - the number of blocks incident to a point - INPUT: + - ``lmbda`` - each t-tuple of points should be incident with + lmbda blocks - - ``t`` -- `t` such that the design is a `t`-design. + - ``type`` - can be 'simple' or 'binary' or 'connected' + Depending on the option, this wraps IsBinaryBlockDesign, + IsSimpleBlockDesign, or IsConnectedBlockDesign. - EXAMPLES:: + - Binary: no block has a repeated element. - sage: from sage.combinat.designs.block_design import BlockDesign - sage: BD = BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]], name="FanoPlane") - sage: BD.parameters(t=2) - (2, 7, 3, 1) - sage: BD.parameters(t=3) - (3, 7, 3, 0) - """ - if t is None: - from sage.misc.superseded import deprecation - deprecation(15664, "the 't' argument will become mandatory soon. 2"+ - " is used when none is provided.") - t = 2 + - Simple: no block is repeated. - v = len(self.points()) - blks = self.blocks() - k = len(blks[int(0)]) - b = len(blks) - #A = self.incidence_matrix() - #r = sum(A.rows()[0]) - lmbda = int(b/(binomial(v, t)/binomial(k, t))) - return (t, v, k, lmbda) + - Connected: its incidence graph is a connected graph. - def points(self): - """ - Returns the list of points. + WARNING: This is very fast but can return false positives. EXAMPLES:: - sage: from sage.combinat.designs.block_design import BlockDesign - sage: BD = BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) - sage: BD.points() - [0, 1, 2, 3, 4, 5, 6] - """ - return self.pnts - - def points_from_gap(self): - """ - Literally pushes this block design over to GAP and returns the - points of that. Other than debugging, usefulness is unclear. + sage: BD = designs.IncidenceStructure(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) + sage: BD.is_t_design(return_parameters=True) + (True, (2, 7, 3, 1)) + sage: BD.block_design_checker(2, 7, 3, 1) + doctest:...: DeprecationWarning: .block_design_checker(v,t,k,lmbda) is deprecated; please use + .is_t_design(v,t,k,lmbda) instead + See http://trac.sagemath.org/16553 for details. + True - REQUIRES: GAP's Design package. + sage: BD.block_design_checker(2, 7, 3, 1,"binary") + doctest:1: DeprecationWarning: .block_design_checker(type='binary') is + deprecated; use .is_binary() instead + See http://trac.sagemath.org/16553 for details. + True - EXAMPLES:: + sage: BD.block_design_checker(2, 7, 3, 1,"connected") + doctest:1: DeprecationWarning: block_design_checker(type='connected') is + deprecated, please use .is_connected() instead + See http://trac.sagemath.org/16553 for details. + True - sage: from sage.combinat.designs.block_design import BlockDesign - sage: BD = BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) - sage: BD.points_from_gap() # optional - gap_packages (design package) - doctest:1: DeprecationWarning: Unless somebody protests this method will be removed, as nobody seems to know why it is there. - See http://trac.sagemath.org/14499 for details. - [1, 2, 3, 4, 5, 6, 7] + sage: BD.block_design_checker(2, 7, 3, 1,"simple") + doctest:1: DeprecationWarning: .block_design_checker(type='simple') + is deprecated; all designs here are simple! + See http://trac.sagemath.org/16553 for details. + True """ from sage.misc.superseded import deprecation - deprecation(14499, ('Unless somebody protests this method will be ' - 'removed, as nobody seems to know why it is there.')) - from sage.interfaces.gap import gap - gap.load_package("design") - gD = self._gap_() - gP = gap.eval("BlockDesignPoints("+gD+")").replace("..", ",") - return range(eval(gP)[0], eval(gP)[1]+1) + + ans = self.is_t_design(t,v,k,lmbda) + + if type is None: + deprecation(16553, ".block_design_checker(v,t,k,lmbda) is deprecated; please use .is_t_design(v,t,k,lmbda) instead") + return ans + + if type == "binary": + deprecation(16553, ".block_design_checker(type='binary') is deprecated; use .is_binary() instead") + return True + if type == "simple": + deprecation(16553, ".block_design_checker(type='simple') is deprecated; all designs here are simple!") + return True + if type == "connected": + deprecation(16553, "block_design_checker(type='connected') is deprecated, please use .is_connected() instead") + return self.incidence_graph().is_connected() From bd609fcef6ba2923a6659e7c2dd35edf828467cf Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 27 Jun 2014 22:10:44 +0200 Subject: [PATCH 433/546] trac #16553: is_t_design --- .../combinat/designs/incidence_structures.py | 213 ++++++++---------- 1 file changed, 89 insertions(+), 124 deletions(-) diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 8aeb9299faf..6395cc5311f 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -537,127 +537,24 @@ def incidence_graph(self): # real computations # ##################### - def _t_design_parameters(self, t=None): - r""" - Return a 4-tuple `(t,v,k,l)` such that the design is a `t-(v,k,l)` - design with `t` maximum. If `t` is provided, then only check for - `t`-design. If ``self`` is not a `t`-design return ``None``. - - EXAMPLES:: - - Many affine geometry design are examples of `t`-designs:: - - sage: A = designs.AffineGeometryDesign(3, 1, GF(2)) - sage: A._t_design_parameters() - (2, 8, 2, 1) - sage: A = designs.AffineGeometryDesign(4, 2, GF(2)) - sage: A._t_design_parameters() - (3, 16, 4, 1) - - Bad cases:: - - sage: I = designs.IncidenceStructure(2, []) - sage: I._t_design_parameters() is None - True - - sage: I = designs.IncidenceStructure(2, [[0],[0,1]]) - sage: I._t_design_parameters() is None - True - """ - from sage.rings.arith import binomial - - v = self.num_points() - b = self.num_blocks() - - if v == 0 or b == 0: - return None - - k = len(self._blocks[0]) - if any(len(block) != k for block in self._blocks): - return None - - if t is not None and t > k: - return None - if k == v: - # this is the design (X, {X,X,X,...}) - if t is None: - return (v,v,v,b) - else: - return (t,v,v,b) - - # here we have at least a 0-design - if t == 0: - return (0,v,k,b) - elif k == 0: - # this is the design (X, {{},{},...}) - if t is None: - return (0,v,0,b) - else: - return None - - s = {} - for block in self._blocks: - for i in block: - s[i] = s.get(i,0) + 1 - K = set(s.values()) - if len(K) != 1: - if t is None or t == 0: - return (0,v,k,b) - else: - return None - l = K.pop() - - # here we have at least a 1-design - - if t == 1: - return (1,v,k,l) - if k == 1: - if t is None or t == 1: - return (1,v,1,l) - else: - return None - - # Handbook of combinatorial design theorem II.4.8: a t-(v,k,lambda) is - # also a s-(v,k,lambda_s) design with: - # lambda_s = lambda binomial(v-s,t-s) / binomial(k-s,t-s) - # so we check for increasing values of t whether we have a t-design - from itertools import combinations - for tt in (range(2,k+1) if t is None else [t]): - # is lambda an integer? - if (b*binomial(k,tt)) % binomial(v,tt) != 0: - tt -= 1 - break - - ll = b*binomial(k,tt) // binomial(v,tt) - s = {} - for block in self._blocks: - for i in combinations(block,tt): - s[i] = s.get(i,0) + 1 - K = set(s.values()) - if len(K) != 1: - tt -= 1 - break - l = ll - - if t is not None and tt != t: - return None - return (tt,v,k,l) - def is_t_design(self, t=None, v=None, k=None, l=None, return_parameters=False): - """ - Test whether ``self`` is a ``t-(v,k,l)` design. + """Test whether ``self`` is a ``t-(v,k,l)` design. A `t-(v,k,\lambda)` (sometimes called `t`-design for short) is a block design in which: + - the underlying set has cardinality `v` - the blocks have size `k` - each `t`-subset of points is covered by `\lambda` blocks INPUT: - - ``t``, ``v``, ``k``, ``l`` -- optional parameters + - ``t,v,k,l`` (integers) -- their value is set to ``None`` by + default. The function tests whether the design is a ``t-(v,k,l)`` + design using the provided values. Note that `l`` cannot be specified + if ``t`` is not - - ``return_parameters`` -- whether to return the parameters of the + - ``return_parameters`` (boolean)-- whether to return the parameters of the `t`-design EXAMPLES:: @@ -747,26 +644,94 @@ def is_t_design(self, t=None, v=None, k=None, l=None, return_parameters=False): [(8, 4, 7)] sage: [(v,k,l) for v in R for k in R for l in R if S4_8.is_t_design(0,v,k,l)] [(8, 4, 14)] + sage: A = designs.AffineGeometryDesign(3, 1, GF(2)) + sage: A.is_t_design(return_parameters=True) + (True, (2, 8, 2, 1)) + sage: A = designs.AffineGeometryDesign(4, 2, GF(2)) + sage: A.is_t_design(return_parameters=True) + (True, (3, 16, 4, 1)) + sage: I = designs.IncidenceStructure(2, []) + sage: I.is_t_design(return_parameters=True) + (True, (0, 2, 0, 0)) + sage: I = designs.IncidenceStructure(2, [[0],[0,1]]) + sage: I.is_t_design(return_parameters=True) + (False, (0, 0, 0, 0)) + + """ - res = self._t_design_parameters(t=t) - if res is None: - if return_parameters: - return False, (None,)*4 + from sage.rings.arith import binomial + + # Missing parameters ? + if v is None: + v = self.num_points() + + if k is None: + k = len(self._blocks[0]) if self._blocks else 0 + + if l is not None and t is None: + raise ValueError("t must be set when l=None") + + b = self.num_blocks() + + # Trivial wrong answers + if (any(len(block) != k for block in self._blocks) or # non k-uniform + v != self.num_points()): + return (False, (0,0,0,0)) if return_parameters else False + + # Trivial case t>k + if (t is not None and t>k): + if (l is None or l == 0): + return (True, (t,v,k,0)) if return_parameters else True else: - return False + return (False, (0,0,0,0)) if return_parameters else False - tt,vv,kk,ll = res + # Trivial case k=0 + if k==0: + if (l is None or l == 0): + return (True, (0,v,k,b)) if return_parameters else True + else: + return (False, (0,0,0,0)) if return_parameters else False - if ((v is None or v == vv) and (k is None or k == kk) and (l is None or l == ll)): - if return_parameters: - return True, (tt,vv,kk,ll) + # Trivial case k=v (includes v=0) + if k == v: + if t is None: + t = v + if l is None or b == l: + return (True, (t,v,k,b)) if return_parameters else True else: - return True + return (True, (0,0,0,0)) if return_parameters else False + + # Handbook of combinatorial design theorem II.4.8: + # + # a t-(v,k,l') is also a t'-(v,k,l') + # for t' < t and l' = l* binomial(v-t',t-t') / binomial(k-t',t-t') + # + # We look for the largest t such that self is a t-design + from itertools import combinations + for tt in (range(1,k+1) if t is None else [t]): + # is lambda an integer? + if (b*binomial(k,tt)) % binomial(v,tt) != 0: + tt -= 1 + break + + s = {} + for block in self._blocks: + for i in combinations(block,tt): + s[i] = s.get(i,0) + 1 + + if len(set(s.values())) != 1: + tt -= 1 + break + + ll = b*binomial(k,tt) // binomial(v,tt) + + if ((t is not None and t!=tt) or + (l is not None and l!=ll)): + return (False, (0,0,0,0)) if return_parameters else False else: - if return_parameters: - return False, (None,)*4 - else: - return False + if tt == 0: + ll = b + return (True, (tt,v,k,ll)) if return_parameters else True def dual(self, algorithm=None): """ From 11994efa18b15a9a0330a0816c7aea24fb2bc498 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sat, 28 Jun 2014 22:45:07 +0200 Subject: [PATCH 434/546] trac #16553: Review --- .../combinat/designs/incidence_structures.py | 61 +++++++++++-------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 6395cc5311f..8ad60eacc9e 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -74,8 +74,8 @@ class IncidenceStructure(object): INPUT: - - ``points`` -- the underlying set. If it is an integer `v`, then the set is - considered to be `\{0, ..., v-1\}`. + - ``points`` -- the underlying set. If ``points`` is an integer `v`, then + the set is considered to be `\{0, ..., v-1\}`. - ``blocks`` -- the blocks (might be any iterable) @@ -87,12 +87,14 @@ class IncidenceStructure(object): - ``copy`` -- (use with caution) if set to ``False`` then ``blocks`` must be a list of lists of integers. The list will not be copied but will be - modified (each block is sorted, and the whole list is sorted). + modified in place (each block is sorted, and the whole list is + sorted). Your ``blocks`` object will become the + :class:`IncidenceStructure` instance's internal data. EXAMPLES: - An incidence structure can be constructed by giving the number of points and the list of - blocks:: + An incidence structure can be constructed by giving the number of points and + the list of blocks:: sage: designs.IncidenceStructure(7, [[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) Incidence structure with 7 points and 7 blocks @@ -104,7 +106,7 @@ class IncidenceStructure(object): sage: designs.IncidenceStructure(m) Incidence structure with 4 points and 3 blocks - The points need not be consecutive integers:: + The points can be any (hashable) object:: sage: V = [(0,'a'),(0,'b'),(1,'a'),(1,'b')] sage: B = [(V[0],V[1],V[2]), (V[1],V[2]), (V[0],V[2])] @@ -114,8 +116,8 @@ class IncidenceStructure(object): sage: I.blocks() [[(0, 'a'), (0, 'b'), (1, 'a')], [(0, 'a'), (1, 'a')], [(0, 'b'), (1, 'a')]] - The order of the points and blocks do not matter as they are sorted on input - (see :trac:`11333`):: + The order of the points and blocks does not matter as they are sorted on + input (see :trac:`11333`):: sage: A = designs.IncidenceStructure([0,1,2], [[0],[0,2]]) sage: B = designs.IncidenceStructure([1,0,2], [[0],[2,0]]) @@ -127,7 +129,7 @@ class IncidenceStructure(object): sage: C == D True - If you care for speed, you can set ``copy`` to ``True``, but in that + If you care for speed, you can set ``copy`` to ``False``, but in that case, your input must be a list of lists and the ground set must be `{0, ..., v-1}`:: @@ -144,7 +146,7 @@ def __init__(self, points=None, blocks=None, incidence_matrix=None, sage: designs.IncidenceStructure(3, [[4]]) Traceback (most recent call last): ... - ValueError: Block [4] not contained in the points + ValueError: Block [4] is not contained in the point set sage: designs.IncidenceStructure(3, [[0,1],[0,2]], test=True) doctest:...: DeprecationWarning: the keyword test is deprecated, @@ -206,7 +208,7 @@ def __init__(self, points=None, blocks=None, incidence_matrix=None, if check: for block in blocks: if any(x not in self._points for x in block): - raise ValueError("Block {} not contained in the points".format(block)) + raise ValueError("Block {} is not contained in the point set".format(block)) if len(block) != len(set(block)): raise ValueError("Repeated element in block {}".format(block)) @@ -218,8 +220,8 @@ def __init__(self, points=None, blocks=None, incidence_matrix=None, blocks = [sorted(block) for block in blocks] else: # sort the data but avoid copying it - for i in xrange(len(blocks)): - blocks[i].sort() + for b in blocks: + b.sort() blocks.sort() self._blocks = blocks @@ -284,10 +286,7 @@ def __str__(self): def __eq__(self, other): """ - Returns true if their points and blocks are equal (and in the same - order). - - We are extra careful in this method since not everything can be sorted! + Tests is the two incidence structures are equal TESTS:: @@ -310,6 +309,8 @@ def __eq__(self, other): sage: I1 == I2 and I2 == I1 and I1 == I3 and I3 == I1 and I2 == I3 and I3 == I2 True """ + # We are extra careful in this method since we cannot assume that a + # total order is defined on the point set. if not isinstance(other, IncidenceStructure): return False @@ -346,6 +347,12 @@ def points(self, copy=True): r""" Return the list of points. + INPUT: + + - ``copy`` (boolean) -- ``True`` by default. When set to ``False``, a + pointer toward the object's internal data is given. Set it to + ``False`` only if you know what you are doing. + EXAMPLES:: sage: designs.IncidenceStructure(3, [[0,1],[0,2]]).points() @@ -384,11 +391,13 @@ def num_blocks(self): return len(self._blocks) def blocks(self, copy=True): - """ - Return the list of blocks. + """Return the list of blocks. + + INPUT: - If ``copy`` is set to ``False`` then the output must not be modified as - it is a pointer to the internal list of this design. + - ``copy`` (boolean) -- ``True`` by default. When set to ``False``, a + pointer toward the object's internal data is given. Set it to + ``False`` only if you know what you are doing. EXAMPLES:: @@ -402,6 +411,7 @@ def blocks(self, copy=True): sage: del blocks[0:6] sage: BD Incidence structure with 7 points and 1 blocks + """ if self._point_to_index is None: if copy: @@ -551,11 +561,12 @@ def is_t_design(self, t=None, v=None, k=None, l=None, return_parameters=False): - ``t,v,k,l`` (integers) -- their value is set to ``None`` by default. The function tests whether the design is a ``t-(v,k,l)`` - design using the provided values. Note that `l`` cannot be specified - if ``t`` is not + design using the provided values and guesses the others. Note that + `l`` cannot be specified if ``t`` is not. - - ``return_parameters`` (boolean)-- whether to return the parameters of the - `t`-design + - ``return_parameters`` (boolean)-- whether to return the parameters of + the `t`-design. If set to ``True``, the function returns a pair + ``(boolean_answer,(t,v,k,l))``. EXAMPLES:: From fcd7ed2f4df85e79a60fec73a0cb367ab872eb89 Mon Sep 17 00:00:00 2001 From: Martin Albrecht Date: Sat, 28 Jun 2014 16:39:42 -0700 Subject: [PATCH 435/546] document behaviour if system is inconsistent --- .../polynomial/multi_polynomial_sequence.py | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index c0e7dc19dfb..9e74c3b76be 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -995,6 +995,21 @@ def eliminate_linear_variables(self, maxlength=Infinity, skip=None, return_reduc sage: R [a + b + d, c + d] + + If the input system is detected to be inconsistent then [1] is returned + and the list of reductors is empty:: + + sage: R. = BooleanPolynomialRing() + sage: S = Sequence([x*y*z+x*y+z*y+x*z, x+y+z+1, x+y+z]) + sage: S.eliminate_linear_variables() + [1] + + sage: R. = BooleanPolynomialRing() + sage: S = Sequence([x*y*z+x*y+z*y+x*z, x+y+z+1, x+y+z]) + sage: S.eliminate_linear_variables(return_reductors=True) + ([1], []) + + TESTS: The function should really dispose of linear equations (:trac:`13968`):: @@ -1019,19 +1034,12 @@ def eliminate_linear_variables(self, maxlength=Infinity, skip=None, return_reduc sage: f = a*d + a + b*d + c*d + 1 sage: Sequence([f, a + b*c + c+d + 1]).eliminate_linear_variables() [a*d + a + b*d + c*d + 1, a + b*c + c + d + 1] - + sage: B. = BooleanPolynomialRing() sage: f = a*d + a + b*d + c*d + 1 sage: Sequence([f, a + b*c + c+d + 1]).eliminate_linear_variables(use_polybori=True) [b*c*d + b*c + b*d + c + d] - This used to produce a SIGSEGV:: - - sage: R. = BooleanPolynomialRing() - sage: S = Sequence([x*y*z+x*y+z*y+x*z, x+y+z+1, x+y+z]) - sage: S.eliminate_linear_variables() - [1] - .. NOTE:: This is called "massaging" in [CBJ07]_. @@ -1350,4 +1358,3 @@ def weil_restriction(self): from sage.structure.sage_object import register_unpickle_override register_unpickle_override("sage.crypto.mq.mpolynomialsystem","MPolynomialSystem_generic", PolynomialSequence_generic) register_unpickle_override("sage.crypto.mq.mpolynomialsystem","MPolynomialRoundSystem_generic", PolynomialSequence_generic) - From 83de5c354949dbb67a6281306f05971c0777c739 Mon Sep 17 00:00:00 2001 From: Miguel Marco Date: Sun, 29 Jun 2014 04:08:37 +0200 Subject: [PATCH 436/546] Corrected license --- build/pkgs/tides/SPKG.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/tides/SPKG.txt b/build/pkgs/tides/SPKG.txt index eab13f8e65b..c4264bc81fd 100644 --- a/build/pkgs/tides/SPKG.txt +++ b/build/pkgs/tides/SPKG.txt @@ -6,7 +6,7 @@ TIDES is a library for integration of ODE's with high precission. == License == -GPLv3 +GPLv3+ == SPKG Maintainers == From a195985c0617bf14a5d644f5c065bea896340ce4 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sun, 29 Jun 2014 10:14:25 +0200 Subject: [PATCH 437/546] 8734: remove superfluous code --- src/sage/calculus/desolvers.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/calculus/desolvers.py b/src/sage/calculus/desolvers.py index ef5ff3b0ac9..243bfeb438e 100644 --- a/src/sage/calculus/desolvers.py +++ b/src/sage/calculus/desolvers.py @@ -426,8 +426,7 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) ivar_str=P(ivar).str() de00 = de00.str() def sanitize_var(exprs): - t = exprs.replace("'"+dvar_str+"(_SAGE_VAR_"+ivar_str+")",dvar_str) - return t.replace("'"+dvar_str+"("+ivar_str+")",dvar_str) + return exprs.replace("'"+dvar_str+"("+ivar_str+")",dvar_str) de0 = sanitize_var(de00) ode_solver="ode2" cmd="(TEMP:%s(%s,%s,%s), if TEMP=false then TEMP else substitute(%s=%s(%s),TEMP))"%(ode_solver,de0,dvar_str,ivar_str,dvar_str,dvar_str,ivar_str) @@ -663,8 +662,7 @@ def desolve_laplace(de, dvar, ics=None, ivar=None): dvar_str = str(dvar) def sanitize_var(exprs): # 'y(x) -> y(x) - t = exprs.replace("'"+dvar_str,dvar_str) - return t.replace("'_SAGE_VAR_"+dvar_str,dvar_str) + return exprs.replace("'"+dvar_str,dvar_str) de0=de._maxima_() P = de0.parent() i = dvar_str.find('(') From e7a97ea3ca390627aed24693e5314568234dfdf3 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 29 Jun 2014 01:27:13 +0200 Subject: [PATCH 438/546] trac #16553: doc fix + deprecation is_block_design --- .../combinat/designs/incidence_structures.py | 10 +++++----- src/sage/matroids/catalog.py | 18 ++++++------------ 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 8ad60eacc9e..c6abf5dfccd 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -548,7 +548,8 @@ def incidence_graph(self): ##################### def is_t_design(self, t=None, v=None, k=None, l=None, return_parameters=False): - """Test whether ``self`` is a ``t-(v,k,l)` design. + r""" + Test whether ``self`` is a `t-(v,k,l)` design. A `t-(v,k,\lambda)` (sometimes called `t`-design for short) is a block design in which: @@ -571,7 +572,7 @@ def is_t_design(self, t=None, v=None, k=None, l=None, return_parameters=False): EXAMPLES:: sage: fano_blocks = [[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]] - sage: BD = designs.BlockDesign(7, fano_blocks) + sage: BD = designs.IncidenceStructure(7, fano_blocks) sage: BD.is_t_design() True sage: BD.is_t_design(return_parameters=True) @@ -667,8 +668,6 @@ def is_t_design(self, t=None, v=None, k=None, l=None, return_parameters=False): sage: I = designs.IncidenceStructure(2, [[0],[0,1]]) sage: I.is_t_design(return_parameters=True) (False, (0, 0, 0, 0)) - - """ from sage.rings.arith import binomial @@ -714,7 +713,7 @@ def is_t_design(self, t=None, v=None, k=None, l=None, return_parameters=False): # Handbook of combinatorial design theorem II.4.8: # - # a t-(v,k,l') is also a t'-(v,k,l') + # a t-(v,k,l) is also a t'-(v,k,l') # for t' < t and l' = l* binomial(v-t',t-t') / binomial(k-t',t-t') # # We look for the largest t such that self is a t-design @@ -869,6 +868,7 @@ def parameters(self): dual_design = deprecated_function_alias(16553, dual) dual_incidence_structure = deprecated_function_alias(16553, dual) + is_block_design = deprecated_function_alias(16553, is_t_design) def block_design_checker(self, t, v, k, lmbda, type=None): """ diff --git a/src/sage/matroids/catalog.py b/src/sage/matroids/catalog.py index e233b51ac27..ca352a47997 100644 --- a/src/sage/matroids/catalog.py +++ b/src/sage/matroids/catalog.py @@ -1342,12 +1342,9 @@ def Block_9_4(): sage: M = matroids.named_matroids.Block_9_4() sage: M.is_valid() # long time True - sage: C = M.nonspanning_circuits() - sage: D = {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 5, 'g': 6, - ....: 'h': 7, 'i': 8} - sage: B = [[D[x] for x in L] for L in C] - sage: BlockDesign(9, B).is_block_design() - (True, [2, 9, 4, 3]) + sage: BD = designs.BlockDesign(M.groundset(), M.nonspanning_circuits()) + sage: BD.is_t_design(return_parameters=True) + (True, (2, 9, 4, 3)) """ E = 'abcdefghi' CC = { @@ -1369,12 +1366,9 @@ def Block_10_5(): sage: M = matroids.named_matroids.Block_10_5() sage: M.is_valid() # long time True - sage: C = M.nonspanning_circuits() - sage: D = {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 5, 'g': 6, - ....: 'h': 7, 'i': 8, 'j': 9} - sage: B = [[D[x] for x in L] for L in C] - sage: BlockDesign(10, B).is_block_design() - (True, [3, 10, 5, 3]) + sage: BD = designs.BlockDesign(M.groundset(), M.nonspanning_circuits()) + sage: BD.is_t_design(return_parameters=True) + (True, (3, 10, 5, 3)) """ E = 'abcdefghij' From 3c0dd71a1ecaf61af062ff7ddedf4e95f38d5b22 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 29 Jun 2014 12:24:55 +0200 Subject: [PATCH 439/546] trac #16553v3: change .points() -> .ground_set() --- src/sage/combinat/designs/covering_design.py | 2 +- .../combinat/designs/incidence_structures.py | 23 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/sage/combinat/designs/covering_design.py b/src/sage/combinat/designs/covering_design.py index 78d21fe2ebf..db1bec0f030 100644 --- a/src/sage/combinat/designs/covering_design.py +++ b/src/sage/combinat/designs/covering_design.py @@ -439,7 +439,7 @@ def incidence_structure(self): sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: D = C.incidence_structure() - sage: D.points() + sage: D.ground_set() [0, 1, 2, 3, 4, 5, 6] sage: D.blocks() [[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]] diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index c6abf5dfccd..4f08530e83a 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -111,7 +111,7 @@ class IncidenceStructure(object): sage: V = [(0,'a'),(0,'b'),(1,'a'),(1,'b')] sage: B = [(V[0],V[1],V[2]), (V[1],V[2]), (V[0],V[2])] sage: I = designs.IncidenceStructure(V, B) - sage: I.points() + sage: I.ground_set() [(0, 'a'), (0, 'b'), (1, 'a'), (1, 'b')] sage: I.blocks() [[(0, 'a'), (0, 'b'), (1, 'a')], [(0, 'a'), (1, 'a')], [(0, 'b'), (1, 'a')]] @@ -166,7 +166,7 @@ def __init__(self, points=None, blocks=None, incidence_matrix=None, True sage: blocks = [[e0,e1,e2],[e0,e1],[e2,e4]] sage: I = designs.IncidenceStructure(V, blocks) - sage: type(I.points()[0]) + sage: type(I.ground_set()[0]) sage: type(I.blocks()[0][0]) @@ -282,7 +282,7 @@ def __str__(self): IncidenceStructure """ return '{}'.format( - self._name, self.points(), self.blocks()) + self._name, self.ground_set(), self.blocks()) def __eq__(self, other): """ @@ -323,7 +323,7 @@ def __eq__(self, other): p_to_i = self._point_to_index if self._point_to_index else range(self.num_points()) - if any(p not in p_to_i for p in other.points()): + if any(p not in p_to_i for p in other.ground_set()): return False other_blocks = sorted(sorted(p_to_i[p] for p in b) for b in other.blocks()) @@ -343,9 +343,9 @@ def __ne__(self, other): """ return not self.__eq__(other) - def points(self, copy=True): + def ground_set(self, copy=True): r""" - Return the list of points. + Return the ground set (i.e the list of points). INPUT: @@ -355,7 +355,7 @@ def points(self, copy=True): EXAMPLES:: - sage: designs.IncidenceStructure(3, [[0,1],[0,2]]).points() + sage: designs.IncidenceStructure(3, [[0,1],[0,2]]).ground_set() [0, 1, 2] """ if copy: @@ -413,13 +413,14 @@ def blocks(self, copy=True): Incidence structure with 7 points and 1 blocks """ - if self._point_to_index is None: - if copy: + if copy: + if self._point_to_index is None: from copy import deepcopy return deepcopy(self._blocks) else: - return self._blocks - return [[self._points[i] for i in b] for b in self._blocks] + return [[self._points[i] for i in b] for b in self._blocks] + else: + return self._blocks def block_sizes(self): r""" From 56042dcd69a3beaa1536c449682d5e95dfaf2f80 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 29 Jun 2014 14:46:50 +0200 Subject: [PATCH 440/546] trac #16147: fix .abs() for elt of embedded number field --- .../number_field/number_field_element.pyx | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index ab8209e78d5..8548fce3373 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -891,12 +891,16 @@ cdef class NumberFieldElement(FieldElement): sage: a.abs(prec=128) 1.2599210498948731647672106072782283506 """ - return self.abs(prec=53, i=0) + return self.abs(prec=53, i=None) - def abs(self, prec=53, i=0): + def abs(self, prec=53, i=None): r""" - Return the absolute value of this element with respect to the - `i`-th complex embedding of parent, to the given precision. + Return the absolute value of this element. + + If ``i`` is provided, then the absolute of the `i`-th embedding is + given. Otherwise, if the number field as a defined embedding into `\CC` + then the corresponding absolute value is returned and if there is none, + it corresponds to the choice ``i=0``. If prec is 53 (the default), then the complex double field is used; otherwise the arbitrary precision (but slow) complex @@ -938,9 +942,24 @@ cdef class NumberFieldElement(FieldElement): 0.414213562373095 sage: a.abs(i=1) 2.41421356237309 + + Check that :trac:`16147` is fixed:: + + sage: x = polygen(ZZ) + sage: f = x^3 - x - 1 + sage: beta = f.complex_roots()[0]; beta + 1.32471795724475 + sage: K. = NumberField(f, embedding=beta) + sage: b.abs() + 1.32471795724475 """ - P = self.number_field().complex_embeddings(prec)[i] - return abs(P(self)) + CCprec = ComplexField(prec) + if i is None and CCprec.has_coerce_map_from(self.parent()): + return CCprec(self).abs() + else: + i = 0 if i is None else i + P = self.number_field().complex_embeddings(prec)[i] + return P(self).abs() def abs_non_arch(self, P, prec=None): r""" From b124959527d51806a30b2e44d3038547e04f2a90 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 29 Jun 2014 07:53:27 -0500 Subject: [PATCH 441/546] Added patch file to lrcalc. --- .../lrcalc/patches/lrcalc-1.1.7-jump.patch | 138 ++++++++++++++++++ build/pkgs/lrcalc/spkg-install | 9 ++ 2 files changed, 147 insertions(+) create mode 100644 build/pkgs/lrcalc/patches/lrcalc-1.1.7-jump.patch diff --git a/build/pkgs/lrcalc/patches/lrcalc-1.1.7-jump.patch b/build/pkgs/lrcalc/patches/lrcalc-1.1.7-jump.patch new file mode 100644 index 00000000000..d21eeaefa45 --- /dev/null +++ b/build/pkgs/lrcalc/patches/lrcalc-1.1.7-jump.patch @@ -0,0 +1,138 @@ +diff -Naur lrcalc-sage-1.1.7-orig/lrcoef/coprod.c lrcalc-sage-1.1.7/lrcoef/coprod.c +--- lrcalc-sage-1.1.7-orig/lrcoef/coprod.c 2013-06-09 16:12:42.000000000 +1200 ++++ lrcalc-sage-1.1.7/lrcoef/coprod.c 2014-06-29 09:51:37.000000000 +1200 +@@ -11,7 +11,7 @@ + #include + + #include "symfcn.h" +- ++#include "lrcalc_jump.h" + + void print_usage() + { +diff -Naur lrcalc-sage-1.1.7-orig/lrcoef/lrcalc.c lrcalc-sage-1.1.7/lrcoef/lrcalc.c +--- lrcalc-sage-1.1.7-orig/lrcoef/lrcalc.c 2013-06-09 16:12:42.000000000 +1200 ++++ lrcalc-sage-1.1.7/lrcoef/lrcalc.c 2014-06-29 09:52:25.000000000 +1200 +@@ -12,7 +12,7 @@ + + #include "symfcn.h" + #include "maple.h" +- ++#include "lrcalc_jump.h" + + #define MULT_USAGE \ + "lrcalc mult [-mz] [-r rows] [-q rows,cols] [-f rows,level] part1 - part2\n" +diff -Naur lrcalc-sage-1.1.7-orig/lrcoef/lrcoef.c lrcalc-sage-1.1.7/lrcoef/lrcoef.c +--- lrcalc-sage-1.1.7-orig/lrcoef/lrcoef.c 2013-06-09 16:12:42.000000000 +1200 ++++ lrcalc-sage-1.1.7/lrcoef/lrcoef.c 2014-06-29 09:52:50.000000000 +1200 +@@ -14,7 +14,7 @@ + + #include "symfcn.h" + #include "maple.h" +- ++#include "lrcalc_jump.h" + + void print_usage() + { +diff -Naur lrcalc-sage-1.1.7-orig/lrcoef/lrskew.c lrcalc-sage-1.1.7/lrcoef/lrskew.c +--- lrcalc-sage-1.1.7-orig/lrcoef/lrskew.c 2013-06-09 16:12:42.000000000 +1200 ++++ lrcalc-sage-1.1.7/lrcoef/lrskew.c 2014-06-29 09:53:16.000000000 +1200 +@@ -11,7 +11,7 @@ + #include + + #include "symfcn.h" +- ++#include "lrcalc_jump.h" + + void print_usage() + { +diff -Naur lrcalc-sage-1.1.7-orig/lrcoef/mult.c lrcalc-sage-1.1.7/lrcoef/mult.c +--- lrcalc-sage-1.1.7-orig/lrcoef/mult.c 2013-06-09 16:12:42.000000000 +1200 ++++ lrcalc-sage-1.1.7/lrcoef/mult.c 2014-06-29 09:53:45.000000000 +1200 +@@ -12,7 +12,7 @@ + + #include "symfcn.h" + #include "maple.h" +- ++#include "lrcalc_jump.h" + + void print_usage() + { +diff -Naur lrcalc-sage-1.1.7-orig/lrcoef/sat.c lrcalc-sage-1.1.7/lrcoef/sat.c +--- lrcalc-sage-1.1.7-orig/lrcoef/sat.c 2013-06-09 16:12:42.000000000 +1200 ++++ lrcalc-sage-1.1.7/lrcoef/sat.c 2014-06-29 09:54:08.000000000 +1200 +@@ -10,7 +10,7 @@ + #include + + #include "symfcn.h" +- ++#include "lrcalc_jump.h" + + #define NUM_PRIMES 50 + +diff -Naur lrcalc-sage-1.1.7-orig/lrcoef/skew.c lrcalc-sage-1.1.7/lrcoef/skew.c +--- lrcalc-sage-1.1.7-orig/lrcoef/skew.c 2013-06-09 16:12:42.000000000 +1200 ++++ lrcalc-sage-1.1.7/lrcoef/skew.c 2014-06-29 09:54:40.000000000 +1200 +@@ -13,7 +13,7 @@ + + #include "symfcn.h" + #include "maple.h" +- ++#include "lrcalc_jump.h" + + void print_usage() + { +diff -Naur lrcalc-sage-1.1.7-orig/mathlib/alloc.c lrcalc-sage-1.1.7/mathlib/alloc.c +--- lrcalc-sage-1.1.7-orig/mathlib/alloc.c 2013-06-09 16:12:17.000000000 +1200 ++++ lrcalc-sage-1.1.7/mathlib/alloc.c 2014-06-29 09:55:08.000000000 +1200 +@@ -8,6 +8,7 @@ + #include + + #include "alloc.h" ++#include "lrcalc_jump.h" + + #if 0 + #define DEBUG_MEMORY_PRINT +diff -Naur lrcalc-sage-1.1.7-orig/mathlib/alloc.h lrcalc-sage-1.1.7/mathlib/alloc.h +--- lrcalc-sage-1.1.7-orig/mathlib/alloc.h 2013-06-09 16:12:17.000000000 +1200 ++++ lrcalc-sage-1.1.7/mathlib/alloc.h 2014-06-29 09:50:13.000000000 +1200 +@@ -2,13 +2,6 @@ + #define _ALLOC_H + + #include +-#include +- +-/* Programs using the lrcalc library should set lrcalc_panic_frame +- * with setjmp(lrcalc_panic_frame). The lrcalc library will call +- * longjmp(lrcalc_panic_frame, 1) if an "out of memory" event occurs. +- */ +-jmp_buf lrcalc_panic_frame; + + void *amalloc(size_t size); + void *acalloc(size_t num, size_t size); +diff -Naur lrcalc-sage-1.1.7-orig/mathlib/lrcalc_jump.h lrcalc-sage-1.1.7/mathlib/lrcalc_jump.h +--- lrcalc-sage-1.1.7-orig/mathlib/lrcalc_jump.h 1970-01-01 12:00:00.000000000 +1200 ++++ lrcalc-sage-1.1.7/mathlib/lrcalc_jump.h 2014-06-29 09:49:42.000000000 +1200 +@@ -0,0 +1,11 @@ ++#ifndef _JUMP_H ++ ++#include ++ ++/* Programs using the lrcalc library should set lrcalc_panic_frame ++ * with setjmp(lrcalc_panic_frame). The lrcalc library will call ++ * longjmp(lrcalc_panic_frame, 1) if an "out of memory" event occurs. ++ */ ++jmp_buf lrcalc_panic_frame; ++ ++#endif +diff -Naur lrcalc-sage-1.1.7-orig/mathlib/salloc.c lrcalc-sage-1.1.7/mathlib/salloc.c +--- lrcalc-sage-1.1.7-orig/mathlib/salloc.c 2013-06-09 16:12:17.000000000 +1200 ++++ lrcalc-sage-1.1.7/mathlib/salloc.c 2014-06-29 09:55:34.000000000 +1200 +@@ -13,6 +13,7 @@ + #include + + #include "alloc.h" ++#include "lrcalc_jump.h" + + typedef struct mlink { + struct mlink *next; diff --git a/build/pkgs/lrcalc/spkg-install b/build/pkgs/lrcalc/spkg-install index b074fba4875..ffc3856777d 100755 --- a/build/pkgs/lrcalc/spkg-install +++ b/build/pkgs/lrcalc/spkg-install @@ -8,6 +8,15 @@ fi cd src +for patch in ../patches/*.patch; do + [ -r "$patch" ] || continue # Skip non-existing or non-readable patches + patch -p1 <"$patch" + if [ $? -ne 0 ]; then + echo >&2 "Error applying '$patch'" + exit 1 + fi +done + ./configure --prefix="$SAGE_LOCAL" if [ $? -ne 0 ]; then echo "Error configuring lrcalc." From 81d8722eb2579cbcb09ab0ef28436988801950fa Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Sun, 29 Jun 2014 20:05:03 +0200 Subject: [PATCH 442/546] FSMState.__copy__: copy final_word_out and color In contrast to FSMState.__deepcopy__, FSMState.__copy__ omitted copying the attributes final_word_out and color. This is now fixed. --- src/sage/combinat/finite_state_machine.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 30b80ea6661..df303a34584 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -1161,7 +1161,9 @@ def __copy__(self): 'A' """ new = FSMState(self.label(), self.word_out, - self.is_initial, self.is_final) + self.is_initial, self.is_final, + color=self.color, + final_word_out=self.final_word_out) if hasattr(self, 'hook'): new.hook = self.hook return new From 58ca5ccd0b1aa87fc721a5c533c2c26c03caa92a Mon Sep 17 00:00:00 2001 From: Miguel Marco Date: Sun, 29 Jun 2014 20:38:55 +0200 Subject: [PATCH 443/546] solved function called with [] as parameter --- src/sage/interfaces/tides.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sage/interfaces/tides.py b/src/sage/interfaces/tides.py index bb60c3c1d52..422e7ae98a3 100644 --- a/src/sage/interfaces/tides.py +++ b/src/sage/interfaces/tides.py @@ -603,7 +603,7 @@ def genfiles_mintides(integrator, driver, f, ics, initial, final, delta, outfile.close() def genfiles_mpfr(integrator, driver, f, ics, initial, final, delta, - parameters=[], parameter_values =[], dig = 20, tolrel=1e-16, + parameters = None , parameter_values = None, dig = 20, tolrel=1e-16, tolabs=1e-16, output = ''): r""" Generate the needed files for the mpfr module of the tides library. @@ -691,6 +691,10 @@ def genfiles_mpfr(integrator, driver, f, ics, initial, final, delta, """ + if parameters == None: + parameters = [] + if parameter_values == None: + parameter_values = [] from sage.misc.misc import SAGE_ROOT RR = RealField() l1, l2 = subexpressions_list(f, parameters) From 58b9c46c732978aa1b82ba4f15f31919d8f3f3c3 Mon Sep 17 00:00:00 2001 From: Niles Johnson Date: Mon, 30 Jun 2014 14:15:08 -0400 Subject: [PATCH 444/546] minimal fix to restore functionality --- src/sage/plot/animate.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/plot/animate.py b/src/sage/plot/animate.py index 38ba89457a5..9160edb04c3 100644 --- a/src/sage/plot/animate.py +++ b/src/sage/plot/animate.py @@ -97,7 +97,7 @@ import os from sage.structure.sage_object import SageObject -from sage.misc.temporary_file import tmp_filename, tmp_dir +from sage.misc.temporary_file import tmp_filename, tmp_dir, graphics_filename import plot import sage.misc.misc import sage.misc.viewer @@ -637,7 +637,10 @@ def show(self, delay=20, iterations=0): return if plot.EMBEDDED_MODE: - self.gif(delay = delay, iterations = iterations) + # graphics_filename is used so that notebook knows + # what file to display + filename = graphics_filename(ext='.gif') + self.gif(savefile=filename, delay=delay, iterations=iterations) else: filename = tmp_filename(ext='.gif') self.gif(delay=delay, savefile=filename, iterations=iterations) From f5e92ea50730bfe25bbf99bf230863a0b4891c32 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Mon, 30 Jun 2014 20:58:14 -0400 Subject: [PATCH 445/546] Updated Sage version to 6.3.beta5 --- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- src/bin/sage-banner | 2 +- src/bin/sage-version.sh | 4 ++-- src/sage/version.py | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index fe4c608dd35..acf22e3ba21 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -Sage version 6.3.beta4, released 2014-06-19 +Sage version 6.3.beta5, released 2014-07-01 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 97ed2219c62..35794f11de9 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=100d46c2358df82a93feb63bc339620f413caa1b -md5=d10af4cfec628f992109225444e68ba3 -cksum=553182482 +sha1=4d10c539769b8aa53c2bd97f9d9ae6b8a139d356 +md5=b9ea7956de216fc5e5965e17b14bab3f +cksum=2387138201 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index a7873645902..8f92bfdd497 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -34 +35 diff --git a/src/bin/sage-banner b/src/bin/sage-banner index 146899e5bd1..16a7975da37 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ Sage Version 6.3.beta4, Release Date: 2014-06-19 │ +│ Sage Version 6.3.beta5, Release Date: 2014-07-01 │ │ Type "notebook()" for the browser-based notebook interface. │ │ Type "help()" for help. │ └────────────────────────────────────────────────────────────────────┘ diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index c9ac6fe4f5e..94875bbe5ba 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,4 +1,4 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='6.3.beta4' -SAGE_RELEASE_DATE='2014-06-19' +SAGE_VERSION='6.3.beta5' +SAGE_RELEASE_DATE='2014-07-01' diff --git a/src/sage/version.py b/src/sage/version.py index 771523e0f6f..72e854cfd25 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,4 +1,4 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '6.3.beta4' -date = '2014-06-19' +version = '6.3.beta5' +date = '2014-07-01' From 0698433258c4f863cc1585ece2065b5e4e1b41eb Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Tue, 1 Jul 2014 10:05:49 +0200 Subject: [PATCH 446/546] trac #16553: deprecated alias .points() + fix --- src/sage/combinat/designs/incidence_structures.py | 1 + src/sage/combinat/designs/orthogonal_arrays_recursive.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 4f08530e83a..5df55af101f 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -870,6 +870,7 @@ def parameters(self): dual_design = deprecated_function_alias(16553, dual) dual_incidence_structure = deprecated_function_alias(16553, dual) is_block_design = deprecated_function_alias(16553, is_t_design) + points = deprecated_function_alias(16553, ground_set) def block_design_checker(self, t, v, k, lmbda, type=None): """ diff --git a/src/sage/combinat/designs/orthogonal_arrays_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_recursive.py index 30a33a0ca27..757ab5b82eb 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_recursive.py @@ -695,7 +695,7 @@ def OA_and_oval(q): from sage.numerical.mip import MixedIntegerLinearProgram p = MixedIntegerLinearProgram() b = p.new_variable(binary=True) - V = B.points() + V = B.ground_set() p.add_constraint(p.sum([b[i] for i in V]) == q+1) for bl in B: p.add_constraint(p.sum([b[i] for i in bl]) <= 2) From 56cd05d2e6802c6c4359317b0c680eec3ad0a70f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 1 Jul 2014 11:11:21 -0500 Subject: [PATCH 447/546] Some fixes discussed. --- src/doc/en/reference/groups/index.rst | 1 + src/doc/en/reference/monoids/index.rst | 1 + src/sage/categories/groups.py | 26 +++++++++-- src/sage/groups/indexed_free_group.py | 59 +++++++++++-------------- src/sage/monoids/indexed_free_monoid.py | 6 ++- 5 files changed, 55 insertions(+), 38 deletions(-) diff --git a/src/doc/en/reference/groups/index.rst b/src/doc/en/reference/groups/index.rst index 3aeb5b20c14..080f84dfb99 100644 --- a/src/doc/en/reference/groups/index.rst +++ b/src/doc/en/reference/groups/index.rst @@ -12,6 +12,7 @@ Groups sage/groups/finitely_presented sage/groups/finitely_presented_named sage/groups/braid + sage/groups/indexed_free_group sage/groups/raag sage/groups/abelian_gps/abelian_group sage/groups/abelian_gps/values diff --git a/src/doc/en/reference/monoids/index.rst b/src/doc/en/reference/monoids/index.rst index 9f984286142..05b8c558d65 100644 --- a/src/doc/en/reference/monoids/index.rst +++ b/src/doc/en/reference/monoids/index.rst @@ -12,6 +12,7 @@ finite number of indeterminates. sage/monoids/free_monoid_element sage/monoids/free_abelian_monoid sage/monoids/free_abelian_monoid_element + sage/monoids/indexed_free_monoid sage/monoids/string_monoid_element sage/monoids/string_monoid diff --git a/src/sage/categories/groups.py b/src/sage/categories/groups.py index 402dc91b73f..fb41d4fc6fc 100644 --- a/src/sage/categories/groups.py +++ b/src/sage/categories/groups.py @@ -58,18 +58,27 @@ def free(index_set=None, names=None, **kwds): - ``names`` -- a string or list/tuple/iterable of strings (default: ``'x'``); the generator names or name prefix + When the index set is an integer or only variable names are given, + this returns :class:`~sage.groups.free_group.FreeGroup_class`, which + currently has more features due to the interface with GAP than + :class:`~sage.groups.indexed_free_group.IndexedFreeGroup`. + EXAMPLES:: sage: Groups.free(index_set=ZZ) Free group indexed by Integer Ring sage: Groups().free(ZZ) Free group indexed by Integer Ring + sage: Groups().free(5) + Free Group on generators {x0, x1, x2, x3, x4} sage: F. = Groups().free(); F Free Group on generators {x, y, z} """ from sage.rings.all import ZZ if index_set in ZZ or (index_set is None and names is not None): from sage.groups.free_group import FreeGroup + if names is None: + return FreeGroup(index_set, **kwds) return FreeGroup(index_set, names, **kwds) from sage.groups.indexed_free_group import IndexedFreeGroup @@ -419,22 +428,31 @@ def free(index_set=None, names=None, **kwds): Free abelian group indexed by Integer Ring sage: Groups().Commutative().free(ZZ) Free abelian group indexed by Integer Ring + sage: Groups().Commutative().free(5) + Multiplicative Abelian group isomorphic to Z x Z x Z x Z x Z sage: F. = Groups().Commutative().free(); F - Free abelian group indexed by {'x', 'y', 'z'} + Multiplicative Abelian group isomorphic to Z x Z x Z """ + from sage.rings.all import ZZ if names is not None: if isinstance(names, str): - from sage.rings.all import ZZ if ',' not in names and index_set in ZZ: names = [names + repr(i) for i in range(index_set)] else: names = names.split(',') names = tuple(names) if index_set is None: - index_set = names + index_set = ZZ(len(names)) + if index_set in ZZ: + from sage.groups.abelian_gps.abelian_group import AbelianGroup + return AbelianGroup(index_set, names=names, **kwds) + + if index_set in ZZ: + from sage.groups.abelian_gps.abelian_group import AbelianGroup + return AbelianGroup(index_set, **kwds) from sage.groups.indexed_free_group import IndexedFreeAbelianGroup - return IndexedFreeAbelianGroup(index_set, **kwds) + return IndexedFreeAbelianGroup(index_set, names=names, **kwds) class Algebras(AlgebrasCategory): r""" diff --git a/src/sage/groups/indexed_free_group.py b/src/sage/groups/indexed_free_group.py index e53bcab3c5c..fb5c2d2537d 100644 --- a/src/sage/groups/indexed_free_group.py +++ b/src/sage/groups/indexed_free_group.py @@ -36,6 +36,32 @@ class IndexedGroup(IndexedMonoid): """ Base class for free (abelian) groups whose generators are indexed by a set. + + TESTS: + + We check finite properties:: + + sage: G = Groups().free(index_set=ZZ) + sage: G.is_finite() + False + sage: G = Groups().free(index_set='abc') + sage: G.is_finite() + False + sage: G = Groups().free(index_set=[]) + sage: G.is_finite() + True + + :: + + sage: G = Groups().Commutative().free(index_set=ZZ) + sage: G.is_finite() + False + sage: G = Groups().Commutative().free(index_set='abc') + sage: G.is_finite() + False + sage: G = Groups().Commutative().free(index_set=[]) + sage: G.is_finite() + True """ def order(self): r""" @@ -56,39 +82,6 @@ def order(self): """ return self.cardinality() - # TODO: once #10963 is merged, use the categories - # Groups().Infinite() / Groups().Finite() and get rid of this - # method - def is_finite(self): - """ - Return ``True`` if ``self`` is finite. - - EXAMPLES:: - - sage: G = Groups().free(index_set=ZZ) - sage: G.is_finite() - False - sage: G = Groups().free(index_set='abc') - sage: G.is_finite() - False - sage: G = Groups().free(index_set=[]) - sage: G.is_finite() - True - - :: - - sage: G = Groups().Commutative().free(index_set=ZZ) - sage: G.is_finite() - False - sage: G = Groups().Commutative().free(index_set='abc') - sage: G.is_finite() - False - sage: G = Groups().Commutative().free(index_set=[]) - sage: G.is_finite() - True - """ - return self.rank() == 0 - def rank(self): """ Return the rank of ``self``. diff --git a/src/sage/monoids/indexed_free_monoid.py b/src/sage/monoids/indexed_free_monoid.py index eb0e4f277b2..7982825ac6b 100644 --- a/src/sage/monoids/indexed_free_monoid.py +++ b/src/sage/monoids/indexed_free_monoid.py @@ -681,7 +681,7 @@ def __classcall__(cls, indices, prefix="F", **kwds): elif isinstance(indices, (list, tuple)): indices = FiniteEnumeratedSet(indices) elif indices is None: - if 'names' not in kwds: + if kwds.get('names', None) is None: raise ValueError("no index set specified") indices = FiniteEnumeratedSet(kwds['names']) @@ -713,6 +713,10 @@ def __init__(self, indices, prefix, category=None, names=None, **kwds): """ self._indices = indices category = Monoids().or_subcategory(category) + #if indices.cardinality() == 0: + # category = category.Finite() + #else: + # category = category.Infinite() Parent.__init__(self, names=names, category=category) # ignore the optional 'key' since it only affects CachedRepresentation From f1f5547fc531b78522ad51ba094414594fe69ee2 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 1 Jul 2014 11:24:22 -0500 Subject: [PATCH 448/546] Fixed category issue. --- src/sage/groups/indexed_free_group.py | 2 -- src/sage/monoids/indexed_free_monoid.py | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/sage/groups/indexed_free_group.py b/src/sage/groups/indexed_free_group.py index fb5c2d2537d..d41d5491ce2 100644 --- a/src/sage/groups/indexed_free_group.py +++ b/src/sage/groups/indexed_free_group.py @@ -159,7 +159,6 @@ def __init__(self, indices, prefix, category=None, **kwds): sage: G = Groups().free(index_set='abc') sage: TestSuite(G).run() """ - Group.__init__(self) category = Groups().or_subcategory(category) IndexedGroup.__init__(self, indices, prefix, category, **kwds) @@ -347,7 +346,6 @@ def __init__(self, indices, prefix, category=None, **kwds): sage: G = Groups().Commutative().free(index_set='abc') sage: TestSuite(G).run() """ - AbelianGroup.__init__(self) category = Groups().or_subcategory(category) IndexedGroup.__init__(self, indices, prefix, category, **kwds) diff --git a/src/sage/monoids/indexed_free_monoid.py b/src/sage/monoids/indexed_free_monoid.py index 7982825ac6b..323a8fc3d66 100644 --- a/src/sage/monoids/indexed_free_monoid.py +++ b/src/sage/monoids/indexed_free_monoid.py @@ -713,10 +713,10 @@ def __init__(self, indices, prefix, category=None, names=None, **kwds): """ self._indices = indices category = Monoids().or_subcategory(category) - #if indices.cardinality() == 0: - # category = category.Finite() - #else: - # category = category.Infinite() + if indices.cardinality() == 0: + category = category.Finite() + else: + category = category.Infinite() Parent.__init__(self, names=names, category=category) # ignore the optional 'key' since it only affects CachedRepresentation From f6a73fd57fdcb739ddf7ac4907fb3d67117af908 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 1 Jul 2014 11:37:14 -0500 Subject: [PATCH 449/546] Fixed failing doctests in categories due to new intermediate categories. --- src/sage/categories/category.py | 6 +++--- src/sage/categories/commutative_rings.py | 2 +- src/sage/categories/primer.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py index 36200017d0d..02cc4375e14 100644 --- a/src/sage/categories/category.py +++ b/src/sage/categories/category.py @@ -2872,9 +2872,9 @@ def _without_axioms(self, named=False): Traceback (most recent call last): ... ValueError: This join category isn't built by adding axioms to a single category - sage: C = Monoids().Commutative() + sage: C = Monoids().Infinite() sage: C._repr_(as_join=True) - 'Join of Category of monoids and Category of commutative magmas' + 'Join of Category of monoids and Category of infinite sets' sage: C._without_axioms() Category of magmas sage: C._without_axioms(named=True) @@ -2922,7 +2922,7 @@ def _repr_object_names(self): EXAMPLES:: sage: Groups().Finite().Commutative()._repr_(as_join=True) - 'Join of Category of finite groups and Category of commutative magmas' + 'Join of Category of finite groups and Category of commutative groups' sage: Groups().Finite().Commutative()._repr_object_names() 'finite commutative groups' diff --git a/src/sage/categories/commutative_rings.py b/src/sage/categories/commutative_rings.py index 8986c2530c9..b3978cea797 100644 --- a/src/sage/categories/commutative_rings.py +++ b/src/sage/categories/commutative_rings.py @@ -25,7 +25,7 @@ class CommutativeRings(CategoryWithAxiom): sage: C = CommutativeRings(); C Category of commutative rings sage: C.super_categories() - [Category of rings, Category of commutative magmas] + [Category of rings, Category of commutative monoids] TESTS:: diff --git a/src/sage/categories/primer.py b/src/sage/categories/primer.py index 62176f48924..99a864f5054 100644 --- a/src/sage/categories/primer.py +++ b/src/sage/categories/primer.py @@ -348,7 +348,7 @@ Category of unique factorization domains, Category of gcd domains, Category of integral domains, Category of domains, Category of commutative rings, Category of rings, ... - Category of magmas and additive magmas, + Category of magmas and additive magmas, ... Category of monoids, Category of semigroups, Category of commutative magmas, Category of unital magmas, Category of magmas, Category of commutative additive groups, ..., Category of additive magmas, From 8ee95c9f3ea0cff687c9f77a48d0e5a0b98e5935 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 1 Jul 2014 11:48:28 -0500 Subject: [PATCH 450/546] Gave doctest a proper trac number. --- src/sage/graphs/generic_graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 89e4553fcc6..ce1e98fb4ac 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -17951,7 +17951,7 @@ def canonical_label(self, partition=None, certify=False, verbosity=0, edge_label sage: G.canonical_label(edge_labels=True,certify=True) (Graph on 5 vertices, {0: 4, 1: 3, 2: 0, 3: 1, 4: 2}) - Check for immutable graphs (:trac:`16XXX`):: + Check for immutable graphs (:trac:`16602`):: sage: G = Graph([[1, 2], [2, 3]], immutable=True) sage: C = G.canonical_label(); C From e611e562d63879d812ddd249fb7f8a41dc94f66c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 1 Jul 2014 12:02:45 -0500 Subject: [PATCH 451/546] Some improvements from talking with Nicolas. --- src/sage/sets/family.py | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/sage/sets/family.py b/src/sage/sets/family.py index 3a55ab22f9f..e5baacef49c 100644 --- a/src/sage/sets/family.py +++ b/src/sage/sets/family.py @@ -564,7 +564,10 @@ def __hash__(self): sage: hash(f) == hash(g) False """ - return hash(frozenset(list(self.keys()) + map(repr, self.values()))) + try: + return hash(frozenset(self._dictionary.items())) + except (TypeError, ValueError): + return hash(frozenset(list(self.keys()) + map(repr, self.values()))) def keys(self): """ @@ -890,24 +893,13 @@ def __hash__(self): sage: g = LazyFamily(ZZ, lambda i: 2*i) sage: hash(g) == hash(g) True + sage: h = LazyFamily(ZZ, lambda i: 2*i, name='foo') + sage: hash(h) == hash(h) + True """ try: - h = hash(self.keys()) - - if self.function_name is not None: - name = self.function_name + "(i)" - elif isinstance(self.function, type(lambda x:1)): - name = self.function.__name__ - name = name+"(i)" - else: - name = repr(self.function) - if isinstance(self.function, AttrCallObject): - name = "i"+name[1:] - else: - name = name+"(i)" - - return h + hash(name) - except TypeError: + return hash(self.keys()) + hash(self.function) + except (TypeError, ValueError): return super(LazyFamily, self).__hash__() def __eq__(self, other): From f403eb3870e77402e60d66c11a4ec39e98515bb8 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 1 Jul 2014 12:12:05 -0500 Subject: [PATCH 452/546] Minor tweaks for family sets. --- src/sage/sets/family.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/sage/sets/family.py b/src/sage/sets/family.py index e5baacef49c..9172d461faf 100644 --- a/src/sage/sets/family.py +++ b/src/sage/sets/family.py @@ -560,9 +560,15 @@ def __hash__(self): sage: f2 = Family(["a", "c", "b"], lambda x: x+x) sage: hash(f) == hash(f2) True - sage: g = Family(["a", "c", "b"], lambda x: x+x+x) + sage: g = Family(["b", "c", "a"], lambda x: x+x+x) sage: hash(f) == hash(g) False + + :: + + sage: f = Family({1:[1,2]}) + sage: hash(f) == hash(f) + True """ try: return hash(frozenset(self._dictionary.items())) @@ -884,7 +890,7 @@ def __hash__(self): """ Return a hash value for ``self``. - TESTS:: + EXAMPLES:: sage: from sage.sets.family import LazyFamily sage: f = LazyFamily([3,4,7], lambda i: 2*i) @@ -1122,7 +1128,7 @@ def __hash__(self): """ Return a hash value for ``self``. - TESTS:: + EXAMPLES:: sage: from sage.sets.family import TrivialFamily sage: f = TrivialFamily((3,4,7)) From 5aa1e5bfa4b6054df40482d9274792e2d2a64b96 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 1 Jul 2014 12:18:10 -0500 Subject: [PATCH 453/546] Extra doctest for Nicolas. --- src/sage/sets/family.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sage/sets/family.py b/src/sage/sets/family.py index 9172d461faf..d289cc69e86 100644 --- a/src/sage/sets/family.py +++ b/src/sage/sets/family.py @@ -902,6 +902,16 @@ def __hash__(self): sage: h = LazyFamily(ZZ, lambda i: 2*i, name='foo') sage: hash(h) == hash(h) True + + :: + + sage: class X(object): + ....: def __call__(self, x): + ....: return x + ....: __hash__ = None + sage: f = Family([1,2,3], X()) + sage: hash(f) == hash(f) + True """ try: return hash(self.keys()) + hash(self.function) From 69ec7b219d799ffa9263bc83364bc91bf07a8065 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 1 Jul 2014 12:47:28 -0500 Subject: [PATCH 454/546] Fixed c3_controlled.pyx doctests. --- src/sage/misc/c3_controlled.pyx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/misc/c3_controlled.pyx b/src/sage/misc/c3_controlled.pyx index 7012d7b4879..efdd608046c 100644 --- a/src/sage/misc/c3_controlled.pyx +++ b/src/sage/misc/c3_controlled.pyx @@ -314,9 +314,9 @@ For a typical category, few bases, if any, need to be added to force sage: x.mro == x.mro_standard False sage: x.all_bases_len() - 64 - sage: x.all_bases_controlled_len() 66 + sage: x.all_bases_controlled_len() + 69 sage: C = GradedHopfAlgebrasWithBasis(QQ) sage: x = HierarchyElement(C, attrcall("super_categories"), attrgetter("_cmp_key")) @@ -336,7 +336,6 @@ for any that requires the addition of some bases:: ....: if len(C._super_categories_for_classes) != len(C.super_categories())], ....: key=str) [Category of affine weyl groups, - Category of commutative rings, Category of fields, Category of finite dimensional algebras with basis over Rational Field, Category of finite dimensional hopf algebras with basis over Rational Field, From 48e5a6ac043196520f95991b47bcd2557c77b835 Mon Sep 17 00:00:00 2001 From: Miguel Marco Date: Tue, 1 Jul 2014 20:39:15 +0200 Subject: [PATCH 455/546] fixed non-ascii character --- src/sage/interfaces/tides.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/interfaces/tides.py b/src/sage/interfaces/tides.py index 422e7ae98a3..87451c8f031 100644 --- a/src/sage/interfaces/tides.py +++ b/src/sage/interfaces/tides.py @@ -30,7 +30,7 @@ REFERENCES: .. [ALG924] A. Abad, R. Barrio, F. Blesa, M. Rodriguez. Algorithm 924. *ACM -Transactions on Mathematical Software*, *39*(1), 1–28. +Transactions on Mathematical Software*, *39*(1), 1-28. .. [TI](http://www.unizar.es/acz/05Publicaciones/Monografias/MonografiasPublicadas/Monografia36/IndMonogr36.htm) A. Abad, R. Barrio, F. Blesa, M. Rodriguez. From 365ebaf34dd6222071325866523de583ad411521 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Wed, 2 Jul 2014 17:25:13 +0200 Subject: [PATCH 456/546] 13781: change all 'ntl' to 'NTL' --- .../polynomial/polynomial_modn_dense_ntl.pyx | 116 +++++++++--------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx index f9ae60d3232..50a93e95172 100644 --- a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx @@ -65,7 +65,7 @@ cdef class Polynomial_dense_mod_n(Polynomial): EXAMPLES:: - sage: R. = PolynomialRing(Integers(16), implementation='ntl') + sage: R. = PolynomialRing(Integers(16), implementation='NTL') sage: f = x^3 - x + 17 sage: f^2 x^6 + 14*x^4 + 2*x^3 + x^2 + 14*x + 1 @@ -73,12 +73,12 @@ cdef class Polynomial_dense_mod_n(Polynomial): sage: loads(f.dumps()) == f True - sage: R. = PolynomialRing(Integers(100), implementation='ntl') + sage: R. = PolynomialRing(Integers(100), implementation='NTL') sage: p = 3*x sage: q = 7*x sage: p+q 10*x - sage: R. = PolynomialRing(Integers(8), implementation='ntl') + sage: R. = PolynomialRing(Integers(8), implementation='NTL') sage: parent(p) Univariate Polynomial Ring in x over Ring of integers modulo 100 (using NTL) sage: p + q @@ -156,7 +156,7 @@ cdef class Polynomial_dense_mod_n(Polynomial): """ EXAMPLES:: - sage: t = PolynomialRing(IntegerModRing(17),"t", implementation='ntl').gen() + sage: t = PolynomialRing(IntegerModRing(17),"t", implementation='NTL').gen() sage: f = t^3 + 3*t - 17 sage: pari(f) Mod(1, 17)*t^3 + Mod(3, 17)*t @@ -185,7 +185,7 @@ cdef class Polynomial_dense_mod_n(Polynomial): EXAMPLES:: - sage: R. = PolynomialRing(Integers(100), implementation='ntl') + sage: R. = PolynomialRing(Integers(100), implementation='NTL') sage: from sage.rings.polynomial.polynomial_modn_dense_ntl import Polynomial_dense_mod_n sage: f = Polynomial_dense_mod_n(R,[5,10,13,1,4]); f 4*x^4 + x^3 + 13*x^2 + 10*x + 5 @@ -228,7 +228,7 @@ cdef class Polynomial_dense_mod_n(Polynomial): """ EXAMPLES:: - sage: x = PolynomialRing(Integers(100), 'x', implementation='ntl').0 + sage: x = PolynomialRing(Integers(100), 'x', implementation='NTL').0 sage: (x - 2)*(x^2 - 8*x + 16) x^3 + 90*x^2 + 32*x + 68 """ @@ -263,7 +263,7 @@ cdef class Polynomial_dense_mod_n(Polynomial): EXAMPLES:: - sage: R. = PolynomialRing(Integers(12345678901234567890), implementation='ntl') + sage: R. = PolynomialRing(Integers(12345678901234567890), implementation='NTL') sage: p = x^2 + 2*x + 4 sage: p.shift(0) x^2 + 2*x + 4 @@ -313,7 +313,7 @@ cdef class Polynomial_dense_mod_n(Polynomial): EXAMPLES:: - sage: _. = PolynomialRing(Integers(100), implementation='ntl') + sage: _. = PolynomialRing(Integers(100), implementation='NTL') sage: f = x^3 + 3*x - 17 sage: f.list() [83, 3, 0, 1] @@ -335,7 +335,7 @@ cdef class Polynomial_dense_mod_n(Polynomial): EXAMPLES:: - sage: R. = PolynomialRing(Integers(100), implementation='ntl') + sage: R. = PolynomialRing(Integers(100), implementation='NTL') sage: from sage.rings.polynomial.polynomial_modn_dense_ntl import Polynomial_dense_mod_n as poly_modn_dense sage: poly_modn_dense(R, ([1,-2,3])) 3*x^2 + 98*x + 1 @@ -379,7 +379,7 @@ cdef class Polynomial_dense_mod_n(Polynomial): sage: N = 10001 sage: K = Zmod(10001) - sage: P. = PolynomialRing(K, implementation='ntl') + sage: P. = PolynomialRing(K, implementation='NTL') sage: f = x^3 + 10*x^2 + 5000*x - 222 sage: f.small_roots() [4] @@ -416,7 +416,7 @@ def small_roots(self, X=None, beta=1.0, epsilon=None, **kwds): sage: N = 10001 sage: K = Zmod(10001) - sage: P. = PolynomialRing(K, implementation='ntl') + sage: P. = PolynomialRing(K, implementation='NTL') sage: f = x^3 + 10*x^2 + 5000*x - 222 This polynomial has no roots without modular reduction (i.e. over `\ZZ`):: @@ -476,7 +476,7 @@ def small_roots(self, X=None, beta=1.0, epsilon=None, **kwds): To recover `K` we consider the following polynomial modulo `N`:: - sage: P. = PolynomialRing(ZmodN, implementation='ntl') + sage: P. = PolynomialRing(ZmodN, implementation='NTL') sage: f = (2^Nbits - 2^Kbits + x)^e - C and recover its small roots:: @@ -503,7 +503,7 @@ def small_roots(self, X=None, beta=1.0, epsilon=None, **kwds): And try to recover `q` from it:: - sage: F. = PolynomialRing(Zmod(N), implementation='ntl') + sage: F. = PolynomialRing(Zmod(N), implementation='NTL') sage: f = x - qbar We know that the error is `\le 2^{\text{hidden}}-1` and that the modulus @@ -590,7 +590,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: sage: R = Integers(5**21) - sage: S. = PolynomialRing(R, implementation='ntl') + sage: S. = PolynomialRing(R, implementation='NTL') sage: S(1/4) 357627868652344 """ @@ -636,7 +636,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = PolynomialRing(Integers(100), implementation='ntl') + sage: R. = PolynomialRing(Integers(100), implementation='NTL') sage: from sage.rings.polynomial.polynomial_modn_dense_ntl import Polynomial_dense_mod_n as poly_modn_dense sage: f = poly_modn_dense(R,[5,0,0,1]) sage: f.int_list() @@ -654,7 +654,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = PolynomialRing(Integers(100), implementation='ntl') + sage: R. = PolynomialRing(Integers(100), implementation='NTL') sage: from sage.rings.polynomial.polynomial_modn_dense_ntl import Polynomial_dense_modn_ntl_zz sage: f = Polynomial_dense_modn_ntl_zz(R,[2, 1])^7 sage: f[3] @@ -690,7 +690,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = PolynomialRing(Integers(100), implementation='ntl') + sage: R. = PolynomialRing(Integers(100), implementation='NTL') sage: (x+5) + (x^2 - 6) x^2 + x + 99 """ @@ -707,7 +707,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = PolynomialRing(Integers(100), implementation='ntl') + sage: R. = PolynomialRing(Integers(100), implementation='NTL') sage: (x+5) - (x^2 - 6) 99*x^2 + x + 11 """ @@ -724,7 +724,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = PolynomialRing(Integers(100), implementation='ntl') + sage: R. = PolynomialRing(Integers(100), implementation='NTL') sage: (x+5) * (x^2 - 1) x^3 + 5*x^2 + 99*x + 95 """ @@ -781,7 +781,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = PolynomialRing(Integers(100), implementation='ntl') + sage: R. = PolynomialRing(Integers(100), implementation='NTL') sage: (x+5) * 3 3*x + 15 """ @@ -797,7 +797,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = PolynomialRing(Integers(100), implementation='ntl') + sage: R. = PolynomialRing(Integers(100), implementation='NTL') sage: 3 * (x+5) 3*x + 15 """ @@ -813,13 +813,13 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = PolynomialRing(Integers(100), implementation='ntl') + sage: R. = PolynomialRing(Integers(100), implementation='NTL') sage: (x-1)^5 x^5 + 95*x^4 + 10*x^3 + 90*x^2 + 5*x + 99 Negative powers will not work:: - sage: R. = PolynomialRing(Integers(101), implementation='ntl') + sage: R. = PolynomialRing(Integers(101), implementation='NTL') sage: (x-1)^(-5) Traceback (most recent call last): ... @@ -886,7 +886,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = PolynomialRing(Integers(125), implementation='ntl') + sage: R. = PolynomialRing(Integers(125), implementation='NTL') sage: f = x^5+1; g = (x+1)^2 sage: q, r = f.quo_rem(g) sage: q @@ -913,7 +913,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = PolynomialRing(Integers(25), implementation='ntl') + sage: R. = PolynomialRing(Integers(25), implementation='NTL') sage: f = x^7 + 1; g = x^2 - 1 sage: q = f // g; q x^5 + x^3 + x @@ -936,7 +936,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ EXAMPLES:: - sage: R. = PolynomialRing(Integers(81), implementation='ntl') + sage: R. = PolynomialRing(Integers(81), implementation='NTL') sage: f = x^7 + x + 1; g = x^3 sage: r = f % g; r x + 1 @@ -962,7 +962,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = PolynomialRing(Integers(77), implementation='ntl') + sage: R. = PolynomialRing(Integers(77), implementation='NTL') sage: f = x^7 + x + 1 sage: f.shift(1) x^8 + x^2 + x @@ -986,7 +986,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ TEST:: - sage: R. = PolynomialRing(Integers(77), implementation='ntl') + sage: R. = PolynomialRing(Integers(77), implementation='NTL') sage: f = x^5 + 2*x + 1 sage: f << 3 x^8 + 2*x^4 + x^3 @@ -999,7 +999,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ TEST:: - sage: R. = PolynomialRing(Integers(77), implementation='ntl') + sage: R. = PolynomialRing(Integers(77), implementation='NTL') sage: f = x^5 + 2*x + 1 sage: f >> 3 x^2 @@ -1020,7 +1020,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = PolynomialRing(Integers(77), implementation='ntl') + sage: R. = PolynomialRing(Integers(77), implementation='NTL') sage: f = x^4 - x - 1 sage: f._derivative() 4*x^3 + 76 @@ -1053,7 +1053,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = PolynomialRing(Integers(77), implementation='ntl') + sage: R. = PolynomialRing(Integers(77), implementation='NTL') sage: f = x^4 - x - 1 sage: f.reverse() 76*x^4 + 76*x^3 + 1 @@ -1072,7 +1072,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = PolynomialRing(Integers(77), implementation='ntl') + sage: R. = PolynomialRing(Integers(77), implementation='NTL') sage: f = x^4 - x - 1 sage: not f False @@ -1088,7 +1088,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = PolynomialRing(Integers(10), implementation='ntl') + sage: R. = PolynomialRing(Integers(10), implementation='NTL') sage: x.valuation() 1 sage: f = x-3; f.valuation() @@ -1108,7 +1108,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): """ EXAMPLES:: - sage: R. = PolynomialRing(Integers(77), implementation='ntl') + sage: R. = PolynomialRing(Integers(77), implementation='NTL') sage: f = x^4 - x - 1 sage: f.degree() 4 @@ -1124,7 +1124,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = PolynomialRing(Integers(77), implementation='ntl') + sage: R. = PolynomialRing(Integers(77), implementation='NTL') sage: f = sum(x^n for n in range(10)); f x^9 + x^8 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1 sage: f.truncate(6) @@ -1142,7 +1142,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = PolynomialRing(Integers(100), implementation='ntl') + sage: R. = PolynomialRing(Integers(100), implementation='NTL') sage: f = x^3+7 sage: f(5) 32 @@ -1152,7 +1152,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): 32 sage: f(x) x^3 + 7 - sage: S. = PolynomialRing(Integers(5), implementation='ntl') + sage: S. = PolynomialRing(Integers(5), implementation='NTL') sage: f(y) y^3 + 2 """ @@ -1218,7 +1218,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') + sage: R. = PolynomialRing(Integers(10^30), implementation='NTL') sage: from sage.rings.polynomial.polynomial_modn_dense_ntl import Polynomial_dense_modn_ntl_ZZ sage: f = Polynomial_dense_modn_ntl_ZZ(R,[2,1])^7 sage: f[3] @@ -1266,7 +1266,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') + sage: R. = PolynomialRing(Integers(10^30), implementation='NTL') sage: (x+5) + (x^2 - 6) x^2 + x + 999999999999999999999999999999 """ @@ -1283,7 +1283,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') + sage: R. = PolynomialRing(Integers(10^30), implementation='NTL') sage: (x+5) - (x^2 - 6) 999999999999999999999999999999*x^2 + x + 11 """ @@ -1300,7 +1300,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') + sage: R. = PolynomialRing(Integers(10^30), implementation='NTL') sage: (x+5) * (x^2 - 1) x^3 + 5*x^2 + 999999999999999999999999999999*x + 999999999999999999999999999995 """ @@ -1357,7 +1357,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') + sage: R. = PolynomialRing(Integers(10^30), implementation='NTL') sage: (x+5) * 3 3*x + 15 """ @@ -1374,7 +1374,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') + sage: R. = PolynomialRing(Integers(10^30), implementation='NTL') sage: 3 * (x+5) 3*x + 15 """ @@ -1384,7 +1384,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') + sage: R. = PolynomialRing(Integers(10^30), implementation='NTL') sage: (x+1)^5 x^5 + 5*x^4 + 10*x^3 + 10*x^2 + 5*x + 1 @@ -1447,7 +1447,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') + sage: R. = PolynomialRing(Integers(10^30), implementation='NTL') sage: f = x^5+1; g = (x+1)^2 sage: q, r = f.quo_rem(g) sage: q @@ -1474,7 +1474,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') + sage: R. = PolynomialRing(Integers(10^30), implementation='NTL') sage: f = x^7 + 1; g = x^2 - 1 sage: q = f // g; q x^5 + x^3 + x @@ -1497,7 +1497,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ EXAMPLES:: - sage: R. = PolynomialRing(Integers(9^30), implementation='ntl') + sage: R. = PolynomialRing(Integers(9^30), implementation='NTL') sage: f = x^7 + x + 1; g = x^3 - 1 sage: r = f % g; r 2*x + 1 @@ -1523,7 +1523,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = PolynomialRing(Integers(12^30), implementation='ntl') + sage: R. = PolynomialRing(Integers(12^30), implementation='NTL') sage: f = x^7 + x + 1 sage: f.shift(1) x^8 + x^2 + x @@ -1547,7 +1547,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ TEST:: - sage: R. = PolynomialRing(Integers(14^30), implementation='ntl') + sage: R. = PolynomialRing(Integers(14^30), implementation='NTL') sage: f = x^5 + 2*x + 1 sage: f << 3 x^8 + 2*x^4 + x^3 @@ -1560,7 +1560,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ TEST:: - sage: R. = PolynomialRing(Integers(15^30), implementation='ntl') + sage: R. = PolynomialRing(Integers(15^30), implementation='NTL') sage: f = x^5 + 2*x + 1 sage: f >> 3 x^2 @@ -1582,7 +1582,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = PolynomialRing(Integers(12^29), implementation='ntl') + sage: R. = PolynomialRing(Integers(12^29), implementation='NTL') sage: f = x^4 + x + 5 sage: f._derivative() 4*x^3 + 1 @@ -1617,7 +1617,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = PolynomialRing(Integers(12^29), implementation='ntl') + sage: R. = PolynomialRing(Integers(12^29), implementation='NTL') sage: f = x^4 + 2*x + 5 sage: f.reverse() 5*x^4 + 2*x^3 + 1 @@ -1639,7 +1639,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = PolynomialRing(Integers(10^50), implementation='ntl') + sage: R. = PolynomialRing(Integers(10^50), implementation='NTL') sage: x.valuation() 1 sage: f = x-3; f.valuation() @@ -1661,7 +1661,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ TESTS:: - sage: R. = PolynomialRing(Integers(12^29), implementation='ntl') + sage: R. = PolynomialRing(Integers(12^29), implementation='NTL') sage: f = x^4 + 1 sage: not f False @@ -1674,7 +1674,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): """ EXAMPLES:: - sage: R. = PolynomialRing(Integers(14^34), implementation='ntl') + sage: R. = PolynomialRing(Integers(14^34), implementation='NTL') sage: f = x^4 - x - 1 sage: f.degree() 4 @@ -1690,7 +1690,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = PolynomialRing(Integers(15^30), implementation='ntl') + sage: R. = PolynomialRing(Integers(15^30), implementation='NTL') sage: f = sum(x^n for n in range(10)); f x^9 + x^8 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1 sage: f.truncate(6) @@ -1708,7 +1708,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): EXAMPLES:: - sage: R. = PolynomialRing(Integers(10^30), implementation='ntl') + sage: R. = PolynomialRing(Integers(10^30), implementation='NTL') sage: f = x^3+7 sage: f(5) 132 @@ -1718,7 +1718,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): 132 sage: f(x) x^3 + 7 - sage: S. = PolynomialRing(Integers(5), implementation='ntl') + sage: S. = PolynomialRing(Integers(5), implementation='NTL') sage: f(y) y^3 + 2 """ From 7c4deab9a8444be428c6071a624680da1344496e Mon Sep 17 00:00:00 2001 From: Emmanuel Charpentier Date: Wed, 2 Jul 2014 19:43:27 +0200 Subject: [PATCH 457/546] First attempt at (trivial) R version update. --- build/pkgs/r/package-version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/r/package-version.txt b/build/pkgs/r/package-version.txt index 254cffbc732..018c6c94a4d 100644 --- a/build/pkgs/r/package-version.txt +++ b/build/pkgs/r/package-version.txt @@ -1 +1 @@ -3.0.2.p1 +3.1.0.p0 From 83d75c97a3a7bd64c83f6270b877b62ba0083692 Mon Sep 17 00:00:00 2001 From: Emmanuel Charpentier Date: Thu, 3 Jul 2014 09:54:52 +0200 Subject: [PATCH 458/546] New upstream version (3.1) configure patch adjusted Broken ARM regexes are now handled by upstream Adjusted SPKG.txt --- build/pkgs/r/SPKG.txt | 1 + build/pkgs/r/checksums.ini | 8 +++--- build/pkgs/r/patches/configure.patch | 4 +-- build/pkgs/r/patches/install.R-arm.patch | 31 ------------------------ 4 files changed, 7 insertions(+), 37 deletions(-) delete mode 100644 build/pkgs/r/patches/install.R-arm.patch diff --git a/build/pkgs/r/SPKG.txt b/build/pkgs/r/SPKG.txt index fbd87b09743..45938c4a1df 100644 --- a/build/pkgs/r/SPKG.txt +++ b/build/pkgs/r/SPKG.txt @@ -46,4 +46,5 @@ much code written for S runs unaltered under R. libintl library. * large_address_aware.patch: don't pass --large-address-aware to ld on Cygwin64. + * Management of the broken regexes in ARM is now handled by upstream. diff --git a/build/pkgs/r/checksums.ini b/build/pkgs/r/checksums.ini index e0a97c2b2ad..765af8a7fcb 100644 --- a/build/pkgs/r/checksums.ini +++ b/build/pkgs/r/checksums.ini @@ -1,4 +1,4 @@ -tarball=r-VERSION.tar.bz2 -sha1=0ba9c6e5ceda749cccf5bd9cf5b535ff163deefe -md5=9247263d8a7d1e2e6794ba41f265eda4 -cksum=1117391428 +tarball=r-VERSION.tar.gz +sha1=a9d13932c739cc12667c6a17fabd9361624a1708 +md5=a1ee52446bee81820409661e6d114ab1 +cksum=3701745191 diff --git a/build/pkgs/r/patches/configure.patch b/build/pkgs/r/patches/configure.patch index d475b617610..e07b7c8bcae 100644 --- a/build/pkgs/r/patches/configure.patch +++ b/build/pkgs/r/patches/configure.patch @@ -1,7 +1,7 @@ diff -ru src/configure src.configure/configure --- src/configure 2011-10-24 00:05:54.000000000 +0200 +++ src.configure/configure 2012-03-30 16:31:51.409247321 +0200 -@@ -22492,7 +22492,7 @@ +@@ -22876,7 +22492,7 @@ if ac_fn_f77_try_compile "$LINENO"; then : ac_cv_prog_f77_v= # Try some options frequently used verbose output @@ -10,7 +10,7 @@ diff -ru src/configure src.configure/configure cat > conftest.$ac_ext <<_ACEOF program main -@@ -22808,7 +22808,7 @@ +@@ -23232,7 +22808,7 @@ if ac_fn_c_try_compile "$LINENO"; then : r_cv_prog_c_v= # Try some options frequently used verbose output diff --git a/build/pkgs/r/patches/install.R-arm.patch b/build/pkgs/r/patches/install.R-arm.patch deleted file mode 100644 index 6ca5128ddf1..00000000000 --- a/build/pkgs/r/patches/install.R-arm.patch +++ /dev/null @@ -1,31 +0,0 @@ -diff --git a/src/library/tools/R/install.R b/src/library/tools/R/install.R -index 5d55ca8..b654cd6 100644 ---- a/src/library/tools/R/install.R -+++ b/src/library/tools/R/install.R -@@ -110,7 +110,11 @@ - SHLIB_EXT <- if (WINDOWS) ".dll" else { - ## can we do better? - mconf <- file.path(R.home(), paste0("etc", rarch), "Makeconf") -- sub(".*= ", "", grep("^SHLIB_EXT", readLines(mconf), value = TRUE)) -+ -+ if (substr( Sys.info()["machine"], 1, 3) == "arm") # arm has broken regexps in libc -+ sub(".*= ", "", grep("^SHLIB_EXT", readLines(mconf), value = TRUE), perl = TRUE) -+ else -+ sub(".*= ", "", grep("^SHLIB_EXT", readLines(mconf), value = TRUE)) - } - - options(warn = 1) -@@ -1676,8 +1680,13 @@ - mconf <- readLines(file.path(R.home(), - paste0("etc", Sys.getenv("R_ARCH")), - "Makeconf")) -+ if (substr( Sys.info()["machine"], 1, 3) == "arm") { # arm has broken regexps in libc -+ SHLIB_EXT <- sub(".*= ", "", grep("^SHLIB_EXT", mconf, value = TRUE), perl = TRUE) -+ SHLIB_LIBADD <- sub(".*= ", "", grep("^SHLIB_LIBADD", mconf, value = TRUE), perl = TRUE) -+ } else { - SHLIB_EXT <- sub(".*= ", "", grep("^SHLIB_EXT", mconf, value = TRUE)) - SHLIB_LIBADD <- sub(".*= ", "", grep("^SHLIB_LIBADD", mconf, value = TRUE)) -+ } - MAKE <- Sys.getenv("MAKE") - rarch <- Sys.getenv("R_ARCH") - } else { From 191e410df38d16677f252e97015a532625c9e245 Mon Sep 17 00:00:00 2001 From: Emmanuel Charpentier Date: Thu, 3 Jul 2014 10:08:31 +0200 Subject: [PATCH 459/546] More patches fixes libintl visibility is now handled by upstream some patches moved --- build/pkgs/r/patches/large_address_aware.patch | 4 ++-- build/pkgs/r/patches/libintl-visibility.patch | 13 ------------- 2 files changed, 2 insertions(+), 15 deletions(-) delete mode 100644 build/pkgs/r/patches/libintl-visibility.patch diff --git a/build/pkgs/r/patches/large_address_aware.patch b/build/pkgs/r/patches/large_address_aware.patch index ecf8657bd9c..48bfa8f8023 100644 --- a/build/pkgs/r/patches/large_address_aware.patch +++ b/build/pkgs/r/patches/large_address_aware.patch @@ -1,7 +1,7 @@ diff -druN r-3.0.2.orig/configure.ac r-3.0.2/configure.ac --- r-3.0.2.orig/configure.ac 2013-08-26 15:05:06.000000000 -0700 +++ r-3.0.2/configure.ac 2014-01-18 09:35:57.516091309 -0800 -@@ -1310,7 +1310,11 @@ +@@ -1310,7 +1321,11 @@ SHLIB_EXT=".dll" dylib_undefined_allowed=no is_cygwin=yes @@ -17,7 +17,7 @@ diff -druN r-3.0.2.orig/configure.ac r-3.0.2/configure.ac diff -druN r-3.0.2.orig/configure r-3.0.2/configure --- r-3.0.2.orig/configure 2013-09-17 15:06:13.000000000 -0700 +++ r-3.0.2/configure 2014-01-18 09:38:03.426103900 -0800 -@@ -26247,7 +26247,11 @@ +@@ -26313,7 +26247,11 @@ SHLIB_EXT=".dll" dylib_undefined_allowed=no is_cygwin=yes diff --git a/build/pkgs/r/patches/libintl-visibility.patch b/build/pkgs/r/patches/libintl-visibility.patch deleted file mode 100644 index e652708acde..00000000000 --- a/build/pkgs/r/patches/libintl-visibility.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff -druN src.orig/src/extra/intl/dngettext.c src/src/extra/intl/dngettext.c ---- src.orig/src/extra/intl/dngettext.c 2010-03-17 15:43:05.000000000 +0100 -+++ src/src/extra/intl/dngettext.c 2013-11-12 14:07:19.134450433 +0100 -@@ -46,6 +46,9 @@ - - /* Look up MSGID in the DOMAINNAME message catalog of the current - LC_MESSAGES locale and skip message according to the plural form. */ -+#ifdef HAVE_VISIBILITY_ATTRIBUTE -+__attribute__ ((visibility ("default"))) -+#endif - char * - DNGETTEXT (const char *domainname, - const char *msgid1, const char *msgid2, unsigned long int n) From 45bc9377d525b94d7f9ddfbba15b9f074a324937 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 3 Jul 2014 13:42:58 +0200 Subject: [PATCH 460/546] trac #16504: Broken doctest --- src/sage/numerical/mip.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 2f8d0e3de19..560ac9048b6 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -255,7 +255,7 @@ cdef class MixedIntegerLinearProgram(SageObject): ....: items = [1/5, 1/3, 2/3, 3/4, 5/7] ....: maximum=1 ....: p=MixedIntegerLinearProgram() - ....: box=p.new_variable(**{type:True}) + ....: box=p.new_variable(nonnegative=True, **{type:True}) ....: for b in range(k): ....: p.add_constraint(p.sum([items[i]*box[i,b] for i in range(len(items))]) <= maximum) ....: for i in range(len(items)): @@ -362,7 +362,7 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p = MixedIntegerLinearProgram() sage: v = p.new_variable(real=True) - doctest:839: DeprecationWarning: The default value of 'nonnegative' will change, to False instead of True. You should add the explicit 'nonnegative=True'. + doctest:...: DeprecationWarning: The default value of 'nonnegative' will change, to False instead of True. You should add the explicit 'nonnegative=True'. See http://trac.sagemath.org/15521 for details. sage: p.get_min(v[0]) 0.0 From fa8aac39919483773ed5b5d98ce6ea2d0863d7f1 Mon Sep 17 00:00:00 2001 From: Emmanuel Charpentier Date: Thu, 3 Jul 2014 19:09:02 +0200 Subject: [PATCH 461/546] Fixed docstrings The examples for source(), _source and help() are not satisfying... --- src/sage/interfaces/r.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/sage/interfaces/r.py b/src/sage/interfaces/r.py index c583dea45fa..6e9bfb65d8c 100644 --- a/src/sage/interfaces/r.py +++ b/src/sage/interfaces/r.py @@ -512,9 +512,8 @@ def _source(self, s): EXAMPLES:: - sage: print r._source("print.anova") - function (x, digits = max(getOption("digits") - 2L, 3L), signif.stars = getOption("show.signif.stars"), - ... + sage: print r._source("c") + function (..., recursive = FALSE) .Primitive("c") """ if s[-2:] == "()": s = s[-2:] @@ -532,9 +531,8 @@ def source(self, s): EXAMPLES:: - sage: print r.source("print.anova") - function (x, digits = max(getOption("digits") - 2L, 3L), signif.stars = getOption("show.signif.stars"), - ... + sage: print r.source("c") + function (..., recursive = FALSE) .Primitive("c") """ return self._source(s) @@ -701,15 +699,12 @@ def help(self, command): EXAMPLES:: - sage: r.help('print.anova') - anova package:stats R Documentation - ... - Chambers, J. M. and Hastie, T. J. (1992) _Statistical Models in - S_, Wadsworth & Brooks/Cole. + sage: r.help('c') + c package:base R Documentation ... - .. note:: - + .. note:: + This is similar to typing r.command?. """ s = self.eval('help("%s")'%command).strip() # ?cmd is only an unsafe shortcut From fc0994b6301e972d71f96194915b3ee893f9f9c6 Mon Sep 17 00:00:00 2001 From: Emmanuel Charpentier Date: Thu, 3 Jul 2014 20:16:31 +0200 Subject: [PATCH 462/546] Upgrade R to version 3.1 A few issues fixed by Sage-specific patches are now handled bu upstream source. The relevant patches are gone. A few other patches have moved a bit. Three examples (r.source, r._source and r.help) have changed due to the unavailability of print.anova from the main namespace. The replacement examples are not quite satisfying. --- build/pkgs/r/SPKG.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build/pkgs/r/SPKG.txt b/build/pkgs/r/SPKG.txt index 45938c4a1df..fa537ed514d 100644 --- a/build/pkgs/r/SPKG.txt +++ b/build/pkgs/r/SPKG.txt @@ -46,5 +46,4 @@ much code written for S runs unaltered under R. libintl library. * large_address_aware.patch: don't pass --large-address-aware to ld on Cygwin64. - * Management of the broken regexes in ARM is now handled by upstream. - + * 3.1 : Upstream now handles some of these issues. Some examples changed. From fa1a863e0b959dff5a05f3e109da2bcec2932c79 Mon Sep 17 00:00:00 2001 From: Emmanuel Charpentier Date: Thu, 3 Jul 2014 22:06:02 +0200 Subject: [PATCH 463/546] Various fixes of flaws pointed by Vincent Delecroix (thanks !) - trailing spaces in src/sage/interfaces/r.py - lowercasing the source tarball name. --- src/sage/interfaces/r.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/interfaces/r.py b/src/sage/interfaces/r.py index 6e9bfb65d8c..9b52618d015 100644 --- a/src/sage/interfaces/r.py +++ b/src/sage/interfaces/r.py @@ -704,7 +704,7 @@ def help(self, command): ... .. note:: - + This is similar to typing r.command?. """ s = self.eval('help("%s")'%command).strip() # ?cmd is only an unsafe shortcut From b3e656b4507d9f6544e87641614836dc6275e929 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Fri, 20 Dec 2013 13:51:27 +0100 Subject: [PATCH 464/546] graphics_filename: return a tmp_filename() if not in EMBEDDED_MODE --- src/sage/matrix/matrix2.pyx | 4 +- src/sage/matrix/matrix_modn_sparse.pyx | 6 +- src/sage/misc/temporary_file.py | 55 +++++++++-- src/sage/plot/graphics.py | 127 +++++++++++-------------- src/sage/plot/plot3d/tachyon.py | 19 ++-- 5 files changed, 112 insertions(+), 99 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index e657f6fb26b..3bfdf4819b6 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -35,7 +35,6 @@ from sage.misc.randstate cimport randstate, current_randstate from sage.structure.sequence import Sequence from sage.structure.element import is_Vector from sage.misc.misc import verbose, get_verbose -from sage.misc.temporary_file import graphics_filename from sage.rings.number_field.number_field_base import is_NumberField from sage.rings.integer_ring import ZZ, is_IntegerRing from sage.rings.integer import Integer @@ -7924,7 +7923,7 @@ cdef class Matrix(matrix1.Matrix): EXAMPLE:: sage: M = random_matrix(CC, 4) - sage: M.visualize_structure(os.path.join(SAGE_TMP, "matrix.png")) + sage: M.visualize_structure() """ import gd import os @@ -7979,6 +7978,7 @@ cdef class Matrix(matrix1.Matrix): setPixel((y,x), val) if filename is None: + from sage.misc.temporary_file import graphics_filename filename = graphics_filename() im.writePng(filename) diff --git a/src/sage/matrix/matrix_modn_sparse.pyx b/src/sage/matrix/matrix_modn_sparse.pyx index 3cdc57035dc..12a7bb2ca6b 100644 --- a/src/sage/matrix/matrix_modn_sparse.pyx +++ b/src/sage/matrix/matrix_modn_sparse.pyx @@ -642,12 +642,8 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): setPixel( (x,y), colorExact((r-delta,g-delta,b-delta)) ) if filename is None: - from sage.misc.temporary_file import graphics_filename, tmp_dir - from sage.doctest import DOCTEST_MODE + from sage.misc.temporary_file import graphics_filename filename = graphics_filename() - if DOCTEST_MODE: - import os - filename = os.path.join(tmp_dir(), filename) im.writePng(filename) diff --git a/src/sage/misc/temporary_file.py b/src/sage/misc/temporary_file.py index 59ec45bec06..2580311a1d8 100644 --- a/src/sage/misc/temporary_file.py +++ b/src/sage/misc/temporary_file.py @@ -120,7 +120,9 @@ def tmp_filename(name="tmp_", ext=""): - ``name`` -- (default: ``"tmp_"``) A prefix for the file name. - - ``ext`` -- (default: ``""``) A suffix for the file name. + - ``ext`` -- (default: ``""``) A suffix for the file name. If you + want a filename extension in the usual sense, this should start + with a dot. OUTPUT: @@ -147,14 +149,51 @@ def tmp_filename(name="tmp_", ext=""): def graphics_filename(ext='png'): """ - Return the next available canonical filename for a plot/graphics - file. + When run from the Sage notebook, return the next available canonical + filename for a plot/graphics file in the current working directory. + Otherwise, return a temporary file inside ``SAGE_TMP``. + + INPUT: + + - ``ext`` -- (default: ``"png"``) A file extension (without the dot) + for the filename. + + OUTPUT: + + The path of the temporary file created. In the notebook, this is + a filename without path in the current directory. Otherwise, this + an absolute path. + + EXAMPLES:: + + sage: from sage.misc.temporary_file import graphics_filename + sage: print graphics_filename() # random, typical filename for sagenb + sage0.png + + TESTS: + + When doctesting, this returns instead a random temporary file. + We check that it's a file inside ``SAGE_TMP`` and that the extension + is correct:: + + sage: fn = graphics_filename(ext="jpeg") + sage: fn.startswith(str(SAGE_TMP)) + True + sage: fn.endswith('.jpeg') + True """ - i = 0 - while os.path.exists('sage%d.%s'%(i,ext)): - i += 1 - filename = 'sage%d.%s'%(i,ext) - return filename + ext = '.' + ext + # Don't use this unsafe function except in the notebook, #15515 + import sage.plot.plot + if sage.plot.plot.EMBEDDED_MODE: + i = 0 + while os.path.exists('sage%d%s'%(i,ext)): + i += 1 + filename = 'sage%d%s'%(i,ext) + return filename + else: + return tmp_filename(ext=ext) + ################################################################# # write to a temporary file and move it in place diff --git a/src/sage/plot/graphics.py b/src/sage/plot/graphics.py index 1e07333537e..169e194c110 100644 --- a/src/sage/plot/graphics.py +++ b/src/sage/plot/graphics.py @@ -35,7 +35,6 @@ ALLOWED_EXTENSIONS = ['.eps', '.pdf', '.png', '.ps', '.sobj', '.svg'] DEFAULT_DPI = 100 -DOCTEST_MODE_FILE = os.path.join(sage.misc.misc.SAGE_TMP, 'test.png') def show_default(default=None): r""" @@ -1244,7 +1243,7 @@ def _set_scale(self, figure, scale=None, base=None): labelspacing=0.02, loc='best', markerscale=0.6, ncol=1, numpoints=2, shadow=False, title=None) - def show(self, **kwds): + def show(self, filename=None, linkmode=False, **kwds): r""" Show this graphics image with the default image viewer. @@ -1813,24 +1812,20 @@ def show(self, **kwds): ValueError: 'title_pos' must be a list or tuple of two real numbers. """ - # This option should not be passed on to save(). - linkmode = kwds.pop('linkmode', False) + if filename is None: + filename = graphics_filename() - if sage.doctest.DOCTEST_MODE: - kwds.pop('filename', None) - self.save(DOCTEST_MODE_FILE, **kwds) - elif sage.plot.plot.EMBEDDED_MODE: - kwds.setdefault('filename', graphics_filename()) - self.save(**kwds) - if linkmode == True: - return "" % kwds['filename'] + self.save(filename, **kwds) + + if sage.plot.plot.EMBEDDED_MODE: + if linkmode: + return "" % filename else: - html("" % kwds['filename']) - else: - kwds.setdefault('filename', tmp_filename(ext='.png')) - self.save(**kwds) + html("" % filename) + return + if not sage.doctest.DOCTEST_MODE: os.system('%s %s 2>/dev/null 1>/dev/null &' - % (sage.misc.viewer.png_viewer(), kwds['filename'])) + % (sage.misc.viewer.png_viewer(), filename)) def xmin(self, xmin=None): """ @@ -2878,9 +2873,12 @@ def save(self, filename=None, **kwds): fig_tight = options.pop('fig_tight') if filename is None: - filename = options.pop('filename') - if filename is None: - filename = graphics_filename() + try: + filename = options.pop('filename') + except KeyError: + # Put this in except (not in pop()) such that the file is + # only created when needed. + filename = graphics_filename() ext = os.path.splitext(filename)[1].lower() if ext not in ALLOWED_EXTENSIONS: @@ -3222,20 +3220,40 @@ def append(self, g): raise NotImplementedError('Appending to a graphics array is not yet implemented') - def _render(self, filename, dpi=None, figsize=None, axes=None, **args): + def save(self, filename=None, dpi=DEFAULT_DPI, figsize=None, **kwds): r""" - ``_render`` loops over all graphics objects in the array - and adds them to the subplot. This is only used internally - when the plot is actually saved or shown. + Save the ``graphics_array`` to a png called ``filename``. + + We loop over all graphics objects in the array and add them to + a subplot and then render that. + + INPUT: + + - ``filename`` - (default: None) string + + - ``dpi`` - dots per inch + + - ``figsize`` - width or [width, height] + + - ``axes`` - (default: True) EXAMPLES:: - sage: graphics_array([[plot(sin), plot(cos)], [plot(tan), plot(sec)]]) + sage: F = tmp_filename(ext='.png') + sage: L = [plot(sin(k*x),(x,-pi,pi)) for k in [1..3]] + sage: G = graphics_array(L) + sage: G.save(F, dpi=500, axes=False) # long time (6s on sage.math, 2012) TESTS:: - sage: graphics_array([]) + sage: graphics_array([]).save() + sage: graphics_array([[]]).save() """ + if figsize is not None: + self._set_figsize_(figsize) + if filename is None: + filename = graphics_filename() + #glist is a list of Graphics objects: glist = self._glist rows = self._rows @@ -3246,15 +3264,15 @@ def _render(self, filename, dpi=None, figsize=None, axes=None, **args): rows = cols = dims = 1 #make a blank matplotlib Figure: from matplotlib.figure import Figure - figure = Figure(figsize) + figure = Figure(self._figsize) global do_verify do_verify = True for i,g in zip(range(1, dims+1), glist): subplot = figure.add_subplot(rows, cols, i) g.matplotlib(filename, figure=figure, sub=subplot, - verify=do_verify, axes = axes, **args) + verify=do_verify, **kwds) g.save(filename, dpi=dpi, figure=figure, sub=subplot, - verify=do_verify, axes = axes, **args) + verify=do_verify, **kwds) def save_image(self, filename=None, *args, **kwds): r""" @@ -3278,34 +3296,8 @@ def save_image(self, filename=None, *args, **kwds): """ self.save(filename, *args, **kwds) - def save(self, filename=None, dpi=DEFAULT_DPI, figsize=None, - axes = None, **args): - """ - Save the ``graphics_array`` to (for now) a png called - 'filename'. - - OPTIONAL INPUT: - - - ``filename`` - (default: None) string - - - ``dpi`` - dots per inch - - - ``figsize`` - width or [width, height] - - - ``axes`` - (default: True) - - EXAMPLES:: - - sage: F = tmp_filename(ext='.png') - sage: L = [plot(sin(k*x),(x,-pi,pi)) for k in [1..3]] - sage: G = graphics_array(L) - sage: G.save(F,500,axes=False) # long time (6s on sage.math, 2012) - """ - if (figsize is not None): self._set_figsize_(figsize) - self._render(filename, dpi=dpi, figsize=self._figsize, axes = axes, **args) - def show(self, filename=None, dpi=DEFAULT_DPI, figsize=None, - axes = None, **args): + def show(self, filename=None, **kwds): r""" Show this graphics array using the default viewer. @@ -3324,26 +3316,17 @@ def show(self, filename=None, dpi=DEFAULT_DPI, figsize=None, - ``frame`` - (default: False) draw a frame around the image - EXAMPLES: This draws a graphics array with four trig plots and no - axes in any of the plots. + EXAMPLES: - :: + This draws a graphics array with four trig plots and no + axes in any of the plots:: sage: G = graphics_array([[plot(sin), plot(cos)], [plot(tan), plot(sec)]]) sage: G.show(axes=False) """ - if (figsize is not None): self._set_figsize_(figsize) - if sage.doctest.DOCTEST_MODE: - self.save(DOCTEST_MODE_FILE, - dpi=dpi, figsize=self._figsize, axes = axes, **args) - return - if sage.plot.plot.EMBEDDED_MODE: - self.save(filename, dpi=dpi, figsize=self._figsize, axes = axes, **args) - return if filename is None: - filename = tmp_filename(ext='.png') - self._render(filename, dpi=dpi, figsize=self._figsize, axes = axes, **args) - os.system('%s %s 2>/dev/null 1>/dev/null &'%( + filename = graphics_filename() + self.save(filename, **kwds) + if not sage.doctest.DOCTEST_MODE and not sage.plot.plot.EMBEDDED_MODE: + os.system('%s %s 2>/dev/null 1>/dev/null &'%( sage.misc.viewer.png_viewer(), filename)) - - diff --git a/src/sage/plot/plot3d/tachyon.py b/src/sage/plot/plot3d/tachyon.py index 82f9fcdb17f..7c47b39a925 100644 --- a/src/sage/plot/plot3d/tachyon.py +++ b/src/sage/plot/plot3d/tachyon.py @@ -344,20 +344,15 @@ def show(self, verbose=0, extra_opts=''): sage: q.light((-1,-1,10), 1,(1,1,1)) sage: q.texture('s') sage: q.sphere((0,0,0),1,'s') - sage: q.show(verbose = False) + sage: q.show(verbose=False) """ - import sage.plot.plot - if sage.doctest.DOCTEST_MODE: - filename = graphics_filename() - self.save(os.path.join(SAGE_TMP, 'test.png'), verbose=verbose, extra_opts=extra_opts) - return - if sage.plot.plot.EMBEDDED_MODE: - filename = graphics_filename() - self.save(filename, verbose=verbose, extra_opts=extra_opts) - return - filename = tmp_filename(ext='.png') + filename = graphics_filename() self.save(filename, verbose=verbose, extra_opts=extra_opts) - os.system('%s %s 2>/dev/null 1>/dev/null &'%(sage.misc.viewer.png_viewer(), filename)) + + from sage.doctest import DOCTEST_MODE + from sage.plot.plot import EMBEDDED_MODE + if not DOCTEST_MODE and not EMBEDDED_MODE: + os.system('%s %s 2>/dev/null 1>/dev/null &'%(sage.misc.viewer.png_viewer(), filename)) def _res(self): r""" From a64dbbeed92616bbb69ee7dcc9c7a7b340f34420 Mon Sep 17 00:00:00 2001 From: Miguel Marco Date: Fri, 4 Jul 2014 14:42:54 +0200 Subject: [PATCH 465/546] Added a patch to solve some problems with inverse function and temp file names --- build/pkgs/tides/SPKG.txt | 4 ++++ build/pkgs/tides/patches/minc_tides.patch | 25 +++++++++++++++++++++++ build/pkgs/tides/spkg-install | 11 ++++++++++ 3 files changed, 40 insertions(+) create mode 100644 build/pkgs/tides/patches/minc_tides.patch diff --git a/build/pkgs/tides/SPKG.txt b/build/pkgs/tides/SPKG.txt index c4264bc81fd..4b99507b806 100644 --- a/build/pkgs/tides/SPKG.txt +++ b/build/pkgs/tides/SPKG.txt @@ -24,3 +24,7 @@ GPLv3+ == Special Update/Build Instructions == + +minc_tides.patch changes the size of the name of the temporal files, so there is +no problem in systems that use long names. Also solves a bug in the inverse +function \ No newline at end of file diff --git a/build/pkgs/tides/patches/minc_tides.patch b/build/pkgs/tides/patches/minc_tides.patch new file mode 100644 index 00000000000..c8c8d301b42 --- /dev/null +++ b/build/pkgs/tides/patches/minc_tides.patch @@ -0,0 +1,25 @@ +--- libTIDES/minc_tides.c 2014-07-04 14:37:20.861839294 +0200 ++++ b/libTIDES/minc_tides.c 2014-07-04 14:33:03.891862654 +0200 +@@ -40,7 +40,7 @@ + int dense_output = -1, coef_output = 0; + int accepted_steps = 0, rejected_steps = 0; + int ipos = 1; +-char ofname[20]="", cfname[20]=""; ++char ofname[500]="", cfname[500]=""; + FILE *fd, *fc; + + +@@ -347,11 +347,11 @@ + exit(EXIT_FAILURE); + } + if(k == 0) +- ww = 1.e0; ++ ww = p; + else + for(j = 0; j < k; j++) ww -= (u[k-j] *w[j]); + ww /= u[0]; +- return ww*p; ++ return ww; + } + + double exp_mc(double* u, double* v, int k) diff --git a/build/pkgs/tides/spkg-install b/build/pkgs/tides/spkg-install index 2ef25cc797a..cc2938c47f5 100644 --- a/build/pkgs/tides/spkg-install +++ b/build/pkgs/tides/spkg-install @@ -2,6 +2,17 @@ cd src + +for patch in ../patches/*.patch; do + [ -r "$patch" ] || continue # Skip non-existing or non-readable patches + echo "applying '$patch'" + patch -p1 <"$patch" + if [ $? -ne 0 ]; then + echo >&2 "Error applying '$patch'" + exit 1 + fi +done + ./configure --prefix="$SAGE_LOCAL" --libdir="$SAGE_LOCAL/lib" if [ $? -ne 0 ]; then echo >&2 "Error configuring TIDES." From d591ad2728fc5bd0cdc187506f4de6999a5f2d94 Mon Sep 17 00:00:00 2001 From: Emmanuel Charpentier Date: Sun, 6 Jul 2014 11:37:46 +0200 Subject: [PATCH 466/546] fixed R's spkg-src --- build/pkgs/r/checksums.ini | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/r/checksums.ini b/build/pkgs/r/checksums.ini index 765af8a7fcb..ab98ef2f667 100644 --- a/build/pkgs/r/checksums.ini +++ b/build/pkgs/r/checksums.ini @@ -1,4 +1,4 @@ -tarball=r-VERSION.tar.gz -sha1=a9d13932c739cc12667c6a17fabd9361624a1708 -md5=a1ee52446bee81820409661e6d114ab1 -cksum=3701745191 +tarball=r-VERSION.tar.bz2 +sha1=6b0f71b069b627e4f9947799fa1caa4ee4b18eb4 +md5=dd594ad26c3d40dc452071aac77a3759 +cksum=1505471622 From a81f7283ffdcd556512713d076a619422c202cce Mon Sep 17 00:00:00 2001 From: Hao Chen Date: Sun, 10 Nov 2013 16:29:00 +0000 Subject: [PATCH 467/546] trac 15382 Macaulay Resultant of Multivariate Polynomials --- src/sage/rings/polynomial/all.py | 6 + .../rings/polynomial/macaulay_resultant.py | 347 ++++++++++++++++++ 2 files changed, 353 insertions(+) create mode 100644 src/sage/rings/polynomial/macaulay_resultant.py diff --git a/src/sage/rings/polynomial/all.py b/src/sage/rings/polynomial/all.py index 194f885601f..6b3e38ce528 100644 --- a/src/sage/rings/polynomial/all.py +++ b/src/sage/rings/polynomial/all.py @@ -17,6 +17,8 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.misc.lazy_import import lazy_import + # Quotient of polynomial ring from sage.rings.polynomial.polynomial_quotient_ring import PolynomialQuotientRing from sage.rings.polynomial.polynomial_quotient_ring_element import PolynomialQuotientRingElement @@ -44,3 +46,7 @@ # Evaluation of cyclotomic polynomials from sage.rings.polynomial.cyclotomic import cyclotomic_value + +# Macaulay resultant for multivariable polynomials +lazy_import('sage.rings.polynomial.macaulay_resultant', 'macaulay_resultant') +lazy_import('sage.rings.polynomial.macaulay_resultant', 'macaulay_general_resultant') diff --git a/src/sage/rings/polynomial/macaulay_resultant.py b/src/sage/rings/polynomial/macaulay_resultant.py new file mode 100644 index 00000000000..3edf76d8e1e --- /dev/null +++ b/src/sage/rings/polynomial/macaulay_resultant.py @@ -0,0 +1,347 @@ +""" +Macaulay Resultant of Multivariate Polynomials + +This is an implementation of the Macaulay Resultant. It computes +the resultant of universal polynomials as well as polynomials +with constant coefficients. This is a project done in +sage days 55. It's based on the implementation in Maple by +Manfred Minimair, which in turn is based on the following: + +-Using Algebraic Geometry by Cox, Little, O'Shea +-Canny, J., "Generalised characteristic polynomials", p.241--250, + J. Symbolic Comput. Vol. 9, No. 3, 1990 +-The algebraic theory of modular systems by Macaulay + + +AUTHORS: + +- Hao Chen +- Solomon Vishkautsan + +""" + +from sage.combinat.integer_vector_weighted import WeightedIntegerVectors +from sage.misc.misc_c import prod +from sage.matrix.constructor import matrix +from sage.rings.integer_ring import ZZ +from sage.rings.arith import binomial +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + +def getS(mon_deg_tuple,dlist): + r""" + In the MR algorithm the list of all monomials of the total degree is partitioned into sets S_i. + This function returns the index i for the set S_i for the inputted given monomial. + + INPUT: + + - `mon_deg_tuple` -- a list representing a monomial of a degree d + - `dlist` -- a list of degrees d_i of the polynomials in question, where + d = sum(dlist) - len(dlist) + 1 + + OUTPUT: + + - the index i such that the input monomial lives in S_i + + EXAMPLES:: + + sage: from sage.rings.polynomial.macaulay_resultant import getS + sage: getS([1,1,0],[2,1,1]) # the monomial xy where the total degree = 2 + 1 + + sage: getS([29,21,8],[10,20,30]) + 0 + + sage: getS(range(0,9)+[10],range(1,11)) + 9 + """ + for i in xrange(len(dlist)): + if mon_deg_tuple[i] - dlist[i] >= 0: + return i + + +def monomials(d,R): + r""" + returns all the monomials of degree d with variables a list of + generators of the ring R + + INPUT: + + -`d` -- a positive integer + -`R` -- a polynoimal ring + + OUTPUT: + + - a list of all monomials of degree d. + + EXAMPLES:: + + sage: from sage.rings.polynomial.macaulay_resultant import monomials + sage: monomials(3, PolynomialRing(QQ,3,'x')) + [x0^3, x0^2*x1, x0^2*x2, x0*x1^2, x0*x1*x2, x0*x2^2, x1^3, x1^2*x2, x1*x2^2, x2^3] + + It's OK if the coefficients of R live in some polynomial ring:: + + sage: U = PolynomialRing(QQ,20,'u') + sage: monomials(2, PolynomialRing(U,5,'x')) + [x0^2, x0*x1, x0*x2, x0*x3, x0*x4, x1^2, x1*x2, x1*x3, x1*x4, x2^2, x2*x3, x2*x4, x3^2, x3*x4, x4^2] + + """ + xlist = R.gens() + n = len(xlist) -1 + one_list = [1 for i in xrange(0,n+1)] + degs = WeightedIntegerVectors(d, one_list) + return [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) + for deg in degs] + +def is_reduced(mon,dlist,xlist): + r""" + A monomial in the variables x_0,...,x_n is called reduced with respect to the list of degrees d_0,...,d_n + if the degree of x_i in the monomial is >= d_i for exactly one i. This function checks this property for an inputted monomial. + + INPUT: + + - mon -- a monomial in the variables listed in xlist + - dlist -- a list of degrees with respect to which we check reducedness + - xlist -- a list of variables in some Polynomial ring. + + OUTPUT: + + - True/False + + EXAMPLES:: + + sage: from sage.rings.polynomial.macaulay_resultant import is_reduced + sage: R. = PolynomialRing(QQ,3) + sage: is_reduced(x^2*y^3*z,[2,3,3],[x,y,z]) + False + + sage: R. = PolynomialRing(QQ,3) + sage: is_reduced(x*y^3*z^2,[2,3,3],[x,y,z]) + True + """ + deg = [mon.degree(xi) for xi in xlist] + diff = [deg[i] - dlist[i] for i in xrange(0,len(dlist))] + return len([1 for d in diff if d >= 0]) == 1 + +def construct_universal_polynomial(dlist): + r""" + Given a list of degrees, this function returns a list of len(dlist) polynomials with len(dlist) variables, + with generic coefficients. This is useful for generating polynomials for tests, + and for getting the general resultant for the given degrees. + + INPUT: + + - dlist -- a list of degrees. + + OUTPUT: + + - a list of polynomials of the given degrees with general coefficients. + - a polynomial ring over ZZ generated by the coefficients of the output polynomials. + + EXAMPLES:: + + sage: from sage.rings.polynomial.macaulay_resultant import construct_universal_polynomial + sage: construct_universal_polynomial([1,1,2]) + ([u0*x0 + u1*x1 + u2*x2, u3*x0 + u4*x1 + u5*x2, u6*x0^2 + u7*x0*x1 + u9*x1^2 + u8*x0*x2 + u10*x1*x2 + u11*x2^2], + Multivariate Polynomial Ring in u0, u1, u2, u3, u4, u5, u6, u7, u8, u9, u10, u11 over Integer Ring) + """ + + n = len(dlist) - 1 + number_of_coeffs = sum([binomial(n+di,di) for di in dlist]) + U = PolynomialRing(ZZ,'u',number_of_coeffs) + d = sum(dlist) - len(dlist) + 1 + flist = [] + R = PolynomialRing(U,'x',n+1) + #print R + ulist = U.gens() + for d in dlist: + # construct a universal polynomial of degree d + # suppose we already have mon_d + mon_d = monomials(d,R) + #print mon_d + f = sum([mon_d[i]*ulist[i] for i in xrange(0,len(mon_d))]) + flist.append (f) + #print f + ulist = ulist[len(mon_d):] + return flist, U + +def macaulay_resultant(flist): + r""" + This is an implementation of the Macaulay Resultant. It computes + the resultant of universal polynomials as well as polynomials + with constant coefficients. This is a project done in + sage days 55. It's based on the implementation in Maple by + Manfred Minimair, which in turn is based on the following: + + -Using Algebraic Geometry by Cox, Little, O'Shea + -Canny, J., "Generalised characteristic polynomials", p.241--250, + J. Symbolic Comput. Vol. 9, No. 3, 1990 + -The algebraic theory of modular systems by Macaulay + + It calculates the Macaulay resultant for a list of Polynomials, + up to sign! + + AUTHORS: + + - Hao Chen + - Solomon Vishkautsan + + + INPUT: + + - flist -- a list of n homogeneous polynomials in n variables + + OUTPUT: + + - the resultant + + EXAMPLES: + + The polynomials need to be all homogeneous:: + + sage: from sage.rings.polynomial.macaulay_resultant import * + sage: R. = PolynomialRing(QQ,3) + sage: macaulay_resultant([y, x+z, z+x^3]) + Traceback (most recent call last): + ... + AssertionError: resultant for non-homogeneous polynomials is not supported + + + The number of polynomials has to match the number of variables:: + + sage: R. = PolynomialRing(QQ,3) + sage: macaulay_resultant([y,x+z]) + Traceback (most recent call last): + ... + AssertionError: number of polynomials(= 2) must equal to number of variables (= 3) + + + + The following example recreates Proposition 2.10 in Ch.3 of Using Algebraic Geometry:: + + + sage: flist,_ = construct_universal_polynomial([1,1,2]) + sage: macaulay_resultant(flist) + u2^2*u4^2*u6 - 2*u1*u2*u4*u5*u6 + u1^2*u5^2*u6 - u2^2*u3*u4*u7 + u1*u2*u3*u5*u7 + u0*u2*u4*u5*u7 - u0*u1*u5^2*u7 + u1*u2*u3*u4*u8 - u0*u2*u4^2*u8 - u1^2*u3*u5*u8 + u0*u1*u4*u5*u8 + u2^2*u3^2*u9 - 2*u0*u2*u3*u5*u9 + u0^2*u5^2*u9 - u1*u2*u3^2*u10 + u0*u2*u3*u4*u10 + u0*u1*u3*u5*u10 - u0^2*u4*u5*u10 + u1^2*u3^2*u11 - 2*u0*u1*u3*u4*u11 + u0^2*u4^2*u11 + + The following example degenerates into the determinant of a 3*3 matrix:: + + sage: flist,_ = construct_universal_polynomial([1,1,1]) + sage: macaulay_resultant(flist) + -u2*u4*u6 + u1*u5*u6 + u2*u3*u7 - u0*u5*u7 - u1*u3*u8 + u0*u4*u8 + + The following example is by Patrick Ingram(arxiv:1310.4114):: + + sage: U = PolynomialRing(ZZ,'y',2); y0,y1 = U.gens() + sage: R = PolynomialRing(U,'x',3); x0,x1,x2 = R.gens() + sage: f0 = y0*x2^2 - x0^2 + 2*x1*x2 + sage: f1 = y1*x2^2 - x1^2 + 2*x0*x2 + sage: f2 = x0*x1 - x2^2 + sage: flist = [f0,f1,f2] + sage: macaulay_resultant([f0,f1,f2]) + y0^2*y1^2 - 4*y0^3 - 4*y1^3 + 18*y0*y1 - 27 + + a simple example with constant rational coefficients:: + + sage: R. = PolynomialRing(QQ,4) + sage: macaulay_resultant([w,z,y,x]) + 1 + + an example where the resultant vanishes:: + + sage: R. = PolynomialRing(QQ,3) + sage: macaulay_resultant([x+y,y^2,x]) + 0 + + an example of bad reduction at a prime p = 5:: + + sage: R. = PolynomialRing(QQ,3) + sage: macaulay_resultant([y,x^3+25*y^2*x,5*z]) + 125 + + an example when the coefficients live in a finite field:: + + sage: F = FiniteField(11) + sage: R. = PolynomialRing(F,4) + sage: macaulay_resultant([z,x^3,5*y,w]) + 4 + + + example when the denominator in the algorithm vanishes(in this case + the resultant is the constant term of the quotient of + char polynomials of numerator/denominator):: + + sage: R. = PolynomialRing(QQ,3) + sage: macaulay_resultant([y, x+z, z^2]) + -1 + + when there are only 2 functions, macaulay resultant degenerates to the traditional resultant:: + + sage: R. = PolynomialRing(QQ,1) + sage: f = x^2+1; g = x^5+1 + sage: f.resultant(g) == macaulay_resultant([f.homogenize(),g.homogenize()]) + True + + """ + assert len(flist) > 0, 'you have to input least 1 polynomial in the list' + assert all([f.is_homogeneous() for f in flist]), 'resultant for non-homogeneous polynomials is not supported' + R = flist[0].parent() + dlist = [f.degree() for f in flist] + xlist = R.gens() + assert len(xlist) == len(dlist), 'number of polynomials(= %d) must equal to number of variables (= %d)' % (len(dlist),len(xlist)) + n = len(dlist) - 1 + d = sum(dlist) - len(dlist) + 1 + one_list = [1 for i in xrange(0,len(dlist))] + degs = WeightedIntegerVectors(d, one_list) + mon_d = [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) for deg in degs] + M = len(mon_d) + mons_to_keep = [] + for j in xrange(0,M): + if not is_reduced(mon_d[j],dlist,xlist): + mons_to_keep.append(j) + newflist = [] + for mon in mon_d: + degs_mon = [mon.degree(x) for x in xlist] + si_mon = getS(degs_mon, dlist) + degs_mon[si_mon] -= dlist[si_mon] + quo = prod([xlist[k]**(degs_mon[k]) for k in xrange(0,n+1)]) + newflist.append(flist[si_mon]*quo) + result = [] + for f in newflist: + result.append([f.monomial_coefficient(mon) for mon in mon_d]) + numer_matrix = matrix(result) + denom_matrix = numer_matrix.matrix_from_rows_and_columns(mons_to_keep,mons_to_keep) + if denom_matrix.dimensions()[0] == 0: + return numer_matrix.det() + denom_det = denom_matrix.det() + if denom_det != 0: + return numer_matrix.det()/denom_det + poly_num = numer_matrix.characteristic_polynomial('T') + poly_denom = denom_matrix.characteristic_polynomial('T') + poly_quo = poly_num.quo_rem(poly_denom)[0] + return poly_quo(0) + + +def macaulay_general_resultant(dlist): + r""" + this is just a wrapper function of macaulay_resultant, where + it takes a list of degrees as input and returns the resultant of + a list of generic polynomials with coefficients in a polynomial ring. + + INPUT: + + - dlist -- a list of degrees + + OUTPUT: + + - the general resultant + + EXAMPLES:: + + sage: from sage.rings.polynomial.macaulay_resultant import macaulay_general_resultant + sage: macaulay_general_resultant([1,1,2]) + u2^2*u4^2*u6 - 2*u1*u2*u4*u5*u6 + u1^2*u5^2*u6 - u2^2*u3*u4*u7 + u1*u2*u3*u5*u7 + u0*u2*u4*u5*u7 - u0*u1*u5^2*u7 + u1*u2*u3*u4*u8 - u0*u2*u4^2*u8 - u1^2*u3*u5*u8 + u0*u1*u4*u5*u8 + u2^2*u3^2*u9 - 2*u0*u2*u3*u5*u9 + u0^2*u5^2*u9 - u1*u2*u3^2*u10 + u0*u2*u3*u4*u10 + u0*u1*u3*u5*u10 - u0^2*u4*u5*u10 + u1^2*u3^2*u11 - 2*u0*u1*u3*u4*u11 + u0^2*u4^2*u11 + """ + assert all([d >=0 for d in dlist]), 'degrees must be non-negative' + flist, U = construct_universal_polynomial(dlist) + return U(macaulay_resultant(flist)) From e08b29cb17ad20ac7e33ba5917ff3c1c34d99fdc Mon Sep 17 00:00:00 2001 From: Solomon Vishkautsan Date: Sun, 1 Dec 2013 14:01:40 +0200 Subject: [PATCH 468/546] improve performance of macaulay_resultant --- .../rings/polynomial/macaulay_resultant.py | 54 ++++++++++++------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/src/sage/rings/polynomial/macaulay_resultant.py b/src/sage/rings/polynomial/macaulay_resultant.py index 3edf76d8e1e..cdb97105a0c 100644 --- a/src/sage/rings/polynomial/macaulay_resultant.py +++ b/src/sage/rings/polynomial/macaulay_resultant.py @@ -93,7 +93,7 @@ def monomials(d,R): return [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) for deg in degs] -def is_reduced(mon,dlist,xlist): +def is_reduced(mon_degs,dlist,xlist): r""" A monomial in the variables x_0,...,x_n is called reduced with respect to the list of degrees d_0,...,d_n if the degree of x_i in the monomial is >= d_i for exactly one i. This function checks this property for an inputted monomial. @@ -119,8 +119,10 @@ def is_reduced(mon,dlist,xlist): sage: is_reduced(x*y^3*z^2,[2,3,3],[x,y,z]) True """ - deg = [mon.degree(xi) for xi in xlist] - diff = [deg[i] - dlist[i] for i in xrange(0,len(dlist))] + #TODO fix comments to reflect change in input parameters + #RRR deg = [mon.degree(xi) for xi in xlist] + + diff = [mon_degs[i] - dlist[i] for i in xrange(0,len(dlist))] return len([1 for d in diff if d >= 0]) == 1 def construct_universal_polynomial(dlist): @@ -152,6 +154,7 @@ def construct_universal_polynomial(dlist): d = sum(dlist) - len(dlist) + 1 flist = [] R = PolynomialRing(U,'x',n+1) + #TODO remove ugly prints #print R ulist = U.gens() for d in dlist: @@ -219,7 +222,6 @@ def macaulay_resultant(flist): The following example recreates Proposition 2.10 in Ch.3 of Using Algebraic Geometry:: - sage: flist,_ = construct_universal_polynomial([1,1,2]) sage: macaulay_resultant(flist) u2^2*u4^2*u6 - 2*u1*u2*u4*u5*u6 + u1^2*u5^2*u6 - u2^2*u3*u4*u7 + u1*u2*u3*u5*u7 + u0*u2*u4*u5*u7 - u0*u1*u5^2*u7 + u1*u2*u3*u4*u8 - u0*u2*u4^2*u8 - u1^2*u3*u5*u8 + u0*u1*u4*u5*u8 + u2^2*u3^2*u9 - 2*u0*u2*u3*u5*u9 + u0^2*u5^2*u9 - u1*u2*u3^2*u10 + u0*u2*u3*u4*u10 + u0*u1*u3*u5*u10 - u0^2*u4*u5*u10 + u1^2*u3^2*u11 - 2*u0*u1*u3*u4*u11 + u0^2*u4^2*u11 @@ -283,32 +285,46 @@ def macaulay_resultant(flist): True """ + #TODO add test that checks that the output of the function is a polynomial assert len(flist) > 0, 'you have to input least 1 polynomial in the list' assert all([f.is_homogeneous() for f in flist]), 'resultant for non-homogeneous polynomials is not supported' R = flist[0].parent() dlist = [f.degree() for f in flist] xlist = R.gens() - assert len(xlist) == len(dlist), 'number of polynomials(= %d) must equal to number of variables (= %d)' % (len(dlist),len(xlist)) + assert len(xlist) == len(dlist), 'number of polynomials(= %d) must equal number of variables (= %d)' % (len(dlist),len(xlist)) n = len(dlist) - 1 d = sum(dlist) - len(dlist) + 1 one_list = [1 for i in xrange(0,len(dlist))] - degs = WeightedIntegerVectors(d, one_list) - mon_d = [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) for deg in degs] - M = len(mon_d) + mons = WeightedIntegerVectors(d, one_list) # returns a list of integer vectors representing the list of all monomials of degree d + #RRR mon_d = [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) for deg in degs] + #RRR M = len(mon_d) + mons_num = len(mons) mons_to_keep = [] - for j in xrange(0,M): - if not is_reduced(mon_d[j],dlist,xlist): - mons_to_keep.append(j) newflist = [] - for mon in mon_d: - degs_mon = [mon.degree(x) for x in xlist] - si_mon = getS(degs_mon, dlist) - degs_mon[si_mon] -= dlist[si_mon] - quo = prod([xlist[k]**(degs_mon[k]) for k in xrange(0,n+1)]) - newflist.append(flist[si_mon]*quo) result = [] - for f in newflist: - result.append([f.monomial_coefficient(mon) for mon in mon_d]) + + for j in xrange(0,mons_num): + if not is_reduced(mons[j],dlist,xlist): + mons_to_keep.append(j) + si_mon = getS(mons[j], dlist) + # Monomial is in S_i under the partition, now we reduce the i'th degree of the monomial + new_mon = list(mons[j]) + new_mon[si_mon] -= dlist[si_mon] + quo = prod([xlist[k]**(new_mon[k]) for k in xrange(0,n+1)]) # this produces the actual monomial + new_f = flist[si_mon]*quo + print(new_f) + # we strip the coefficients of the new polynomial: + result.append([new_f.monomial_coefficient(prod([xlist[k]**(mon[k]) for k in xrange(0,n+1)])) for mon in mons]) + + # for mon in mon_d: + # degs_mon = [mon.degree(x) for x in xlist] + + # degs_mon[si_mon] -= dlist[si_mon] + # quo = prod([xlist[k]**(degs_mon[k]) for k in xrange(0,n+1)]) + # newflist.append(flist[si_mon]*quo) + + #for f in newflist: + # result.append([f.monomial_coefficient(mon) for mon in mon_d]) numer_matrix = matrix(result) denom_matrix = numer_matrix.matrix_from_rows_and_columns(mons_to_keep,mons_to_keep) if denom_matrix.dimensions()[0] == 0: From 95639b7dce4a4fb4c425f2d39a73f309a18bf731 Mon Sep 17 00:00:00 2001 From: Solomon Vishkautsan Date: Sun, 1 Dec 2013 21:49:52 +0200 Subject: [PATCH 469/546] some fixes to performance issues --- src/sage/rings/polynomial/macaulay_resultant.py | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/src/sage/rings/polynomial/macaulay_resultant.py b/src/sage/rings/polynomial/macaulay_resultant.py index cdb97105a0c..f8155746ab0 100644 --- a/src/sage/rings/polynomial/macaulay_resultant.py +++ b/src/sage/rings/polynomial/macaulay_resultant.py @@ -296,8 +296,7 @@ def macaulay_resultant(flist): d = sum(dlist) - len(dlist) + 1 one_list = [1 for i in xrange(0,len(dlist))] mons = WeightedIntegerVectors(d, one_list) # returns a list of integer vectors representing the list of all monomials of degree d - #RRR mon_d = [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) for deg in degs] - #RRR M = len(mon_d) + mon_d = [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) for deg in mons] mons_num = len(mons) mons_to_keep = [] newflist = [] @@ -312,19 +311,9 @@ def macaulay_resultant(flist): new_mon[si_mon] -= dlist[si_mon] quo = prod([xlist[k]**(new_mon[k]) for k in xrange(0,n+1)]) # this produces the actual monomial new_f = flist[si_mon]*quo - print(new_f) # we strip the coefficients of the new polynomial: - result.append([new_f.monomial_coefficient(prod([xlist[k]**(mon[k]) for k in xrange(0,n+1)])) for mon in mons]) - - # for mon in mon_d: - # degs_mon = [mon.degree(x) for x in xlist] - - # degs_mon[si_mon] -= dlist[si_mon] - # quo = prod([xlist[k]**(degs_mon[k]) for k in xrange(0,n+1)]) - # newflist.append(flist[si_mon]*quo) - - #for f in newflist: - # result.append([f.monomial_coefficient(mon) for mon in mon_d]) + result.append([new_f.monomial_coefficient(mon) for mon in mon_d]) + numer_matrix = matrix(result) denom_matrix = numer_matrix.matrix_from_rows_and_columns(mons_to_keep,mons_to_keep) if denom_matrix.dimensions()[0] == 0: From d258bf8cab6d5700c90051df7d707db9488035d7 Mon Sep 17 00:00:00 2001 From: Solomon Vishkautsan Date: Tue, 3 Dec 2013 09:49:56 +0200 Subject: [PATCH 470/546] minor changes --- .../rings/polynomial/macaulay_resultant.py | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/sage/rings/polynomial/macaulay_resultant.py b/src/sage/rings/polynomial/macaulay_resultant.py index f8155746ab0..3fef0d90ab3 100644 --- a/src/sage/rings/polynomial/macaulay_resultant.py +++ b/src/sage/rings/polynomial/macaulay_resultant.py @@ -93,7 +93,7 @@ def monomials(d,R): return [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) for deg in degs] -def is_reduced(mon_degs,dlist,xlist): +def is_reduced(mon_degs,dlist): r""" A monomial in the variables x_0,...,x_n is called reduced with respect to the list of degrees d_0,...,d_n if the degree of x_i in the monomial is >= d_i for exactly one i. This function checks this property for an inputted monomial. @@ -112,16 +112,15 @@ def is_reduced(mon_degs,dlist,xlist): sage: from sage.rings.polynomial.macaulay_resultant import is_reduced sage: R. = PolynomialRing(QQ,3) - sage: is_reduced(x^2*y^3*z,[2,3,3],[x,y,z]) + sage: is_reduced([2,3,1],[2,3,3]) # the monomial x^2*y^3*z is not reduced w.r.t. degrees vector [2,3,3] False sage: R. = PolynomialRing(QQ,3) - sage: is_reduced(x*y^3*z^2,[2,3,3],[x,y,z]) + sage: is_reduced([1,3,2],[2,3,3]) # the monomial x*y^3*z^2 is not reduced w.r.t. degrees vector [2,3,3] True """ #TODO fix comments to reflect change in input parameters #RRR deg = [mon.degree(xi) for xi in xlist] - diff = [mon_degs[i] - dlist[i] for i in xrange(0,len(dlist))] return len([1 for d in diff if d >= 0]) == 1 @@ -200,25 +199,24 @@ def macaulay_resultant(flist): EXAMPLES: - The polynomials need to be all homogeneous:: - - sage: from sage.rings.polynomial.macaulay_resultant import * - sage: R. = PolynomialRing(QQ,3) - sage: macaulay_resultant([y, x+z, z+x^3]) - Traceback (most recent call last): - ... - AssertionError: resultant for non-homogeneous polynomials is not supported - The number of polynomials has to match the number of variables:: sage: R. = PolynomialRing(QQ,3) sage: macaulay_resultant([y,x+z]) + ... Traceback (most recent call last): ... AssertionError: number of polynomials(= 2) must equal to number of variables (= 3) + The polynomials need to be all homogeneous:: + sage: from sage.rings.polynomial.macaulay_resultant import * + sage: R. = PolynomialRing(QQ,3) + sage: macaulay_resultant([y, x+z, z+x^3]) + Traceback (most recent call last): + ... + AssertionError: resultant for non-homogeneous polynomials is not supported The following example recreates Proposition 2.10 in Ch.3 of Using Algebraic Geometry:: @@ -283,7 +281,6 @@ def macaulay_resultant(flist): sage: f = x^2+1; g = x^5+1 sage: f.resultant(g) == macaulay_resultant([f.homogenize(),g.homogenize()]) True - """ #TODO add test that checks that the output of the function is a polynomial assert len(flist) > 0, 'you have to input least 1 polynomial in the list' @@ -291,11 +288,11 @@ def macaulay_resultant(flist): R = flist[0].parent() dlist = [f.degree() for f in flist] xlist = R.gens() - assert len(xlist) == len(dlist), 'number of polynomials(= %d) must equal number of variables (= %d)' % (len(dlist),len(xlist)) + assert len(xlist) == len(dlist), 'number of polynomials(= %d) must equal number of variables (= %d)'%(len(dlist),len(xlist)) n = len(dlist) - 1 d = sum(dlist) - len(dlist) + 1 one_list = [1 for i in xrange(0,len(dlist))] - mons = WeightedIntegerVectors(d, one_list) # returns a list of integer vectors representing the list of all monomials of degree d + mons = WeightedIntegerVectors(d, one_list) # returns a list of integer vectors representing the list of all monomials of degree d mon_d = [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) for deg in mons] mons_num = len(mons) mons_to_keep = [] @@ -303,7 +300,7 @@ def macaulay_resultant(flist): result = [] for j in xrange(0,mons_num): - if not is_reduced(mons[j],dlist,xlist): + if not is_reduced(mons[j],dlist): mons_to_keep.append(j) si_mon = getS(mons[j], dlist) # Monomial is in S_i under the partition, now we reduce the i'th degree of the monomial From ca921413390ce2ca579e69031a1f7f9731a025dd Mon Sep 17 00:00:00 2001 From: Solomon Vishkautsan Date: Tue, 3 Dec 2013 16:09:39 +0200 Subject: [PATCH 471/546] finished implementing minor performance improvements. There is still a failed test to take care of. --- src/sage/rings/polynomial/macaulay_resultant.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/polynomial/macaulay_resultant.py b/src/sage/rings/polynomial/macaulay_resultant.py index 3fef0d90ab3..41fd5134f41 100644 --- a/src/sage/rings/polynomial/macaulay_resultant.py +++ b/src/sage/rings/polynomial/macaulay_resultant.py @@ -204,7 +204,6 @@ def macaulay_resultant(flist): sage: R. = PolynomialRing(QQ,3) sage: macaulay_resultant([y,x+z]) - ... Traceback (most recent call last): ... AssertionError: number of polynomials(= 2) must equal to number of variables (= 3) @@ -293,7 +292,7 @@ def macaulay_resultant(flist): d = sum(dlist) - len(dlist) + 1 one_list = [1 for i in xrange(0,len(dlist))] mons = WeightedIntegerVectors(d, one_list) # returns a list of integer vectors representing the list of all monomials of degree d - mon_d = [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) for deg in mons] + mon_d = [prod([xlist[i]**(deg[i]) for i in xrange(0,len(deg))]) for deg in mons] mons_num = len(mons) mons_to_keep = [] newflist = [] @@ -303,9 +302,9 @@ def macaulay_resultant(flist): if not is_reduced(mons[j],dlist): mons_to_keep.append(j) si_mon = getS(mons[j], dlist) - # Monomial is in S_i under the partition, now we reduce the i'th degree of the monomial - new_mon = list(mons[j]) - new_mon[si_mon] -= dlist[si_mon] + # Monomial is in S_i under the partition, now we reduce the i'th degree of the monomial + new_mon = list(mons[j]) + new_mon[si_mon] -= dlist[si_mon] quo = prod([xlist[k]**(new_mon[k]) for k in xrange(0,n+1)]) # this produces the actual monomial new_f = flist[si_mon]*quo # we strip the coefficients of the new polynomial: From 3ece4d94f266db4a0c16f54079c9a342c4c336f0 Mon Sep 17 00:00:00 2001 From: Solomon Vishkautsan Date: Thu, 5 Dec 2013 10:10:32 +0200 Subject: [PATCH 472/546] removed all usage of actual monomials, only using degree-lists now. --- src/sage/rings/polynomial/macaulay_resultant.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/macaulay_resultant.py b/src/sage/rings/polynomial/macaulay_resultant.py index 41fd5134f41..a71de0e75c6 100644 --- a/src/sage/rings/polynomial/macaulay_resultant.py +++ b/src/sage/rings/polynomial/macaulay_resultant.py @@ -206,7 +206,7 @@ def macaulay_resultant(flist): sage: macaulay_resultant([y,x+z]) Traceback (most recent call last): ... - AssertionError: number of polynomials(= 2) must equal to number of variables (= 3) + AssertionError: number of polynomials(= 2) must equal number of variables (= 3) The polynomials need to be all homogeneous:: @@ -292,7 +292,7 @@ def macaulay_resultant(flist): d = sum(dlist) - len(dlist) + 1 one_list = [1 for i in xrange(0,len(dlist))] mons = WeightedIntegerVectors(d, one_list) # returns a list of integer vectors representing the list of all monomials of degree d - mon_d = [prod([xlist[i]**(deg[i]) for i in xrange(0,len(deg))]) for deg in mons] + #mon_d = [prod([xlist[i]**(deg[i]) for i in xrange(0,len(deg))]) for deg in mons] mons_num = len(mons) mons_to_keep = [] newflist = [] @@ -308,7 +308,7 @@ def macaulay_resultant(flist): quo = prod([xlist[k]**(new_mon[k]) for k in xrange(0,n+1)]) # this produces the actual monomial new_f = flist[si_mon]*quo # we strip the coefficients of the new polynomial: - result.append([new_f.monomial_coefficient(mon) for mon in mon_d]) + result.append([new_f[mon] for mon in mons]) numer_matrix = matrix(result) denom_matrix = numer_matrix.matrix_from_rows_and_columns(mons_to_keep,mons_to_keep) From c99a01a11eeb03a428c5024c4dc4cd1d14e6522e Mon Sep 17 00:00:00 2001 From: Solomon Vishkautsan Date: Thu, 5 Dec 2013 11:25:02 +0200 Subject: [PATCH 473/546] minor changes to comments --- src/sage/rings/polynomial/macaulay_resultant.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/macaulay_resultant.py b/src/sage/rings/polynomial/macaulay_resultant.py index a71de0e75c6..4cf7197642c 100644 --- a/src/sage/rings/polynomial/macaulay_resultant.py +++ b/src/sage/rings/polynomial/macaulay_resultant.py @@ -291,7 +291,7 @@ def macaulay_resultant(flist): n = len(dlist) - 1 d = sum(dlist) - len(dlist) + 1 one_list = [1 for i in xrange(0,len(dlist))] - mons = WeightedIntegerVectors(d, one_list) # returns a list of integer vectors representing the list of all monomials of degree d + mons = WeightedIntegerVectors(d, one_list) # list of exponent-vectors(/lists) of monomials of degree d #mon_d = [prod([xlist[i]**(deg[i]) for i in xrange(0,len(deg))]) for deg in mons] mons_num = len(mons) mons_to_keep = [] @@ -305,18 +305,20 @@ def macaulay_resultant(flist): # Monomial is in S_i under the partition, now we reduce the i'th degree of the monomial new_mon = list(mons[j]) new_mon[si_mon] -= dlist[si_mon] - quo = prod([xlist[k]**(new_mon[k]) for k in xrange(0,n+1)]) # this produces the actual monomial + quo = prod([xlist[k]**(new_mon[k]) for k in xrange(0,n+1)]) # this produces the actual reduced monomial new_f = flist[si_mon]*quo # we strip the coefficients of the new polynomial: result.append([new_f[mon] for mon in mons]) numer_matrix = matrix(result) denom_matrix = numer_matrix.matrix_from_rows_and_columns(mons_to_keep,mons_to_keep) - if denom_matrix.dimensions()[0] == 0: + if denom_matrix.dimensions()[0] == 0: # here we choose the determinant of an empty matrix to be 1 return numer_matrix.det() denom_det = denom_matrix.det() if denom_det != 0: return numer_matrix.det()/denom_det + # if we get to this point, the determinant of the denominator was 0, and we get the resultant + # by taking the free coefficient of the quotient of two characteristic polynomials poly_num = numer_matrix.characteristic_polynomial('T') poly_denom = denom_matrix.characteristic_polynomial('T') poly_quo = poly_num.quo_rem(poly_denom)[0] From dbc80a0ad1198a4a17746497b8893a8662109076 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Sun, 6 Jul 2014 18:33:54 +0200 Subject: [PATCH 474/546] Automatically call setup_latex_preamble As pointed out in http://trac.sagemath.org/ticket/16557#comment:2, the graphs package automatically calls setup_latex_preamble when needed. So we introduce the same mechanism for finite state machines. This also removes setup_latex_preamble from the introductory documentation. Moreover, setup_latex_preamble is decorated by @cached_function, as in the graphs package. --- src/sage/combinat/finite_state_machine.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 80465636cc0..6ead48bb1bb 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -152,12 +152,10 @@ \path[->] (v0) edge[loop above] node {$0$} (); \end{tikzpicture} -We can turn this into a graphical representation. Before doing this, -we have to :func:`setup the latex preamble `. +We can turn this into a graphical representation. :: - sage: sage.combinat.finite_state_machine.setup_latex_preamble() sage: view(NAF) # not tested To actually see this, use the live documentation in the Sage notebook @@ -3125,9 +3123,6 @@ def latex_options(self, means, it can be combined with directly setting some attributes as outlined above. - See also :func:`setup_latex_preamble` or the example below on - how to setup the LaTeX environment. - EXAMPLES: See also the section on :ref:`finite_state_machine_LaTeX_output` @@ -3135,8 +3130,6 @@ def latex_options(self, :: - sage: from sage.combinat.finite_state_machine import setup_latex_preamble - sage: setup_latex_preamble() sage: T = Transducer(initial_states=['I'], ....: final_states=[0, 3]) sage: for j in srange(4): @@ -3430,6 +3423,8 @@ def label_rotation(angle, both_directions): anchor_label = "north" return "rotate=%.2f, anchor=%s" % (angle_label, anchor_label) + setup_latex_preamble() + options = ["auto", "initial text=", ">=latex"] nonempty_final_word_out = False @@ -8891,8 +8886,9 @@ def get_next_transition(self, word_in): #***************************************************************************** +@cached_function def setup_latex_preamble(): - """ + r""" This function adds the package ``tikz`` with support for automata to the preamble of Latex so that the finite state machines can be drawn nicely. @@ -8912,10 +8908,13 @@ def setup_latex_preamble(): sage: from sage.combinat.finite_state_machine import setup_latex_preamble sage: setup_latex_preamble() + sage: ("\usepackage{tikz}" in latex.extra_preamble()) == latex.has_file("tikz.sty") + True """ latex.add_package_to_preamble_if_available('tikz') latex.add_to_mathjax_avoid_list("tikz") - latex.add_to_preamble('\\usetikzlibrary{automata}') + if latex.has_file("tikz.sty"): + latex.add_to_preamble(r'\usetikzlibrary{automata}') #***************************************************************************** From 1bfb513ca3a9dc11a232bdd6ee31625fe5822572 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Sun, 6 Jul 2014 19:03:28 +0200 Subject: [PATCH 475/546] Forgot to import cached_function --- src/sage/combinat/finite_state_machine.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 6ead48bb1bb..ad28a71ce94 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -557,6 +557,7 @@ from sage.rings.real_mpfr import RR from sage.symbolic.ring import SR from sage.calculus.var import var +from sage.misc.cachefunc import cached_function from sage.misc.latex import latex from sage.misc.misc import verbose from sage.functions.trig import cos, sin, atan2 From 91cafd64758db7c69153d41885757be8e4ff9da4 Mon Sep 17 00:00:00 2001 From: Emmanuel Charpentier Date: Mon, 7 Jul 2014 06:31:54 +0200 Subject: [PATCH 476/546] Re-(re-)fixed checksumss.ini and spkg-src --- build/pkgs/r/checksums.ini | 8 ++++---- build/pkgs/r/spkg-src | 8 ++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 build/pkgs/r/spkg-src diff --git a/build/pkgs/r/checksums.ini b/build/pkgs/r/checksums.ini index ab98ef2f667..765af8a7fcb 100644 --- a/build/pkgs/r/checksums.ini +++ b/build/pkgs/r/checksums.ini @@ -1,4 +1,4 @@ -tarball=r-VERSION.tar.bz2 -sha1=6b0f71b069b627e4f9947799fa1caa4ee4b18eb4 -md5=dd594ad26c3d40dc452071aac77a3759 -cksum=1505471622 +tarball=r-VERSION.tar.gz +sha1=a9d13932c739cc12667c6a17fabd9361624a1708 +md5=a1ee52446bee81820409661e6d114ab1 +cksum=3701745191 diff --git a/build/pkgs/r/spkg-src b/build/pkgs/r/spkg-src new file mode 100644 index 00000000000..aabe1608c94 --- /dev/null +++ b/build/pkgs/r/spkg-src @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +## What do we want ? +VERSION=$(cat package-version.txt | sed -re "s/^(.*)\\.p.+$/\\1/") +TARGET_TARBALL=r-$VERSION.tar.gz +SOURCE_TARBALL=R-$VERSION.tar.gz + +mv $SOURCE_VERSION $TARGET_TARBALL From 0f89d3e6c3583bd75df4ca70aa51ce9914af3850 Mon Sep 17 00:00:00 2001 From: Emmanuel Charpentier Date: Mon, 7 Jul 2014 07:52:14 +0200 Subject: [PATCH 477/546] Hand-edited checksum.ini *after* checksum generation (+ thinko in spkg-src). --- build/pkgs/r/checksums.ini | 2 +- build/pkgs/r/spkg-src | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/r/checksums.ini b/build/pkgs/r/checksums.ini index 765af8a7fcb..ef7ba2e249c 100644 --- a/build/pkgs/r/checksums.ini +++ b/build/pkgs/r/checksums.ini @@ -1,4 +1,4 @@ -tarball=r-VERSION.tar.gz +tarball=r-3.1.0.tar.gz sha1=a9d13932c739cc12667c6a17fabd9361624a1708 md5=a1ee52446bee81820409661e6d114ab1 cksum=3701745191 diff --git a/build/pkgs/r/spkg-src b/build/pkgs/r/spkg-src index aabe1608c94..89e6f1563c3 100644 --- a/build/pkgs/r/spkg-src +++ b/build/pkgs/r/spkg-src @@ -5,4 +5,4 @@ VERSION=$(cat package-version.txt | sed -re "s/^(.*)\\.p.+$/\\1/") TARGET_TARBALL=r-$VERSION.tar.gz SOURCE_TARBALL=R-$VERSION.tar.gz -mv $SOURCE_VERSION $TARGET_TARBALL +mv $SOURCE_TARBALL $TARGET_TARBALL From 99b382b785cbaa1cd5ebaf28da94b13f12388d17 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Mon, 7 Jul 2014 11:24:31 +0200 Subject: [PATCH 478/546] class CachedSpecialMethod: Fix formatting issue in docstring --- src/sage/misc/cachefunc.pyx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index 898ebe9b4e6..c05d30b7b81 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -2415,6 +2415,8 @@ cdef class CachedSpecialMethod(CachedMethod): The hash is computed only once, subsequent calls will use the value from the cache. This was implemented in :trac:`12601`. + :: + sage: hash(c) # indirect doctest compute hash 5 From f3a542bc7921a004ded5d4d798281357b7b78213 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Mon, 7 Jul 2014 11:26:52 +0200 Subject: [PATCH 479/546] cached_method: additional docstring Added a docstring illustrating the different instances have distinct caches. --- src/sage/misc/cachefunc.pyx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index c05d30b7b81..2c453b6058b 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -2550,6 +2550,19 @@ def cached_method(f, name=None, key=None): sage: c.f(4) is c.f(4) True + Different instances have distinct caches:: + + sage: b = Foo() + sage: b.f(3) is a.f(3) + computing + False + sage: b.f.clear_cache() + sage: a.f(3) + 9 + sage: b.f(3) + computing + 9 + Using cached methods for the hash and other special methods was implemented in :trac:`12601`, by means of :class:`CachedSpecialMethod`. We show that it is used behind the scenes:: From a4e6c88c8218545d7b1be8970d83e3c38687435d Mon Sep 17 00:00:00 2001 From: Punarbasu Purkayastha Date: Mon, 7 Jul 2014 20:20:15 +0800 Subject: [PATCH 480/546] fix or workaround for no axes in log scale when ticks are off The main culprit seems to be the command bbox_extra_artists=[] while *saving* the figure! So, the fix (or perhaps it is a workaround in some bug in matplotlib) is to set this option only if it is nonempty. --- src/sage/plot/graphics.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/plot/graphics.py b/src/sage/plot/graphics.py index 1e07333537e..d22f64b84e8 100644 --- a/src/sage/plot/graphics.py +++ b/src/sage/plot/graphics.py @@ -2907,13 +2907,13 @@ def save(self, filename=None, **kwds): # tight_layout adjusts the *subplot* parameters so ticks aren't cut off, etc. figure.tight_layout() + opts = dict(dpi=dpi, transparent=transparent) if fig_tight is True: - figure.savefig(filename, dpi=dpi, bbox_inches='tight', - bbox_extra_artists=self._bbox_extra_artists, - transparent=transparent) - else: - figure.savefig(filename, dpi=dpi, - transparent=transparent) + opts['bbox_inches'] = 'tight' + if self._bbox_extra_artists: + opts['bbox_extra_artists'] = self._bbox_extra_artists + + figure.savefig(filename, **opts) # Restore the rcParams to the original, possibly user-set values (rcParams['ps.useafm'], rcParams['pdf.use14corefonts'], From 97f9f7406cad6dde9d7ce9600e31225d0d4245c0 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Mon, 7 Jul 2014 18:54:55 +0100 Subject: [PATCH 481/546] fixes dfenition --- src/sage/game_theory/cooperative_game.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index a5e982f38d9..9eec3b64dfd 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -25,6 +25,7 @@ from sage.rings.integer import Integer from sage.structure.sage_object import SageObject + class CooperativeGame(SageObject): r""" An object representing a co-operative game. Primarily used to compute the @@ -94,8 +95,9 @@ class CooperativeGame(SageObject): A characteristic function game `G = (N, v)` is monotone if it satisfies `v(C_2) \geq v(C_1)` for all `C_1 \subseteq C_2`. A characteristic - function game `G = (N, v)` is superadditive if it satisfies `v(C_2) - \geq v(C_1)` for all `C_1 \subseteq C_2` such that `C_1 \cap C_2 = \emptyset`. + function game `G = (N, v)` is superadditive if it satisfies + `v(C_1 \cup C_2) \geq v(C_1) v(C_2)` for all `C_1, C_2 \subseteq N` such + that `C_1 \cap C_2 = \emptyset`. We can test if a game is monotonic or superadditive. :: From 5f84119d5175b06e44221e1ed7639237ab00853d Mon Sep 17 00:00:00 2001 From: Martin Albrecht Date: Mon, 7 Jul 2014 21:46:57 +0100 Subject: [PATCH 482/546] fixed some doctest failures --- src/sage/rings/polynomial/multi_polynomial_libsingular.pyx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index c9f673c8455..ca37d2e0f66 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -3282,6 +3282,7 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn ....: except OverflowError: ....: print "overflow" overflow # 32-bit + x^1000000 # 64-bit no overflow # 64-bit sage: n=100000; @@ -3293,9 +3294,6 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn ....: print "overflow" overflow """ - if not self._poly: - return self - cdef int mi, i, need_map, try_symbolic cdef unsigned long degree = 0 @@ -3304,6 +3302,9 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn if(_ring != currRing): rChangeCurrRing(_ring) + if self.is_zero(): + return self + cdef poly *_p = p_Copy(self._poly, _ring) cdef poly *_f From 137f49569c5ffa635e984ef1c96e1ba20d431611 Mon Sep 17 00:00:00 2001 From: Jayant Date: Mon, 7 Jul 2014 18:25:12 -0400 Subject: [PATCH 483/546] Fixed the IndexError bug for matroids of size >= 11 --- src/sage/matroids/matroids_plot_helpers.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/sage/matroids/matroids_plot_helpers.py b/src/sage/matroids/matroids_plot_helpers.py index 5fe19e98d68..012af2ab410 100644 --- a/src/sage/matroids/matroids_plot_helpers.py +++ b/src/sage/matroids/matroids_plot_helpers.py @@ -273,15 +273,17 @@ def addnontripts(tripts_labels, nontripts_labels, ptsdict): gridpts = [[float((tripts[0][0]+tripts[1][0]+tripts[2][0])/3), float(tripts[0][1]+tripts[1][1]+tripts[2][1])/3]] n = 0 - while n < num: + while n < num+1: g = trigrid(q[0]) q.extend([[g[0], q[0][pairs[0][0]], q[0][pairs[0][1]]], [g[0], q[0][pairs[1][0]], q[0][pairs[1][1]]], [g[0], q[0][pairs[2][0]], q[0][pairs[2][1]]]]) q.remove(q[0]) gridpts.extend(g[1:]) - n = n + 4 - gridpts = gridpts[0:num] + if n == 0: + n = n + 4 + else: + n = n + 3 j = 0 for p in nontripts_labels: ptsdict[p] = tuple(gridpts[j]) @@ -797,7 +799,7 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): fontsize=13, color='black') limits = tracklims(limits, [rectx, rectx+rectw], [recty, recty+recth]) G.axes(False) - G.axes_range(xmin=limits[0]-0.5, xmax=limits[1]+0.5, + G.axes_range(xmin=limits[0]-0.5, xmax=limits[1]+0.5, ymin=limits[2]-0.5, ymax=limits[3]+0.5) return G elif M.rank() == 1: @@ -886,12 +888,13 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): ptsx, ptsy, x_i, y_i = createline(pts2, ll, lineorders1) lims = tracklims(lims, x_i, y_i) G += line(zip(x_i, y_i), color='black', thickness=3, zorder=1) - pels = [p for p in pts2.keys() if any([M1.rank([p, q]) == 1 + pels = [p for p in pts2.keys() if any([M1.rank([p, q]) == 1 for q in P])] allpts = [list(pts2[i]) for i in M.groundset()] xpts = [float(k[0]) for k in allpts] ypts = [float(k[1]) for k in allpts] - G += points(zip(xpts, ypts), color=Color('#BDBDBD'), size=300, zorder=2) + G += points(zip(xpts, ypts), color=Color('#BDBDBD'), size=300, + zorder=2) for i in pts2: if i not in pels: pt = list(pts2[i]) From 44c7ca5272b552310a0c2ff3214fba9d0a450ab0 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Tue, 8 Jul 2014 09:34:12 +0200 Subject: [PATCH 484/546] trac #16621: Fix failing docstring in previous commit --- src/sage/misc/cachefunc.pyx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index 2c453b6058b..087f125a431 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -2552,16 +2552,16 @@ def cached_method(f, name=None, key=None): Different instances have distinct caches:: - sage: b = Foo() - sage: b.f(3) is a.f(3) - computing + sage: d = C() + sage: d.f(4) is c.f(4) + computing cached method False - sage: b.f.clear_cache() - sage: a.f(3) - 9 - sage: b.f(3) - computing - 9 + sage: d.f.clear_cache() + sage: c.f(4) + 8 + sage: d.f(4) + computing cached method + 8 Using cached methods for the hash and other special methods was implemented in :trac:`12601`, by means of :class:`CachedSpecialMethod`. We From bf946fe074bf01c1e9e377fad37471e6f101ed41 Mon Sep 17 00:00:00 2001 From: drvinceknight Date: Tue, 8 Jul 2014 08:38:08 +0100 Subject: [PATCH 485/546] Added reference for fairness --- src/sage/game_theory/cooperative_game.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 0bd111def48..09ab69e9842 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -126,10 +126,15 @@ class CooperativeGame(SageObject): \Delta_{\pi}^G(i) = v\bigl( S_{\pi}(i) \cup \{i\} \bigr) - v\bigl( S_{\pi}(i) \bigr) - where `S_{\pi}(i)` is the number of predecessors of `i` in `\pi`, i.e. + where `S_{\pi}(i)` is the set of predecessors of `i` in `\pi`, i.e. `S_{\pi}(i) = \{ j \mid \pi(i) > \pi(j) \}` (or the number of inversions of the form `(i, j)`). + This payoff vector is "fair" in that it has a collection of properties + referred to as: Efficiency, Symmetry, Linearity and Null player. + Some of these properties are considered in this documentation (and tests + are implemented in the class) but for a good overview see [CEW2011]_. + Note ([MSZ2013]_) that an equivalent formula for the Shapley value is given by: .. MATH:: From 73637f32f7622f2bd3f85d2e077a597f28dd4970 Mon Sep 17 00:00:00 2001 From: drvinceknight Date: Tue, 8 Jul 2014 08:54:24 +0100 Subject: [PATCH 486/546] Added discussion about complexity of calculating the Shapley value --- src/sage/game_theory/cooperative_game.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 09ab69e9842..890a1912d5e 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -164,10 +164,12 @@ class CooperativeGame(SageObject): sage: g.shapley_value() {0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 1, 9: 1} - For very large game it might be worth taking advantage of the particular + For very large games it might be worth taking advantage of the particular problem structure to calculate the Shapley value and there are also - various approximation approaches to obtaining the Shapley value of a game. - Implementing these would be a worthwhile development. + various approximation approaches to obtaining the Shapley value of a game + (see [SWJ2008]_ for one such example). Implementing these would be a + worthwhile development For more information about the computational + complexity of calculating the Shapley value see [XP1994]_. We can test 3 basic properties of any payoff vector `\lambda`. The Shapley value (described above) is none to be the unique @@ -254,6 +256,13 @@ class CooperativeGame(SageObject): Cambridge: Cambridge University Press, (2013). ISBN 9781107005488. + .. [XP1994] Deng Xiaotie, and Christos Papadimitriou. + *On the complexity of cooperative solution concepts.* + Mathematics of Operations Research 19.2 (1994): 257-266. + + .. [SWJ2008] Fatima Shaheen, Michael Wooldridge, and Nicholas Jennings. + *A linear approximation method for the Shapley value.* + Artificial Intelligence 172.14 (2008): 1673-1699. """ def __init__(self, characteristic_function): r""" From 18ddc6dec77d39d764dcaa9fbdbe91adf02bfbbc Mon Sep 17 00:00:00 2001 From: Martin Albrecht Date: Mon, 7 Jul 2014 22:35:14 +0100 Subject: [PATCH 487/546] we still need to run subst if self == 0 to allow ring changes --- src/sage/libs/singular/polynomial.pyx | 1 + .../polynomial/multi_polynomial_libsingular.pyx | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/sage/libs/singular/polynomial.pyx b/src/sage/libs/singular/polynomial.pyx index 7a33c3e0a87..5d4319639f7 100644 --- a/src/sage/libs/singular/polynomial.pyx +++ b/src/sage/libs/singular/polynomial.pyx @@ -539,6 +539,7 @@ cdef int singular_polynomial_subst(poly **p, int var_index, poly *value, ring *r - ``value`` - a polynomial - ``r`` - a ring """ + if p_IsConstant(value, r): p[0] = pSubst(p[0], var_index+1, value) return 0 diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index ca37d2e0f66..3b6cbdbe84c 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -3272,6 +3272,13 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn sage: f.subs({y:x}).subs({x:z}) z + We test that we change the ring even if there is nothing to do:: + + sage: P = QQ['x,y'] + sage: x = var('x') + sage: parent(P.zero_element() / x) + Symbolic Ring + We are catching overflows:: sage: R. = QQ[] @@ -3302,9 +3309,6 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn if(_ring != currRing): rChangeCurrRing(_ring) - if self.is_zero(): - return self - cdef poly *_p = p_Copy(self._poly, _ring) cdef poly *_f @@ -3314,7 +3318,11 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn need_map = 0 try_symbolic = 0 - if fixed is not None: + if self.is_zero(): + # there is nothing to do except to change the ring + try_symbolic = 1 + + if not try_symbolic and fixed is not None: for m,v in fixed.iteritems(): if isinstance(m, (int, Integer)): mi = m+1 @@ -3424,6 +3432,7 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn g[mi-1] = v + gd = parent.gens_dict() for m,v in kw.iteritems(): m = gd[m] for i from 0 < i <= _ring.N: From b206fb285fc69225eba501ff5162490f1d9506a0 Mon Sep 17 00:00:00 2001 From: Solomon Vishkautsan Date: Tue, 8 Jul 2014 15:45:57 +0300 Subject: [PATCH 488/546] moved macaulay_resultant to MPolynomialRing_generic class; deleted macaulay_resultant.py --- src/sage/rings/polynomial/all.py | 6 +- .../rings/polynomial/macaulay_resultant.py | 350 ------------------ .../multi_polynomial_ring_generic.pyx | 286 ++++++++++++++ 3 files changed, 287 insertions(+), 355 deletions(-) delete mode 100644 src/sage/rings/polynomial/macaulay_resultant.py diff --git a/src/sage/rings/polynomial/all.py b/src/sage/rings/polynomial/all.py index 6b3e38ce528..3c4f8aae025 100644 --- a/src/sage/rings/polynomial/all.py +++ b/src/sage/rings/polynomial/all.py @@ -45,8 +45,4 @@ from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing # Evaluation of cyclotomic polynomials -from sage.rings.polynomial.cyclotomic import cyclotomic_value - -# Macaulay resultant for multivariable polynomials -lazy_import('sage.rings.polynomial.macaulay_resultant', 'macaulay_resultant') -lazy_import('sage.rings.polynomial.macaulay_resultant', 'macaulay_general_resultant') +from sage.rings.polynomial.cyclotomic import cyclotomic_value \ No newline at end of file diff --git a/src/sage/rings/polynomial/macaulay_resultant.py b/src/sage/rings/polynomial/macaulay_resultant.py deleted file mode 100644 index 4cf7197642c..00000000000 --- a/src/sage/rings/polynomial/macaulay_resultant.py +++ /dev/null @@ -1,350 +0,0 @@ -""" -Macaulay Resultant of Multivariate Polynomials - -This is an implementation of the Macaulay Resultant. It computes -the resultant of universal polynomials as well as polynomials -with constant coefficients. This is a project done in -sage days 55. It's based on the implementation in Maple by -Manfred Minimair, which in turn is based on the following: - --Using Algebraic Geometry by Cox, Little, O'Shea --Canny, J., "Generalised characteristic polynomials", p.241--250, - J. Symbolic Comput. Vol. 9, No. 3, 1990 --The algebraic theory of modular systems by Macaulay - - -AUTHORS: - -- Hao Chen -- Solomon Vishkautsan - -""" - -from sage.combinat.integer_vector_weighted import WeightedIntegerVectors -from sage.misc.misc_c import prod -from sage.matrix.constructor import matrix -from sage.rings.integer_ring import ZZ -from sage.rings.arith import binomial -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - -def getS(mon_deg_tuple,dlist): - r""" - In the MR algorithm the list of all monomials of the total degree is partitioned into sets S_i. - This function returns the index i for the set S_i for the inputted given monomial. - - INPUT: - - - `mon_deg_tuple` -- a list representing a monomial of a degree d - - `dlist` -- a list of degrees d_i of the polynomials in question, where - d = sum(dlist) - len(dlist) + 1 - - OUTPUT: - - - the index i such that the input monomial lives in S_i - - EXAMPLES:: - - sage: from sage.rings.polynomial.macaulay_resultant import getS - sage: getS([1,1,0],[2,1,1]) # the monomial xy where the total degree = 2 - 1 - - sage: getS([29,21,8],[10,20,30]) - 0 - - sage: getS(range(0,9)+[10],range(1,11)) - 9 - """ - for i in xrange(len(dlist)): - if mon_deg_tuple[i] - dlist[i] >= 0: - return i - - -def monomials(d,R): - r""" - returns all the monomials of degree d with variables a list of - generators of the ring R - - INPUT: - - -`d` -- a positive integer - -`R` -- a polynoimal ring - - OUTPUT: - - - a list of all monomials of degree d. - - EXAMPLES:: - - sage: from sage.rings.polynomial.macaulay_resultant import monomials - sage: monomials(3, PolynomialRing(QQ,3,'x')) - [x0^3, x0^2*x1, x0^2*x2, x0*x1^2, x0*x1*x2, x0*x2^2, x1^3, x1^2*x2, x1*x2^2, x2^3] - - It's OK if the coefficients of R live in some polynomial ring:: - - sage: U = PolynomialRing(QQ,20,'u') - sage: monomials(2, PolynomialRing(U,5,'x')) - [x0^2, x0*x1, x0*x2, x0*x3, x0*x4, x1^2, x1*x2, x1*x3, x1*x4, x2^2, x2*x3, x2*x4, x3^2, x3*x4, x4^2] - - """ - xlist = R.gens() - n = len(xlist) -1 - one_list = [1 for i in xrange(0,n+1)] - degs = WeightedIntegerVectors(d, one_list) - return [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) - for deg in degs] - -def is_reduced(mon_degs,dlist): - r""" - A monomial in the variables x_0,...,x_n is called reduced with respect to the list of degrees d_0,...,d_n - if the degree of x_i in the monomial is >= d_i for exactly one i. This function checks this property for an inputted monomial. - - INPUT: - - - mon -- a monomial in the variables listed in xlist - - dlist -- a list of degrees with respect to which we check reducedness - - xlist -- a list of variables in some Polynomial ring. - - OUTPUT: - - - True/False - - EXAMPLES:: - - sage: from sage.rings.polynomial.macaulay_resultant import is_reduced - sage: R. = PolynomialRing(QQ,3) - sage: is_reduced([2,3,1],[2,3,3]) # the monomial x^2*y^3*z is not reduced w.r.t. degrees vector [2,3,3] - False - - sage: R. = PolynomialRing(QQ,3) - sage: is_reduced([1,3,2],[2,3,3]) # the monomial x*y^3*z^2 is not reduced w.r.t. degrees vector [2,3,3] - True - """ - #TODO fix comments to reflect change in input parameters - #RRR deg = [mon.degree(xi) for xi in xlist] - diff = [mon_degs[i] - dlist[i] for i in xrange(0,len(dlist))] - return len([1 for d in diff if d >= 0]) == 1 - -def construct_universal_polynomial(dlist): - r""" - Given a list of degrees, this function returns a list of len(dlist) polynomials with len(dlist) variables, - with generic coefficients. This is useful for generating polynomials for tests, - and for getting the general resultant for the given degrees. - - INPUT: - - - dlist -- a list of degrees. - - OUTPUT: - - - a list of polynomials of the given degrees with general coefficients. - - a polynomial ring over ZZ generated by the coefficients of the output polynomials. - - EXAMPLES:: - - sage: from sage.rings.polynomial.macaulay_resultant import construct_universal_polynomial - sage: construct_universal_polynomial([1,1,2]) - ([u0*x0 + u1*x1 + u2*x2, u3*x0 + u4*x1 + u5*x2, u6*x0^2 + u7*x0*x1 + u9*x1^2 + u8*x0*x2 + u10*x1*x2 + u11*x2^2], - Multivariate Polynomial Ring in u0, u1, u2, u3, u4, u5, u6, u7, u8, u9, u10, u11 over Integer Ring) - """ - - n = len(dlist) - 1 - number_of_coeffs = sum([binomial(n+di,di) for di in dlist]) - U = PolynomialRing(ZZ,'u',number_of_coeffs) - d = sum(dlist) - len(dlist) + 1 - flist = [] - R = PolynomialRing(U,'x',n+1) - #TODO remove ugly prints - #print R - ulist = U.gens() - for d in dlist: - # construct a universal polynomial of degree d - # suppose we already have mon_d - mon_d = monomials(d,R) - #print mon_d - f = sum([mon_d[i]*ulist[i] for i in xrange(0,len(mon_d))]) - flist.append (f) - #print f - ulist = ulist[len(mon_d):] - return flist, U - -def macaulay_resultant(flist): - r""" - This is an implementation of the Macaulay Resultant. It computes - the resultant of universal polynomials as well as polynomials - with constant coefficients. This is a project done in - sage days 55. It's based on the implementation in Maple by - Manfred Minimair, which in turn is based on the following: - - -Using Algebraic Geometry by Cox, Little, O'Shea - -Canny, J., "Generalised characteristic polynomials", p.241--250, - J. Symbolic Comput. Vol. 9, No. 3, 1990 - -The algebraic theory of modular systems by Macaulay - - It calculates the Macaulay resultant for a list of Polynomials, - up to sign! - - AUTHORS: - - - Hao Chen - - Solomon Vishkautsan - - - INPUT: - - - flist -- a list of n homogeneous polynomials in n variables - - OUTPUT: - - - the resultant - - EXAMPLES: - - - The number of polynomials has to match the number of variables:: - - sage: R. = PolynomialRing(QQ,3) - sage: macaulay_resultant([y,x+z]) - Traceback (most recent call last): - ... - AssertionError: number of polynomials(= 2) must equal number of variables (= 3) - - The polynomials need to be all homogeneous:: - - sage: from sage.rings.polynomial.macaulay_resultant import * - sage: R. = PolynomialRing(QQ,3) - sage: macaulay_resultant([y, x+z, z+x^3]) - Traceback (most recent call last): - ... - AssertionError: resultant for non-homogeneous polynomials is not supported - - The following example recreates Proposition 2.10 in Ch.3 of Using Algebraic Geometry:: - - sage: flist,_ = construct_universal_polynomial([1,1,2]) - sage: macaulay_resultant(flist) - u2^2*u4^2*u6 - 2*u1*u2*u4*u5*u6 + u1^2*u5^2*u6 - u2^2*u3*u4*u7 + u1*u2*u3*u5*u7 + u0*u2*u4*u5*u7 - u0*u1*u5^2*u7 + u1*u2*u3*u4*u8 - u0*u2*u4^2*u8 - u1^2*u3*u5*u8 + u0*u1*u4*u5*u8 + u2^2*u3^2*u9 - 2*u0*u2*u3*u5*u9 + u0^2*u5^2*u9 - u1*u2*u3^2*u10 + u0*u2*u3*u4*u10 + u0*u1*u3*u5*u10 - u0^2*u4*u5*u10 + u1^2*u3^2*u11 - 2*u0*u1*u3*u4*u11 + u0^2*u4^2*u11 - - The following example degenerates into the determinant of a 3*3 matrix:: - - sage: flist,_ = construct_universal_polynomial([1,1,1]) - sage: macaulay_resultant(flist) - -u2*u4*u6 + u1*u5*u6 + u2*u3*u7 - u0*u5*u7 - u1*u3*u8 + u0*u4*u8 - - The following example is by Patrick Ingram(arxiv:1310.4114):: - - sage: U = PolynomialRing(ZZ,'y',2); y0,y1 = U.gens() - sage: R = PolynomialRing(U,'x',3); x0,x1,x2 = R.gens() - sage: f0 = y0*x2^2 - x0^2 + 2*x1*x2 - sage: f1 = y1*x2^2 - x1^2 + 2*x0*x2 - sage: f2 = x0*x1 - x2^2 - sage: flist = [f0,f1,f2] - sage: macaulay_resultant([f0,f1,f2]) - y0^2*y1^2 - 4*y0^3 - 4*y1^3 + 18*y0*y1 - 27 - - a simple example with constant rational coefficients:: - - sage: R. = PolynomialRing(QQ,4) - sage: macaulay_resultant([w,z,y,x]) - 1 - - an example where the resultant vanishes:: - - sage: R. = PolynomialRing(QQ,3) - sage: macaulay_resultant([x+y,y^2,x]) - 0 - - an example of bad reduction at a prime p = 5:: - - sage: R. = PolynomialRing(QQ,3) - sage: macaulay_resultant([y,x^3+25*y^2*x,5*z]) - 125 - - an example when the coefficients live in a finite field:: - - sage: F = FiniteField(11) - sage: R. = PolynomialRing(F,4) - sage: macaulay_resultant([z,x^3,5*y,w]) - 4 - - - example when the denominator in the algorithm vanishes(in this case - the resultant is the constant term of the quotient of - char polynomials of numerator/denominator):: - - sage: R. = PolynomialRing(QQ,3) - sage: macaulay_resultant([y, x+z, z^2]) - -1 - - when there are only 2 functions, macaulay resultant degenerates to the traditional resultant:: - - sage: R. = PolynomialRing(QQ,1) - sage: f = x^2+1; g = x^5+1 - sage: f.resultant(g) == macaulay_resultant([f.homogenize(),g.homogenize()]) - True - """ - #TODO add test that checks that the output of the function is a polynomial - assert len(flist) > 0, 'you have to input least 1 polynomial in the list' - assert all([f.is_homogeneous() for f in flist]), 'resultant for non-homogeneous polynomials is not supported' - R = flist[0].parent() - dlist = [f.degree() for f in flist] - xlist = R.gens() - assert len(xlist) == len(dlist), 'number of polynomials(= %d) must equal number of variables (= %d)'%(len(dlist),len(xlist)) - n = len(dlist) - 1 - d = sum(dlist) - len(dlist) + 1 - one_list = [1 for i in xrange(0,len(dlist))] - mons = WeightedIntegerVectors(d, one_list) # list of exponent-vectors(/lists) of monomials of degree d - #mon_d = [prod([xlist[i]**(deg[i]) for i in xrange(0,len(deg))]) for deg in mons] - mons_num = len(mons) - mons_to_keep = [] - newflist = [] - result = [] - - for j in xrange(0,mons_num): - if not is_reduced(mons[j],dlist): - mons_to_keep.append(j) - si_mon = getS(mons[j], dlist) - # Monomial is in S_i under the partition, now we reduce the i'th degree of the monomial - new_mon = list(mons[j]) - new_mon[si_mon] -= dlist[si_mon] - quo = prod([xlist[k]**(new_mon[k]) for k in xrange(0,n+1)]) # this produces the actual reduced monomial - new_f = flist[si_mon]*quo - # we strip the coefficients of the new polynomial: - result.append([new_f[mon] for mon in mons]) - - numer_matrix = matrix(result) - denom_matrix = numer_matrix.matrix_from_rows_and_columns(mons_to_keep,mons_to_keep) - if denom_matrix.dimensions()[0] == 0: # here we choose the determinant of an empty matrix to be 1 - return numer_matrix.det() - denom_det = denom_matrix.det() - if denom_det != 0: - return numer_matrix.det()/denom_det - # if we get to this point, the determinant of the denominator was 0, and we get the resultant - # by taking the free coefficient of the quotient of two characteristic polynomials - poly_num = numer_matrix.characteristic_polynomial('T') - poly_denom = denom_matrix.characteristic_polynomial('T') - poly_quo = poly_num.quo_rem(poly_denom)[0] - return poly_quo(0) - - -def macaulay_general_resultant(dlist): - r""" - this is just a wrapper function of macaulay_resultant, where - it takes a list of degrees as input and returns the resultant of - a list of generic polynomials with coefficients in a polynomial ring. - - INPUT: - - - dlist -- a list of degrees - - OUTPUT: - - - the general resultant - - EXAMPLES:: - - sage: from sage.rings.polynomial.macaulay_resultant import macaulay_general_resultant - sage: macaulay_general_resultant([1,1,2]) - u2^2*u4^2*u6 - 2*u1*u2*u4*u5*u6 + u1^2*u5^2*u6 - u2^2*u3*u4*u7 + u1*u2*u3*u5*u7 + u0*u2*u4*u5*u7 - u0*u1*u5^2*u7 + u1*u2*u3*u4*u8 - u0*u2*u4^2*u8 - u1^2*u3*u5*u8 + u0*u1*u4*u5*u8 + u2^2*u3^2*u9 - 2*u0*u2*u3*u5*u9 + u0^2*u5^2*u9 - u1*u2*u3^2*u10 + u0*u2*u3*u4*u10 + u0*u1*u3*u5*u10 - u0^2*u4*u5*u10 + u1^2*u3^2*u11 - 2*u0*u1*u3*u4*u11 + u0^2*u4^2*u11 - """ - assert all([d >=0 for d in dlist]), 'degrees must be non-negative' - flist, U = construct_universal_polynomial(dlist) - return U(macaulay_resultant(flist)) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx index 2583e3a1c4f..e9185dd7133 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx @@ -880,6 +880,292 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): from polynomial_ring_constructor import PolynomialRing return PolynomialRing(base_ring, self.ngens(), names, order=order) + def _macaulay_resultant_getS(self,mon_deg_tuple,dlist): + r""" + In the Macaulay resultant algorithm the list of all monomials of the total degree is partitioned into sets S_i. + This function returns the index i for the set S_i for the inputted given monomial. + + INPUT: + + - `mon_deg_tuple` -- a list representing a monomial of a degree d + - `dlist` -- a list of degrees d_i of the polynomials in question, where + d = sum(dlist) - len(dlist) + 1 + + OUTPUT: + + - the index i such that the input monomial lives in S_i + + EXAMPLES:: + + sage: R. = PolynomialRing(ZZ, 2) + sage: R._macaulay_resultant_getS([1,1,0],[2,1,1]) # the monomial xy where the total degree = 2 + 1 + + sage: R._macaulay_resultant_getS([29,21,8],[10,20,30]) + 0 + + sage: R._macaulay_resultant_getS(range(0,9)+[10],range(1,11)) + 9 + """ + for i in xrange(len(dlist)): + if mon_deg_tuple[i] - dlist[i] >= 0: + return i + + def _macaulay_resultant_is_reduced(self,mon_degs,dlist): + r""" + Helper function for the Macaulay resultant algorithm. + A monomial in the variables x_0,...,x_n is called reduced with respect to the list of degrees d_0,...,d_n + if the degree of x_i in the monomial is >= d_i for exactly one i. This function checks this property for an inputted monomial. + + INPUT: + + - mon -- a monomial in the variables listed in xlist + - dlist -- a list of degrees with respect to which we check reducedness + - xlist -- a list of variables in some Polynomial ring. + + OUTPUT: + + - True/False + + EXAMPLES:: + + sage: from sage.rings.polynomial.macaulay_resultant import is_reduced + sage: R. = PolynomialRing(QQ,3) + sage: R._macaulay_resultant_is_reduced([2,3,1],[2,3,3]) # the monomial x^2*y^3*z is not reduced w.r.t. degrees vector [2,3,3] + False + + sage: R. = PolynomialRing(QQ,3) + sage: R._macaulay_resultant_is_reduced([1,3,2],[2,3,3]) # the monomial x*y^3*z^2 is not reduced w.r.t. degrees vector [2,3,3] + True + """ + #TODO fix comments to reflect change in input parameters + #RRR deg = [mon.degree(xi) for xi in xlist] + diff = [mon_degs[i] - dlist[i] for i in xrange(0,len(dlist))] + return len([1 for d in diff if d >= 0]) == 1 + + def _macaulay_resultant_universal_polynomials(self, dlist): + r""" + Given a list of degrees, this function returns a list of len(dlist) polynomials with len(dlist) variables, + with generic coefficients. This is useful for generating polynomials for tests, + and for getting a universal macaulay resultant for the given degrees. + + INPUT: + + - dlist -- a list of degrees. + + OUTPUT: + + - a list of polynomials of the given degrees with general coefficients. + - a polynomial ring over self generated by the coefficients of the output polynomials. + + EXAMPLES:: + + sage: R. = PolynomialRing(ZZ, 2) + sage: R._macaulay_resultant_universal_polynomials([1,1,2]) + ([u0*x0 + u1*x1 + u2*x2, u3*x0 + u4*x1 + u5*x2, u6*x0^2 + u7*x0*x1 + u9*x1^2 + u8*x0*x2 + u10*x1*x2 + u11*x2^2], Multivariate Polynomial Ring in x0, x1, x2 over Multivariate Polynomial Ring in u0, u1, u2, u3, u4, u5, u6, u7, u8, u9, u10, u11 over Integer Ring) + """ + from sage.misc.misc_c import prod + from sage.combinat.integer_vector import IntegerVectors + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + from sage.rings.arith import binomial + from sage.rings.integer_ring import ZZ + + n = len(dlist) - 1 + number_of_coeffs = sum([binomial(n+di,di) for di in dlist]) + U = PolynomialRing(ZZ,'u',number_of_coeffs) + d = sum(dlist) - len(dlist) + 1 + flist = [] + R = PolynomialRing(U,'x',n+1) + #TODO remove ugly prints + #print R + ulist = U.gens() + for d in dlist: + xlist = R.gens() + #n = len(xlist) - 1 + degs = IntegerVectors(d, n+1) + mon_d = [prod([xlist[i]**(deg[i]) for i in xrange(0,len(deg))]) + for deg in degs] + + f = sum([mon_d[i]*ulist[i] for i in xrange(0,len(mon_d))]) + flist.append (f) + ulist = ulist[len(mon_d):] + return flist, R + + def macaulay_resultant(self, flist): + r""" + This is an implementation of the Macaulay Resultant. It computes + the resultant of universal polynomials as well as polynomials + with constant coefficients. This is a project done in + sage days 55. It's based on the implementation in Maple by + Manfred Minimair, which in turn is based on the following: + + -Using Algebraic Geometry by Cox, Little, O'Shea + -Canny, J., "Generalised characteristic polynomials", p.241--250, + J. Symbolic Comput. Vol. 9, No. 3, 1990 + -The algebraic theory of modular systems by Macaulay + + It calculates the Macaulay resultant for a list of Polynomials, + up to sign! + + AUTHORS: + + - Hao Chen + - Solomon Vishkautsan + - Ben Hutz + + INPUT: + + - flist -- a list of n homogeneous polynomials in n variables + + OUTPUT: + + - the resultant + + EXAMPLES: + + + The number of polynomials has to match the number of variables:: + + sage: R. = PolynomialRing(QQ,3) + sage: R.macaulay_resultant([y,x+z]) + Traceback (most recent call last): + ... + AssertionError: number of polynomials(= 2) must equal number of variables (= 3) + + The polynomials need to be all homogeneous:: + + sage: R. = PolynomialRing(QQ,3) + sage: R.macaulay_resultant([y, x+z, z+x^3]) + Traceback (most recent call last): + ... + AssertionError: resultant for non-homogeneous polynomials is not supported + + The following example recreates Proposition 2.10 in Ch.3 of Using Algebraic Geometry:: + + sage: K. = PolynomialRing(ZZ, 2) + sage: flist,R = K._macaulay_resultant_universal_polynomials([1,1,2]) + sage: R.macaulay_resultant(flist) + u2^2*u4^2*u6 - 2*u1*u2*u4*u5*u6 + u1^2*u5^2*u6 - u2^2*u3*u4*u7 + u1*u2*u3*u5*u7 + u0*u2*u4*u5*u7 - u0*u1*u5^2*u7 + u1*u2*u3*u4*u8 - u0*u2*u4^2*u8 - u1^2*u3*u5*u8 + u0*u1*u4*u5*u8 + u2^2*u3^2*u9 - 2*u0*u2*u3*u5*u9 + u0^2*u5^2*u9 - u1*u2*u3^2*u10 + u0*u2*u3*u4*u10 + u0*u1*u3*u5*u10 - u0^2*u4*u5*u10 + u1^2*u3^2*u11 - 2*u0*u1*u3*u4*u11 + u0^2*u4^2*u11 + + The following example degenerates into the determinant of a 3*3 matrix:: + + sage: K. = PolynomialRing(ZZ, 2) + sage: flist,R = K._macaulay_resultant_universal_polynomials([1,1,1]) + sage: R.macaulay_resultant(flist) + -u2*u4*u6 + u1*u5*u6 + u2*u3*u7 - u0*u5*u7 - u1*u3*u8 + u0*u4*u8 + + The following example is by Patrick Ingram(arxiv:1310.4114):: + + sage: U = PolynomialRing(ZZ,'y',2); y0,y1 = U.gens() + sage: R = PolynomialRing(U,'x',3); x0,x1,x2 = R.gens() + sage: f0 = y0*x2^2 - x0^2 + 2*x1*x2 + sage: f1 = y1*x2^2 - x1^2 + 2*x0*x2 + sage: f2 = x0*x1 - x2^2 + sage: flist = [f0,f1,f2] + sage: R.macaulay_resultant([f0,f1,f2]) + y0^2*y1^2 - 4*y0^3 - 4*y1^3 + 18*y0*y1 - 27 + + a simple example with constant rational coefficients:: + + sage: R. = PolynomialRing(QQ,4) + sage: R.macaulay_resultant([w,z,y,x]) + 1 + + an example where the resultant vanishes:: + + sage: R. = PolynomialRing(QQ,3) + sage: R.macaulay_resultant([x+y,y^2,x]) + 0 + + an example of bad reduction at a prime p = 5:: + + sage: R. = PolynomialRing(QQ,3) + sage: R.macaulay_resultant([y,x^3+25*y^2*x,5*z]) + 125 + + an example when the coefficients live in a finite field:: + + sage: F = FiniteField(11) + sage: R. = PolynomialRing(F,4) + sage: R.macaulay_resultant([z,x^3,5*y,w]) + 4 + + + example when the denominator in the algorithm vanishes(in this case + the resultant is the constant term of the quotient of + char polynomials of numerator/denominator):: + + sage: R. = PolynomialRing(QQ,3) + sage: R.macaulay_resultant([y, x+z, z^2]) + -1 + + when there are only 2 polynomials, macaulay resultant degenerates to the traditional resultant:: + + sage: R. = PolynomialRing(QQ,1) + sage: f = x^2+1; g = x^5+1 + sage: f.resultant(g) == R.macaulay_resultant([f.homogenize(),g.homogenize()]) + True + """ + + from sage.matrix.constructor import matrix + from sage.matrix.constructor import zero_matrix + from sage.combinat.integer_vector import IntegerVectors + + #TODO add test that checks that the output of the function is a polynomial + assert len(flist) > 0, 'you have to input least 1 polynomial in the list' + assert all([f.is_homogeneous() for f in flist]), 'resultant for non-homogeneous polynomials is not supported' + R = flist[0].parent() + dlist = [f.degree() for f in flist] + xlist = R.gens() + assert len(xlist) == len(dlist), 'number of polynomials(= %d) must equal number of variables (= %d)'%(len(dlist),len(xlist)) + n = len(dlist) - 1 + d = sum(dlist) - len(dlist) + 1 + #one_list = [1 for i in xrange(0,len(dlist))] + one_list = [1] * len(dlist) + #mons = WeightedIntegerVectors(d, one_list) # list of exponent-vectors(/lists) of monomials of degree d + mons = IntegerVectors(d, n+1).list() # list of exponent-vectors(/lists) of monomials of degree d + #mon_d = [prod([xlist[i]**(deg[i]) for i in xrange(0,len(deg))]) for deg in mons] + mons_num = len(mons) + mons_to_keep = [] + newflist = [] + flist=[[f.exponents(),f.coefficients()] for f in flist] # strip coefficients of the input polynomials + #result = [] + numer_matrix = zero_matrix(R.base_ring(),mons_num) + + for j in xrange(0,mons_num): + if not self._macaulay_resultant_is_reduced(mons[j],dlist): + mons_to_keep.append(j) + si_mon = self._macaulay_resultant_getS(mons[j], dlist) + # Monomial is in S_i under the partition, now we reduce the i'th degree of the monomial + new_mon = list(mons[j]) + new_mon[si_mon] -= dlist[si_mon] + new_f = [[[g[k] + new_mon[k] for k in range(n+1)] for g in flist[si_mon][0]], flist[si_mon][1]] + #quo = prod([xlist[k]**(new_mon[k]) for k in xrange(0,n+1)]) # this produces the actual reduced monomial + + #new_f = flist[si_mon]*quo + # we strip the coefficients of the new polynomial: + #result.append([new_f[mon] for mon in mons]) + i=0 + for mon in new_f[0]: + k=mons.index(mon) + numer_matrix[j,k]=new_f[1][i] + i+=1 + + + #numer_matrix = matrix(result) + + denom_matrix = numer_matrix.matrix_from_rows_and_columns(mons_to_keep,mons_to_keep) + if denom_matrix.dimensions()[0] == 0: # here we choose the determinant of an empty matrix to be 1 + return numer_matrix.det() + denom_det = denom_matrix.det() + if denom_det != 0: + return numer_matrix.det()/denom_det + # if we get to this point, the determinant of the denominator was 0, and we get the resultant + # by taking the free coefficient of the quotient of two characteristic polynomials + poly_num = numer_matrix.characteristic_polynomial('T') + poly_denom = denom_matrix.characteristic_polynomial('T') + poly_quo = poly_num.quo_rem(poly_denom)[0] + return poly_quo(0) #################### # Leave *all* old versions! From fb636aa9c5019845211ddc90e1166f23a53c684f Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 8 Jul 2014 15:29:39 +0200 Subject: [PATCH 489/546] trac #16622: Hypergraph is now an incidence structure --- src/sage/combinat/designs/all.py | 2 + .../combinat/designs/incidence_structures.py | 179 +++++++++- src/sage/graphs/all.py | 1 - src/sage/graphs/hypergraph.py | 316 ------------------ 4 files changed, 175 insertions(+), 323 deletions(-) delete mode 100644 src/sage/graphs/hypergraph.py diff --git a/src/sage/combinat/designs/all.py b/src/sage/combinat/designs/all.py index 7de8a9f529a..fc3b63f2563 100644 --- a/src/sage/combinat/designs/all.py +++ b/src/sage/combinat/designs/all.py @@ -5,6 +5,8 @@ from incidence_structures import (IncidenceStructure, IncidenceStructureFromMatrix) +from incidence_structures import IncidenceStructure as Hypergraph + from covering_design import (CoveringDesign, schonheim, trivial_covering_design) diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 5df55af101f..07ac761850e 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -40,6 +40,8 @@ from sage.rings.all import ZZ from sage.rings.integer import Integer +from sage.misc.latex import latex +from sage.sets.set import Set def IncidenceStructureFromMatrix(M, name=None): """ @@ -99,6 +101,12 @@ class IncidenceStructure(object): sage: designs.IncidenceStructure(7, [[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) Incidence structure with 7 points and 7 blocks + Only providing the set of blocks is sufficient. In this case, the ground set + is defined as the union of the blocks:: + + sage: IncidenceStructure([[1,2,3],[2,3,4]]) + Incidence structure with 4 points and 2 blocks + Or by its adjacency matrix (a `\{0,1\}`-matrix in which rows are indexed by points and columns by blocks):: @@ -179,13 +187,20 @@ def __init__(self, points=None, blocks=None, incidence_matrix=None, from sage.matrix.constructor import matrix from sage.structure.element import Matrix + # Reformatting input if isinstance(points, Matrix): + assert incidence_matrix is None + assert blocks is None incidence_matrix = points - points = None - - if points is None: - assert incidence_matrix is not None + points = blocks = None + elif points and blocks is None: + blocks = points + points = set().union(*blocks) + assert incidence_matrix is None + if points: + assert incidence_matrix is None + if incidence_matrix: M = matrix(incidence_matrix) v = M.nrows() self._points = range(v) @@ -193,8 +208,6 @@ def __init__(self, points=None, blocks=None, incidence_matrix=None, self._blocks = sorted(M.nonzero_positions_in_column(i) for i in range(M.ncols())) else: - assert incidence_matrix is None - if isinstance(points, (int,Integer)): self._points = range(points) self._point_to_index = None @@ -953,3 +966,157 @@ def block_design_checker(self, t, v, k, lmbda, type=None): if type == "connected": deprecation(16553, "block_design_checker(type='connected') is deprecated, please use .is_connected() instead") return self.incidence_graph().is_connected() + + def edge_coloring(self): + r""" + Compute a proper edge-coloring. + + A proper edge-coloring is an assignment of colors to the block of the + incidence structure such that two sets with non-empty intersection + receive different colors. The coloring returned minimizes the number of + colors. + + OUTPUT: + + A partition of the sets into color classes. + + EXAMPLES:: + + sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H + Incidence structure with 6 points and 4 blocks + sage: C = H.edge_coloring() + sage: C # random + [[[3, 4, 5]], [[2, 3, 4]], [[4, 5, 6], [1, 2, 3]]] + sage: Set(map(Set,sum(C,[]))) == Set(map(Set,H.blocks())) + True + """ + from sage.graphs.graph import Graph + blocks = self.blocks() + blocks_sets = map(frozenset,blocks) + g = Graph([range(self.num_blocks()),lambda x,y : len(blocks_sets[x]&blocks_sets[y])],loops = False) + return [[blocks[i] for i in C] for C in g.coloring(algorithm="MILP")] + + def _spring_layout(self): + r""" + Return a spring layout for the vertices. + + The layout is computed by creating a graph `G` on the vertices *and* + sets of the incidence structure. Each set is then made adjacent in `G` + with all vertices it contains before a spring layout is computed for + this graph. The position of the vertices in the hypergraph is the + position of the same vertices in the graph's layout. + + .. NOTE:: + + This method also returns the position of the "fake" vertices, + i.e. those representing the sets. + + EXAMPLES:: + + sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H + Incidence structure with 6 points and 4 blocks + sage: L = H._spring_layout() + sage: L # random + {1: (0.238, -0.926), + 2: (0.672, -0.518), + 3: (0.449, -0.225), + 4: (0.782, 0.225), + 5: (0.558, 0.518), + 6: (0.992, 0.926), + {3, 4, 5}: (0.504, 0.173), + {2, 3, 4}: (0.727, -0.173), + {4, 5, 6}: (0.838, 0.617), + {1, 2, 3}: (0.393, -0.617)} + sage: all(v in L for v in H.ground_set()) + True + sage: all(v in L for v in map(Set,H.blocks())) + True + """ + from sage.graphs.graph import Graph + + g = Graph() + for s in map(Set,self.blocks()): + for x in s: + g.add_edge(s,x) + + _ = g.plot(iterations = 50000,save_pos=True) + + # The values are rounded as TikZ does not like accuracy. + return {k:(round(x,3),round(y,3)) for k,(x,y) in g.get_pos().items()} + + def _latex_(self): + r""" + Return a TikZ representation of the incidence structure + + EXAMPLES:: + + sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H + Incidence structure with 6 points and 4 blocks + sage: view(H) # not tested + + With sets of size 4:: + + sage: g = graphs.Grid2dGraph(5,5) + sage: C4 = graphs.CycleGraph(4) + sage: sets = Set(map(Set,list(g.subgraph_search_iterator(C4)))) + sage: H = Hypergraph(sets) + sage: view(H) # not tested + """ + from sage.rings.integer import Integer + from sage.functions.trig import arctan2 + + from sage.misc.misc import warn + warn("\nThe hypergraph is drawn as a set of closed curves. The curve " + "representing a set S go **THROUGH** the vertices contained " + "in S.\n A vertex which is encircled by a curve but is not located " + "on its boundary is **NOT** included in the corresponding set.\n" + "\n" + "The colors are picked for readability and have no other meaning.") + + latex.add_package_to_preamble_if_available("tikz") + latex.add_to_mathjax_avoid_list("tikz") + + if not latex.has_file("tikz.sty"): + raise RuntimeError("You must have TikZ installed in order " + "to draw a hypergraph.") + + domain = self.ground_set() + pos = self._spring_layout() + tex = "\\begin{tikzpicture}[scale=3]\n" + + colors = ["black", "red", "green", "blue", "cyan", "magenta", "yellow","pink","brown"] + colored_sets = [(s,i) for i,S in enumerate(self.edge_coloring()) for s in S] + + # Prints each set with its color + for s,i in colored_sets: + current_color = colors[i%len(colors)] + + if len(s) == 2: + s = list(s) + tex += ("\\draw[color="+str(current_color)+","+ + "line width=.1cm,opacity = .6] "+ + str(pos[s[0]])+" -- "+str(pos[s[1]])+";\n") + continue + + tex += ("\\draw[color="+str(current_color)+"," + "line width=.1cm,opacity = .6," + "line cap=round," + "line join=round]" + "plot [smooth cycle,tension=1] coordinates {") + + # Reorders the vertices of s according to their angle with the + # "center", i.e. the vertex representing the set s + cx, cy = pos[Set(s)] + s = map(lambda x: pos[x], s) + s = sorted(s, key = lambda x_y: arctan2(x_y[0] - cx, x_y[1] - cy)) + + for x in s: + tex += str(x)+" " + tex += "};\n" + + # Prints each vertex + for v in domain: + tex += "\\draw node[fill,circle,scale=.5,label={90:$"+latex(v)+"$}] at "+str(pos[v])+" {};\n" + + tex += "\\end{tikzpicture}" + return tex diff --git a/src/sage/graphs/all.py b/src/sage/graphs/all.py index 40cc181f641..57fd01e8175 100644 --- a/src/sage/graphs/all.py +++ b/src/sage/graphs/all.py @@ -6,7 +6,6 @@ from graph_database import GraphDatabase, GenericGraphQuery, GraphQuery from graph import Graph from digraph import DiGraph -from hypergraph import Hypergraph from bipartite_graph import BipartiteGraph from graph_bundle import GraphBundle import weakly_chordal diff --git a/src/sage/graphs/hypergraph.py b/src/sage/graphs/hypergraph.py deleted file mode 100644 index df7fb87ff02..00000000000 --- a/src/sage/graphs/hypergraph.py +++ /dev/null @@ -1,316 +0,0 @@ -r""" -Hypergraphs - -This module consists in a very basic implementation of :class:`Hypergraph`, -whose only current purpose is to observe them: it can be used to compute -automorphism groups and to draw them. The latter is done at the moment through -`\LaTeX` and TikZ, and can be obtained from Sage through the ``view`` command:: - - sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H - Hypergraph on 6 vertices containing 4 sets - sage: view(H) # not tested - -Note that hypergraphs are very hard to visualize, and unless it is very small -(`\leq 10` sets) or has a very specific structure (like the following one), -Sage's drawing will only bring more confusion:: - - sage: g = graphs.Grid2dGraph(5,5) - sage: sets = Set(map(Set,list(g.subgraph_search_iterator(graphs.CycleGraph(4))))) - sage: H = Hypergraph(sets) - sage: view(H) # not tested - -.. SEEALSO:: - - :class:`Hypergraph` for information on the `\LaTeX` output - -Classes and methods -------------------- -""" -from sage.misc.latex import latex -from sage.sets.set import Set - -class Hypergraph: - r""" - A hypergraph. - - A *hypergraph* `H = (V, E)` is a set of vertices `V` and a collection `E` of - sets of vertices called *hyperedges* or edges. In particular `E \subseteq - \mathcal{P}(V)`. If all (hyper)edges contain exactly 2 vertices, then `H` is - a graph in the usual sense. - - .. rubric:: Latex output - - The `\LaTeX` for a hypergraph `H` is consists of the vertices set and a - set of closed curves. The set of vertices in each closed curve represents a - hyperedge of `H`. A vertex which is encircled by a curve but is not - located on its boundary is **NOT** included in the corresponding set. - - The colors are picked for readability and have no other meaning. - - INPUT: - - - ``sets`` -- A list of hyperedges - - EXAMPLES:: - - sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{6}]); H - Hypergraph on 6 vertices containing 4 sets - - REFERENCES: - - - :wikipedia:`Hypergraph` - """ - def __init__(self, sets): - r""" - Constructor - - EXAMPLES:: - - sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H - Hypergraph on 6 vertices containing 4 sets - """ - from sage.sets.set import Set - self._sets = map(Set, sets) - self._domain = set([]) - for s in sets: - for i in s: - self._domain.add(i) - - def __repr__(self): - r""" - Short description of ``self``. - - EXAMPLES:: - - sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H - Hypergraph on 6 vertices containing 4 sets - """ - return ("Hypergraph on "+str(len(self.domain()))+" " - "vertices containing "+str(len(self._sets))+" sets") - - def edge_coloring(self): - r""" - Compute a proper edge-coloring. - - A proper edge-coloring is an assignment of colors to the sets of the - hypergraph such that two sets with non-empty intersection receive - different colors. The coloring returned minimizes the number of colors. - - OUTPUT: - - A partition of the sets into color classes. - - EXAMPLES:: - - sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H - Hypergraph on 6 vertices containing 4 sets - sage: C = H.edge_coloring() - sage: C # random - [[{3, 4, 5}], [{4, 5, 6}, {1, 2, 3}], [{2, 3, 4}]] - sage: Set(sum(C,[])) == Set(H._sets) - True - """ - from sage.graphs.graph import Graph - g = Graph([self._sets,lambda x,y : len(x&y)],loops = False) - return g.coloring(algorithm="MILP") - - def _spring_layout(self): - r""" - Return a spring layout for the vertices. - - The layout is computed by creating a graph `G` on the vertices *and* - sets of the hypergraph. Each set is then made adjacent in `G` with all - vertices it contains before a spring layout is computed for this - graph. The position of the vertices in the hypergraph is the position of - the same vertices in the graph's layout. - - .. NOTE:: - - This method also returns the position of the "fake" vertices, - i.e. those representing the sets. - - EXAMPLES:: - - sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H - Hypergraph on 6 vertices containing 4 sets - sage: L = H._spring_layout() - sage: L # random - {1: (0.238, -0.926), - 2: (0.672, -0.518), - 3: (0.449, -0.225), - 4: (0.782, 0.225), - 5: (0.558, 0.518), - 6: (0.992, 0.926), - {3, 4, 5}: (0.504, 0.173), - {2, 3, 4}: (0.727, -0.173), - {4, 5, 6}: (0.838, 0.617), - {1, 2, 3}: (0.393, -0.617)} - sage: all(v in L for v in H.domain()) - True - sage: all(v in L for v in H._sets) - True - """ - from sage.graphs.graph import Graph - - g = Graph() - for s in self._sets: - for x in s: - g.add_edge(s,x) - - _ = g.plot(iterations = 50000,save_pos=True) - - # The values are rounded as TikZ does not like accuracy. - return {k:(round(x,3),round(y,3)) for k,(x,y) in g.get_pos().items()} - - def domain(self): - r""" - Return the set of vertices. - - EXAMPLES:: - - sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H - Hypergraph on 6 vertices containing 4 sets - sage: H.domain() - set([1, 2, 3, 4, 5, 6]) - """ - return self._domain.copy() - - def _latex_(self): - r""" - Return a TikZ representation of the hypergraph. - - EXAMPLES:: - - sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H - Hypergraph on 6 vertices containing 4 sets - sage: view(H) # not tested - - With sets of size 4:: - - sage: g = graphs.Grid2dGraph(5,5) - sage: C4 = graphs.CycleGraph(4) - sage: sets = Set(map(Set,list(g.subgraph_search_iterator(C4)))) - sage: H = Hypergraph(sets) - sage: view(H) # not tested - """ - from sage.rings.integer import Integer - from sage.functions.trig import arctan2 - - from sage.misc.misc import warn - warn("\nThe hypergraph is drawn as a set of closed curves. The curve " - "representing a set S go **THROUGH** the vertices contained " - "in S.\n A vertex which is encircled by a curve but is not located " - "on its boundary is **NOT** included in the corresponding set.\n" - "\n" - "The colors are picked for readability and have no other meaning.") - - latex.add_package_to_preamble_if_available("tikz") - latex.add_to_mathjax_avoid_list("tikz") - - if not latex.has_file("tikz.sty"): - raise RuntimeError("You must have TikZ installed in order " - "to draw a hypergraph.") - - domain = self.domain() - pos = self._spring_layout() - tex = "\\begin{tikzpicture}[scale=3]\n" - - colors = ["black", "red", "green", "blue", "cyan", "magenta", "yellow","pink","brown"] - colored_sets = [(s,i) for i,S in enumerate(self.edge_coloring()) for s in S] - - # Prints each set with its color - for s,i in colored_sets: - current_color = colors[i%len(colors)] - - if len(s) == 2: - s = list(s) - tex += ("\\draw[color="+str(current_color)+","+ - "line width=.1cm,opacity = .6] "+ - str(pos[s[0]])+" -- "+str(pos[s[1]])+";\n") - continue - - tex += ("\\draw[color="+str(current_color)+"," - "line width=.1cm,opacity = .6," - "line cap=round," - "line join=round]" - "plot [smooth cycle,tension=1] coordinates {") - - # Reorders the vertices of s according to their angle with the - # "center", i.e. the vertex representing the set s - cx, cy = pos[s] - s = map(lambda x: pos[x], s) - s = sorted(s, key = lambda x_y: arctan2(x_y[0] - cx, x_y[1] - cy)) - - for x in s: - tex += str(x)+" " - tex += "};\n" - - # Prints each vertex - for v in domain: - tex += "\\draw node[fill,circle,scale=.5,label={90:$"+latex(v)+"$}] at "+str(pos[v])+" {};\n" - - tex += "\\end{tikzpicture}" - return tex - - def to_bipartite_graph(self, with_partition=False): - r""" - Returns the associated bipartite graph - - INPUT: - - - with_partition -- boolean (default: False) - - OUTPUT: - - - a graph or a pair (graph, partition) - - EXAMPLES:: - - sage: H = designs.steiner_triple_system(7).blocks() - sage: H = Hypergraph(H) - sage: g = H.to_bipartite_graph(); g - Graph on 14 vertices - sage: g.is_regular() - True - """ - from sage.graphs.graph import Graph - - G = Graph() - domain = list(self.domain()) - G.add_vertices(domain) - for s in self._sets: - for i in s: - G.add_edge(s, i) - if with_partition: - return (G, [domain, list(self._sets)]) - else: - return G - - def automorphism_group(self): - r""" - Returns the automorphism group. - - For more information on the automorphism group of a hypergraph, see the - :wikipedia:`Hypergraph`. - - EXAMPLE:: - - sage: H = designs.steiner_triple_system(7).blocks() - sage: H = Hypergraph(H) - sage: g = H.automorphism_group(); g - Permutation Group with generators [(2,4)(5,6), (2,5)(4,6), (1,2)(3,4), (1,3)(5,6), (0,1)(2,5)] - sage: g.is_isomorphic(groups.permutation.PGL(3,2)) - True - """ - from sage.groups.perm_gps.permgroup import PermutationGroup - - G, part = self.to_bipartite_graph(with_partition=True) - - domain = part[0] - - ag = G.automorphism_group(partition=part) - - gens = [[tuple(c) for c in g.cycle_tuples() if c[0] in domain] - for g in ag.gens()] - - return PermutationGroup(gens = gens, domain = domain) From a17192cd308ab607b8a47cb2bc68d3d9ec4d1ba9 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 8 Jul 2014 14:57:28 +0100 Subject: [PATCH 490/546] adds tuple docs --- src/sage/game_theory/cooperative_game.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 3c69b63a685..5104841ff22 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -36,7 +36,7 @@ class CooperativeGame(SageObject): - ``characteristic_function`` -- a dictionary containing all possible sets of players: - * key - each set must be entered as a tuple, not a string + * key - each set must be entered as a tuple. * value - a real number representing each set of players contribution EXAMPLES: @@ -92,6 +92,12 @@ class CooperativeGame(SageObject): ....: ('A', 'B', 'C',): 42} sage: letter_game = CooperativeGame(letter_function) + Please note that keys should be tuples. ``'1, 2, 3'`` is not a valid key, + neither is ``123``. The correct input would be ``(1, 2, 3)``, be + particulary careful with single element tuples as these should also + contain a comma ie; ``(1,)``, although a check has been implemented to + convert ``(1)`` to ``(1,)``. + Characteristic function games can be of various types. A characteristic function game `G = (N, v)` is monotone if it satisfies From 8562845f21656e543c8bd4f54f15f4ae98afb915 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 8 Jul 2014 15:01:25 +0100 Subject: [PATCH 491/546] fixes other typos --- src/sage/game_theory/cooperative_game.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 5104841ff22..db879d63fdf 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -180,7 +180,7 @@ class CooperativeGame(SageObject): complexity of calculating the Shapley value see [XP1994]_. We can test 3 basic properties of any payoff vector `\lambda`. - The Shapley value (described above) is none to be the unique + The Shapley value (described above) is known to be the unique payoff vector that satisfies these and 1 other property not implemented here (additivity). They are: @@ -783,7 +783,7 @@ def nullplayer(self, payoff_vector): return False return True - def symmetry(self, payoff_vector): + def is_symmetric(self, payoff_vector): r""" Return ``True`` if ``payoff_vector`` possesses the symmetry property. @@ -809,7 +809,7 @@ def symmetry(self, payoff_vector): ....: ('B', 'C',): 42, ....: ('A', 'B', 'C',): 42} sage: letter_game = CooperativeGame(letter_function) - sage: letter_game.symmetry({'A': 5, 'B': 14, 'C': 20}) + sage: letter_game.is_symmetric({'A': 5, 'B': 14, 'C': 20}) True A payoff vector that returns ``False``:: From 9ca69fc807c723c1fb52a928e8a6e947baba562f Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 8 Jul 2014 15:04:59 +0100 Subject: [PATCH 492/546] changes is_symmetric --- src/sage/game_theory/cooperative_game.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index db879d63fdf..4073edcca18 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -202,7 +202,7 @@ class CooperativeGame(SageObject): True sage: letter_game.nullplayer(payoff_vector) True - sage: letter_game.symmetry(payoff_vector) + sage: letter_game.is_symmetric(payoff_vector) True Any payoff vector can be passed to the game and these properties @@ -213,7 +213,7 @@ class CooperativeGame(SageObject): False sage: letter_game.nullplayer(payoff_vector) True - sage: letter_game.symmetry(payoff_vector) + sage: letter_game.is_symmetric(payoff_vector) True TESTS: @@ -239,7 +239,7 @@ class CooperativeGame(SageObject): True sage: letter_game.nullplayer({'A': 2, 'C': 35, 'B': 5}) True - sage: letter_game.symmetry({'A': 2, 'C': 35, 'B': 5}) + sage: letter_game.is_symmetric({'A': 2, 'C': 35, 'B': 5}) True Any payoff vector can be passed to the game and these properties can once @@ -249,7 +249,7 @@ class CooperativeGame(SageObject): False sage: letter_game.nullplayer({'A': 0, 'C': 35, 'B': 3}) True - sage: letter_game.symmetry({'A': 0, 'C': 35, 'B': 3}) + sage: letter_game.is_symmetric({'A': 0, 'C': 35, 'B': 3}) True REFERENCES: @@ -823,7 +823,7 @@ def is_symmetric(self, payoff_vector): ....: (2, 3,): 42, ....: (1, 2, 3,): 42} sage: integer_game = CooperativeGame(integer_function) - sage: integer_game.symmetry({1: 2, 2: 5, 3: 35}) + sage: integer_game.is_symmetric({1: 2, 2: 5, 3: 35}) False A longer example for symmetry:: @@ -845,7 +845,7 @@ def is_symmetric(self, payoff_vector): ....: (2, 3, 4): 0, ....: (1, 2, 3, 4): 65} sage: long_game = CooperativeGame(long_function) - sage: long_game.symmetry({1: 20, 2: 20, 3: 5, 4: 20}) + sage: long_game.is_symmetric({1: 20, 2: 20, 3: 5, 4: 20}) True """ sets = self.ch_f.keys() From 9f7045952f7dd14cbcb4dfa25e1d6549353f15d1 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 8 Jul 2014 16:06:19 +0200 Subject: [PATCH 493/546] trac #16622: Changing the terminology --- .../combinat/designs/incidence_structures.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 07ac761850e..bd2889ad525 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -971,7 +971,7 @@ def edge_coloring(self): r""" Compute a proper edge-coloring. - A proper edge-coloring is an assignment of colors to the block of the + A proper edge-coloring is an assignment of colors to the sets of the incidence structure such that two sets with non-empty intersection receive different colors. The coloring returned minimizes the number of colors. @@ -998,17 +998,17 @@ def edge_coloring(self): def _spring_layout(self): r""" - Return a spring layout for the vertices. + Return a spring layout for the points. - The layout is computed by creating a graph `G` on the vertices *and* - sets of the incidence structure. Each set is then made adjacent in `G` - with all vertices it contains before a spring layout is computed for - this graph. The position of the vertices in the hypergraph is the - position of the same vertices in the graph's layout. + The layout is computed by creating a graph `G` on the points *and* sets + of the incidence structure. Each set is then made adjacent in `G` with + all points it contains before a spring layout is computed for this + graph. The position of the points in the graph gives the position of the + points in the final drawing. .. NOTE:: - This method also returns the position of the "fake" vertices, + This method also returns the position of the "fake" points, i.e. those representing the sets. EXAMPLES:: @@ -1067,8 +1067,8 @@ def _latex_(self): from sage.misc.misc import warn warn("\nThe hypergraph is drawn as a set of closed curves. The curve " - "representing a set S go **THROUGH** the vertices contained " - "in S.\n A vertex which is encircled by a curve but is not located " + "representing a set S go **THROUGH** the points contained " + "in S.\n A point which is encircled by a curve but is not located " "on its boundary is **NOT** included in the corresponding set.\n" "\n" "The colors are picked for readability and have no other meaning.") From 5ac822411ed8e045bda92c9c3de694a284fce28e Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Tue, 8 Jul 2014 16:18:44 +0200 Subject: [PATCH 494/546] 2516: fix doctests affected by 16007 merge --- src/sage/functions/hypergeometric.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/functions/hypergeometric.py b/src/sage/functions/hypergeometric.py index b45b397250b..0fed0f157af 100644 --- a/src/sage/functions/hypergeometric.py +++ b/src/sage/functions/hypergeometric.py @@ -108,7 +108,7 @@ Conversions:: sage: maxima(hypergeometric([1, 1, 1], [3, 3, 3], x)) - hypergeometric([1,1,1],[3,3,3],x) + hypergeometric([1,1,1],[3,3,3],_SAGE_VAR_x) sage: hypergeometric((5, 4), (4, 4), 3)._sympy_() hyper((5, 4), (4, 4), 3) sage: hypergeometric((5, 4), (4, 4), 3)._mathematica_init_() @@ -117,7 +117,7 @@ Arbitrary level of nesting for conversions:: sage: maxima(nest(lambda y: hypergeometric([y], [], x), 3, 1)) - 1/(1-x)^(1/(1-x)^(1/(1-x))) + 1/(1-_SAGE_VAR_x)^(1/(1-_SAGE_VAR_x)^(1/(1-_SAGE_VAR_x))) sage: maxima(nest(lambda y: hypergeometric([y], [3], x), 3, 1))._sage_() hypergeometric((hypergeometric((hypergeometric((1,), (3,), x),), (3,),... x),), (3,), x) From b2f8dc5a1c82312271f2e2361b142358a66d0965 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Tue, 8 Jul 2014 16:23:41 +0200 Subject: [PATCH 495/546] 2516: this 16007-related doctest fail seems to be uncovered only by 2516, so fix it here --- src/sage/symbolic/expression_conversions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index f7864596980..5e04ab1c7a1 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -476,7 +476,7 @@ def tuple(self, ex): sage: m = InterfaceInit(maxima) sage: t = SR._force_pyobject((3, 4, e^x)) sage: m.tuple(t) - '[3,4,exp(x)]' + '[3,4,exp(_SAGE_VAR_x)]' """ x = map(self, ex.operands()) X = ','.join(x) From 987faab4e638a39feb9ed8235194df2f3d66ab10 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Tue, 8 Jul 2014 11:39:02 -0400 Subject: [PATCH 496/546] Ellipsize the source line number in warnings --- src/sage/algebras/group_algebra.py | 2 +- src/sage/coding/code_constructions.py | 4 ++-- src/sage/combinat/composition.py | 8 +++---- .../combinat/designs/incidence_structures.py | 2 +- src/sage/combinat/finite_state_machine.py | 10 ++++---- src/sage/combinat/partition.py | 24 +++++++++---------- .../combinat/root_system/cartan_matrix.py | 2 +- src/sage/combinat/set_partition.py | 8 +++---- src/sage/combinat/skew_partition.py | 2 +- src/sage/combinat/words/alphabet.py | 6 ++--- src/sage/databases/sloane.py | 6 ++--- src/sage/doctest/forker.py | 2 +- src/sage/functions/bessel.py | 8 +++---- src/sage/functions/hyperbolic.py | 4 ++-- src/sage/geometry/cone.py | 2 +- src/sage/geometry/lattice_polytope.py | 12 +++++----- src/sage/misc/cachefunc.pyx | 2 +- src/sage/misc/functional.py | 2 +- src/sage/misc/misc.py | 2 +- src/sage/misc/superseded.py | 4 ++-- src/sage/modules/vector_double_dense.pyx | 2 +- src/sage/numerical/mip.pyx | 2 +- src/sage/plot/graphics.py | 2 +- .../elliptic_curves/ell_rational_field.py | 12 +++++----- 24 files changed, 65 insertions(+), 65 deletions(-) diff --git a/src/sage/algebras/group_algebra.py b/src/sage/algebras/group_algebra.py index 5195f6b71a9..4df7e70e36b 100644 --- a/src/sage/algebras/group_algebra.py +++ b/src/sage/algebras/group_algebra.py @@ -51,7 +51,7 @@ def __init__(self, group, base_ring = IntegerRing()): EXAMPLES:: sage: from sage.algebras.group_algebra import GroupAlgebra - doctest:1: DeprecationWarning:... + doctest:...: DeprecationWarning:... sage: GroupAlgebra(GL(3, GF(7))) Group algebra of group "General Linear Group of degree 3 over Finite Field of size 7" over base ring Integer Ring diff --git a/src/sage/coding/code_constructions.py b/src/sage/coding/code_constructions.py index 48d9b629cd0..4992099da19 100644 --- a/src/sage/coding/code_constructions.py +++ b/src/sage/coding/code_constructions.py @@ -980,7 +980,7 @@ def HammingCode(r,F): ``HammingCode`` is still available in the global namespace:: sage: HammingCode(3,GF(2)) - doctest:1: DeprecationWarning: This method soon will not be available in that way anymore. To use it, you can now call it by typing codes.HammingCode + doctest:...: DeprecationWarning: This method soon will not be available in that way anymore. To use it, you can now call it by typing codes.HammingCode See http://trac.sagemath.org/15445 for details. Linear code of length 7, dimension 4 over Finite Field of size 2 @@ -1296,7 +1296,7 @@ def ReedSolomonCode(n,k,F,pts = None): ``ReedSolomonCode`` is still available in the global namespace:: sage: ReedSolomonCode(6,4,GF(7)) - doctest:1: DeprecationWarning: This method soon will not be available in that way anymore. To use it, you can now call it by typing codes.ReedSolomonCode + doctest:...: DeprecationWarning: This method soon will not be available in that way anymore. To use it, you can now call it by typing codes.ReedSolomonCode See http://trac.sagemath.org/15445 for details. Linear code of length 6, dimension 4 over Finite Field of size 7 diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index 96f1bb36918..70b4d09a02a 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -1484,7 +1484,7 @@ class Compositions(Parent, UniqueRepresentation): preferably consider using ``IntegerVectors`` instead:: sage: Compositions(2, length=3, min_part=0).list() - doctest:... RuntimeWarning: Currently, setting min_part=0 produces Composition objects which violate internal assumptions. Calling methods on these objects may produce errors or WRONG results! + doctest:...: RuntimeWarning: Currently, setting min_part=0 produces Composition objects which violate internal assumptions. Calling methods on these objects may produce errors or WRONG results! [[2, 0, 0], [1, 1, 0], [1, 0, 1], [0, 2, 0], [0, 1, 1], [0, 0, 2]] sage: list(IntegerVectors(2, 3)) @@ -1956,7 +1956,7 @@ def from_descents(descents, nps=None): sage: [x-1 for x in Composition([1, 1, 3, 4, 3]).to_subset()] [0, 1, 4, 8] sage: sage.combinat.composition.from_descents([1,0,4,8],12) - doctest:1: DeprecationWarning: from_descents is deprecated. Use Compositions().from_descents instead. + doctest:...: DeprecationWarning: from_descents is deprecated. Use Compositions().from_descents instead. See http://trac.sagemath.org/14063 for details. [1, 1, 3, 4, 3] """ @@ -1973,7 +1973,7 @@ def composition_from_subset(S, n): sage: from sage.combinat.composition import composition_from_subset sage: composition_from_subset([2,1,5,9], 12) - doctest:1: DeprecationWarning: composition_from_subset is deprecated. Use Compositions().from_subset instead. + doctest:...: DeprecationWarning: composition_from_subset is deprecated. Use Compositions().from_subset instead. See http://trac.sagemath.org/14063 for details. [1, 1, 3, 4, 3] """ @@ -1992,7 +1992,7 @@ def from_code(code): sage: Composition([4,1,2,3,5]).to_code() [1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0] sage: composition.from_code(_) - doctest:1: DeprecationWarning: from_code is deprecated. Use Compositions().from_code instead. + doctest:...: DeprecationWarning: from_code is deprecated. Use Compositions().from_code instead. See http://trac.sagemath.org/14063 for details. [4, 1, 2, 3, 5] """ diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index d9a99df92a9..1fed6dafa13 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -657,7 +657,7 @@ def points_from_gap(self): sage: from sage.combinat.designs.block_design import BlockDesign sage: BD = BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) sage: BD.points_from_gap() # optional - gap_packages (design package) - doctest:1: DeprecationWarning: Unless somebody protests this method will be removed, as nobody seems to know why it is there. + doctest:...: DeprecationWarning: Unless somebody protests this method will be removed, as nobody seems to know why it is there. See http://trac.sagemath.org/14499 for details. [1, 2, 3, 4, 5, 6, 7] """ diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 40d2b70faf6..bcffa1783ee 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -6036,7 +6036,7 @@ def prepone_output(self): obtained:: sage: C = Transducer([(0,1,0,0)]) - sage: C.prepone_output() # doctest: +ELLIPSIS + sage: C.prepone_output() verbose 0 (...: finite_state_machine.py, prepone_output) All transitions leaving state 0 have an output label with prefix 0. However, there is no inbound transition and it @@ -7144,7 +7144,7 @@ def asymptotic_moments(self, variable=SR.symbol('n')): sage: T = Transducer([[0, 0, 0, a_1], [0, 1, 1, a_3], ....: [1, 0, 0, a_4], [1, 1, 1, a_2]], ....: initial_states=[0], final_states=[0, 1]) - sage: moments = T.asymptotic_moments() # doctest: +ELLIPSIS + sage: moments = T.asymptotic_moments() verbose 0 (...) Non-integer output weights lead to significant performance degradation. sage: moments['expectation'] @@ -7315,7 +7315,7 @@ def asymptotic_moments(self, variable=SR.symbol('n')): sage: T = Transducer([[0, 0, 0, 0], [0, 0, 1, -1/2]], ....: initial_states=[0], final_states=[0]) - sage: moments = T.asymptotic_moments() # doctest: +ELLIPSIS + sage: moments = T.asymptotic_moments() verbose 0 (...) Non-integer output weights lead to significant performance degradation. sage: moments['expectation'] @@ -7357,7 +7357,7 @@ def asymptotic_moments(self, variable=SR.symbol('n')): sage: s = FSMState(0, word_out=2/3) sage: T = Transducer([(s, s, 0, 1/2)], ....: initial_states=[s], final_states=[s]) - sage: T.asymptotic_moments()['expectation'] # doctest: +ELLIPSIS + sage: T.asymptotic_moments()['expectation'] verbose 0 (...) Non-integer output weights lead to significant performance degradation. 7/6*n + Order(1) @@ -8341,7 +8341,7 @@ def cartesian_product(self, other, only_accessible_components=True): ....: final_states=[1], ....: determine_alphabets=True) sage: result = transducer1.cartesian_product(transducer2) - doctest:1: DeprecationWarning: The output of + doctest:...: DeprecationWarning: The output of Transducer.cartesian_product will change. Please use Transducer.intersection for the original output. See http://trac.sagemath.org/16061 for details. diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 745e93b975f..72e9dbf9871 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -419,7 +419,7 @@ def from_frobenius_coordinates(coords): EXAMPLES:: sage: sage.combinat.partition.from_frobenius_coordinates(([],[])) - doctest:1: DeprecationWarning: from_frobenius_coordinates is deprecated. Use Partitions().from_frobenius_coordinates instead. + doctest:...: DeprecationWarning: from_frobenius_coordinates is deprecated. Use Partitions().from_frobenius_coordinates instead. See http://trac.sagemath.org/13605 for details. [] """ @@ -435,7 +435,7 @@ def from_beta_numbers(beta): EXAMPLES:: sage: sage.combinat.partition.from_beta_numbers([0,1,2,4,5,8]) - doctest:1: DeprecationWarning: from_beta_numbers is deprecated. Use Partitions().from_beta_numbers instead. + doctest:...: DeprecationWarning: from_beta_numbers is deprecated. Use Partitions().from_beta_numbers instead. See http://trac.sagemath.org/13605 for details. [3, 1, 1] """ @@ -451,7 +451,7 @@ def from_exp(exp): EXAMPLES:: sage: sage.combinat.partition.from_exp([2,2,1]) - doctest:1: DeprecationWarning: from_exp is deprecated. Use Partitions().from_exp instead. + doctest:...: DeprecationWarning: from_exp is deprecated. Use Partitions().from_exp instead. See http://trac.sagemath.org/13605 for details. [3, 2, 2, 1, 1] """ @@ -467,7 +467,7 @@ def from_core_and_quotient(core, quotient): EXAMPLES:: sage: sage.combinat.partition.from_core_and_quotient([2,1], [[2,1],[3],[1,1,1]]) - doctest:1: DeprecationWarning: from_core_and_quotient is deprecated. Use Partitions().from_core_and_quotient instead. + doctest:...: DeprecationWarning: from_core_and_quotient is deprecated. Use Partitions().from_core_and_quotient instead. See http://trac.sagemath.org/13605 for details. [11, 5, 5, 3, 2, 2, 2] """ @@ -2746,7 +2746,7 @@ def dominate(self, rows=None): EXAMPLES:: sage: Partition([3,2,1]).dominate() - doctest:1: DeprecationWarning: dominate is deprecated. Use dominated_partitions instead. + doctest:...: DeprecationWarning: dominate is deprecated. Use dominated_partitions instead. See http://trac.sagemath.org/13605 for details. [[3, 2, 1], [3, 1, 1, 1], [2, 2, 2], [2, 2, 1, 1], [2, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]] """ @@ -6466,7 +6466,7 @@ def PartitionsInBox_hw(h, w): EXAMPLES:: sage: sage.combinat.partition.PartitionsInBox_hw(3, 2) - doctest:1: DeprecationWarning: this class is deprecated. Use PartitionsInBox instead + doctest:...: DeprecationWarning: this class is deprecated. Use PartitionsInBox instead See http://trac.sagemath.org/13605 for details. Integer partitions which fit in a 3 x 2 box """ @@ -6652,7 +6652,7 @@ def OrderedPartitions_nk(n, k): EXAMPLES:: sage: sage.combinat.partition.OrderedPartitions_nk(3, 2) - doctest:1: DeprecationWarning: this class is deprecated. Use OrderedPartitions instead + doctest:...: DeprecationWarning: this class is deprecated. Use OrderedPartitions instead See http://trac.sagemath.org/13605 for details. Ordered partitions of 3 of length 2 """ @@ -6726,7 +6726,7 @@ def PartitionsGreatestLE_nk(n, k): EXAMPLES:: sage: sage.combinat.partition.PartitionsGreatestLE_nk(10, 2) - doctest:1: DeprecationWarning: this class is deprecated. Use PartitionsGreatestLE instead + doctest:...: DeprecationWarning: this class is deprecated. Use PartitionsGreatestLE instead See http://trac.sagemath.org/13605 for details. Partitions of 10 having parts less than or equal to 2 """ @@ -6798,7 +6798,7 @@ def PartitionsGreatestEQ_nk(n, k): EXAMPLES:: sage: sage.combinat.partition.PartitionsGreatestEQ_nk(10, 2) - doctest:1: DeprecationWarning: this class is deprecated. Use PartitionsGreatestEQ instead + doctest:...: DeprecationWarning: this class is deprecated. Use PartitionsGreatestEQ instead See http://trac.sagemath.org/13605 for details. Partitions of 10 having greatest part equal to 2 """ @@ -6839,7 +6839,7 @@ def RestrictedPartitions(n, S, k=None): EXAMPLES:: sage: RestrictedPartitions(5,[3,2,1]) - doctest:1: DeprecationWarning: RestrictedPartitions is deprecated; use Partitions with the parts_in keyword instead. + doctest:...: DeprecationWarning: RestrictedPartitions is deprecated; use Partitions with the parts_in keyword instead. See http://trac.sagemath.org/13072 for details. doctest:...: DeprecationWarning: RestrictedPartitions_nsk is deprecated; use Partitions with the parts_in keyword instead. See http://trac.sagemath.org/13072 for details. @@ -6867,7 +6867,7 @@ def __init__(self, n, S, k=None): TESTS:: sage: r = RestrictedPartitions(5,[3,2,1]) - doctest:1: DeprecationWarning: RestrictedPartitions is deprecated; use Partitions with the parts_in keyword instead. + doctest:...: DeprecationWarning: RestrictedPartitions is deprecated; use Partitions with the parts_in keyword instead. See http://trac.sagemath.org/13072 for details. sage: r == loads(dumps(r)) True @@ -6909,7 +6909,7 @@ def _repr_(self): EXAMPLES:: sage: RestrictedPartitions(5,[3,2,1]).__repr__() - doctest:1: DeprecationWarning: RestrictedPartitions is deprecated; use Partitions with the parts_in keyword instead. + doctest:...: DeprecationWarning: RestrictedPartitions is deprecated; use Partitions with the parts_in keyword instead. See http://trac.sagemath.org/13072 for details. 'Partitions of 5 restricted to the values [1, 2, 3]' """ diff --git a/src/sage/combinat/root_system/cartan_matrix.py b/src/sage/combinat/root_system/cartan_matrix.py index 11b8dbd650b..2bb763642ec 100644 --- a/src/sage/combinat/root_system/cartan_matrix.py +++ b/src/sage/combinat/root_system/cartan_matrix.py @@ -735,7 +735,7 @@ def cartan_matrix(t): EXAMPLES:: sage: cartan_matrix(['A', 4]) - doctest:1: DeprecationWarning: cartan_matrix() is deprecated. Use CartanMatrix() instead + doctest:...: DeprecationWarning: cartan_matrix() is deprecated. Use CartanMatrix() instead See http://trac.sagemath.org/14137 for details. [ 2 -1 0 0] [-1 2 -1 0] diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index 1dc165ae421..ecd6d9af95c 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -1592,7 +1592,7 @@ def inf(s,t): sage: sp2 = Set([Set([1,3]), Set([2,4])]) sage: s = Set([ Set([2,4]), Set([3]), Set([1])]) #{{2, 4}, {3}, {1}} sage: sage.combinat.set_partition.inf(sp1, sp2) == s - doctest:1: DeprecationWarning: inf(s, t) is deprecated. Use s.inf(t) instead. + doctest:...: DeprecationWarning: inf(s, t) is deprecated. Use s.inf(t) instead. See http://trac.sagemath.org/14140 for details. True """ @@ -1612,7 +1612,7 @@ def sup(s,t): sage: sp2 = Set([Set([1,3]), Set([2,4])]) sage: s = Set([ Set([1,2,3,4]) ]) sage: sage.combinat.set_partition.sup(sp1, sp2) == s - doctest:1: DeprecationWarning: sup(s, t) is deprecated. Use s.sup(t) instead. + doctest:...: DeprecationWarning: sup(s, t) is deprecated. Use s.sup(t) instead. See http://trac.sagemath.org/14140 for details. True """ @@ -1632,7 +1632,7 @@ def standard_form(sp): EXAMPLES:: sage: map(sage.combinat.set_partition.standard_form, SetPartitions(4, [2,2])) - doctest:1: DeprecationWarning: standard_form(sp) is deprecated. Use sp.standard_form() instead. + doctest:...: DeprecationWarning: standard_form(sp) is deprecated. Use sp.standard_form() instead. See http://trac.sagemath.org/14140 for details. [[[1, 2], [3, 4]], [[1, 3], [2, 4]], [[1, 4], [2, 3]]] """ @@ -1649,7 +1649,7 @@ def less(s, t): sage: z = SetPartitions(3).list() sage: sage.combinat.set_partition.less(z[0], z[1]) - doctest:1: DeprecationWarning: less(s, t) is deprecated. Use SetPartitions.is_less_tan(s, t) instead. + doctest:...: DeprecationWarning: less(s, t) is deprecated. Use SetPartitions.is_less_tan(s, t) instead. See http://trac.sagemath.org/14140 for details. False """ diff --git a/src/sage/combinat/skew_partition.py b/src/sage/combinat/skew_partition.py index b1eed9d5da6..024921208f8 100644 --- a/src/sage/combinat/skew_partition.py +++ b/src/sage/combinat/skew_partition.py @@ -1142,7 +1142,7 @@ def from_row_and_column_length(rowL, colL): EXAMPLES:: sage: sage.combinat.skew_partition.from_row_and_column_length([3,1,2,2],[2,3,1,1,1]) - doctest:1: DeprecationWarning: from_row_and_column_length is deprecated. Use SkewPartitions().from_row_and_column_length instead. + doctest:...: DeprecationWarning: from_row_and_column_length is deprecated. Use SkewPartitions().from_row_and_column_length instead. See http://trac.sagemath.org/14101 for details. [5, 2, 2, 2] / [2, 1] """ diff --git a/src/sage/combinat/words/alphabet.py b/src/sage/combinat/words/alphabet.py index db9f0baee76..c52979a4241 100644 --- a/src/sage/combinat/words/alphabet.py +++ b/src/sage/combinat/words/alphabet.py @@ -285,7 +285,7 @@ class OrderedAlphabet(object): sage: from sage.combinat.words.alphabet import OrderedAlphabet sage: A = OrderedAlphabet('ab'); A - doctest:1: DeprecationWarning: OrderedAlphabet is deprecated; use Alphabet instead. + doctest:...: DeprecationWarning: OrderedAlphabet is deprecated; use Alphabet instead. See http://trac.sagemath.org/8920 for details. {'a', 'b'} sage: type(A) @@ -297,7 +297,7 @@ def __new__(self, alphabet=None, name=None): sage: from sage.combinat.words.alphabet import OrderedAlphabet sage: A = OrderedAlphabet('ab'); A # indirect doctest - doctest:1: DeprecationWarning: OrderedAlphabet is deprecated; use Alphabet instead. + doctest:...: DeprecationWarning: OrderedAlphabet is deprecated; use Alphabet instead. See http://trac.sagemath.org/8920 for details. {'a', 'b'} """ @@ -330,7 +330,7 @@ def __getattr__(self, name): sage: from sage.combinat.words.alphabet import OrderedAlphabet sage: O = OrderedAlphabet() - doctest:1: DeprecationWarning: OrderedAlphabet is deprecated; use Alphabet instead. + doctest:...: DeprecationWarning: OrderedAlphabet is deprecated; use Alphabet instead. See http://trac.sagemath.org/8920 for details. sage: O._alphabet = ['a', 'b'] sage: O._elements diff --git a/src/sage/databases/sloane.py b/src/sage/databases/sloane.py index 6788c60546b..91df768bb32 100644 --- a/src/sage/databases/sloane.py +++ b/src/sage/databases/sloane.py @@ -379,7 +379,7 @@ def parse_sequence(text=''): TESTS:: sage: from sage.databases.sloane import parse_sequence sage: parse_sequence() - doctest:1: DeprecationWarning: The function parse_sequence is not used anymore (2012-01-01). + doctest:...: DeprecationWarning: The function parse_sequence is not used anymore (2012-01-01). See http://trac.sagemath.org/10358 for details. """ deprecation(10358, "The function parse_sequence is not used anymore (2012-01-01).") @@ -394,7 +394,7 @@ def sloane_sequence(number=1, verbose=True): TESTS:: sage: sloane_sequence(123) - doctest:1: DeprecationWarning: The function sloane_sequence is deprecated. Use oeis() instead (2012-01-01). + doctest:...: DeprecationWarning: The function sloane_sequence is deprecated. Use oeis() instead (2012-01-01). See http://trac.sagemath.org/10358 for details. """ deprecation(10358, @@ -411,7 +411,7 @@ def sloane_find(list=[], nresults=30, verbose=True): TESTS:: sage: sloane_find([1,2,3]) - doctest:1: DeprecationWarning: The function sloane_find is deprecated. Use oeis() instead (2012-01-01). + doctest:...: DeprecationWarning: The function sloane_find is deprecated. Use oeis() instead (2012-01-01). See http://trac.sagemath.org/10358 for details. """ deprecation(10358, diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 2273d434a82..ed3ea0c99d3 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -135,7 +135,7 @@ def warning_function(file): sage: wrn("bad stuff", UserWarning, "myfile.py", 0) sage: F.seek(0) sage: F.read() - 'doctest:0: UserWarning: bad stuff\n' + 'doctest:...: UserWarning: bad stuff\n' """ def doctest_showwarning(message, category, filename, lineno, file=file, line=None): try: diff --git a/src/sage/functions/bessel.py b/src/sage/functions/bessel.py index c3bb57416bd..b7cd7313a9a 100644 --- a/src/sage/functions/bessel.py +++ b/src/sage/functions/bessel.py @@ -307,7 +307,7 @@ def __call__(self, *args, **kwds): EXAMPLES:: sage: bessel_J(0, 1.0, "maxima", 53) - doctest:1: DeprecationWarning: precision argument is deprecated; algorithm argument is currently deprecated, but will be available as a named keyword in the future + doctest:...: DeprecationWarning: precision argument is deprecated; algorithm argument is currently deprecated, but will be available as a named keyword in the future See http://trac.sagemath.org/4102 for details. 0.7651976865579666 """ @@ -477,7 +477,7 @@ def __call__(self, *args, **kwds): EXAMPLES:: sage: bessel_Y(0, 1, "maxima", 53) - doctest:1: DeprecationWarning: precision argument is deprecated; algorithm argument is currently deprecated, but will be available as a named keyword in the future + doctest:...: DeprecationWarning: precision argument is deprecated; algorithm argument is currently deprecated, but will be available as a named keyword in the future See http://trac.sagemath.org/4102 for details. 0.0882569642156769 """ @@ -649,7 +649,7 @@ def __call__(self, *args, **kwds): EXAMPLES:: sage: bessel_I(0, 1, "maxima", 53) - doctest:1: DeprecationWarning: precision argument is deprecated; algorithm argument is currently deprecated, but will be available as a named keyword in the future + doctest:...: DeprecationWarning: precision argument is deprecated; algorithm argument is currently deprecated, but will be available as a named keyword in the future See http://trac.sagemath.org/4102 for details. 1.266065877752009 """ @@ -846,7 +846,7 @@ def __call__(self, *args, **kwds): EXAMPLES:: sage: bessel_K(0, 1, "maxima", 53) - doctest:1: DeprecationWarning: precision argument is deprecated; algorithm argument is currently deprecated, but will be available as a named keyword in the future + doctest:...: DeprecationWarning: precision argument is deprecated; algorithm argument is currently deprecated, but will be available as a named keyword in the future See http://trac.sagemath.org/4102 for details. 0.0882569642156769 """ diff --git a/src/sage/functions/hyperbolic.py b/src/sage/functions/hyperbolic.py index f2249c1b2fb..54a26d5b296 100644 --- a/src/sage/functions/hyperbolic.py +++ b/src/sage/functions/hyperbolic.py @@ -608,7 +608,7 @@ def _eval_numpy_(self, x): sage: import numpy sage: a = numpy.linspace(0,1,3) sage: arcsech(a) - doctest:614: RuntimeWarning: divide by zero encountered in divide + doctest:...: RuntimeWarning: divide by zero encountered in divide array([ inf, 1.3169579, 0. ]) """ return arccosh(1.0 / x) @@ -658,7 +658,7 @@ def _eval_numpy_(self, x): sage: import numpy sage: a = numpy.linspace(0,1,3) sage: arccsch(a) - doctest:664: RuntimeWarning: divide by zero encountered in divide + doctest:...: RuntimeWarning: divide by zero encountered in divide array([ inf, 1.44363548, 0.88137359]) """ return arcsinh(1.0 / x) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index a7b3ae7322a..5d278c3662e 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -2841,7 +2841,7 @@ def line_set(self): sage: halfplane = Cone([(1,0), (0,1), (-1,0)]) sage: halfplane.line_set() - doctest:1: DeprecationWarning: + doctest:...: DeprecationWarning: line_set(...) is deprecated, please use lines().set() instead! See http://trac.sagemath.org/12544 for details. frozenset([N(1, 0)]) diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index 3eb74a7bd11..ce5135d84ba 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -2390,7 +2390,7 @@ def normal_form(self): sage: o = lattice_polytope.cross_polytope(3) sage: o.normal_form() - doctest:1: DeprecationWarning: normal_form() output will change, + doctest:...: DeprecationWarning: normal_form() output will change, please use normal_form_pc().column_matrix() instead or consider using normal_form_pc() directly! See http://trac.sagemath.org/15240 for details. @@ -3216,7 +3216,7 @@ def points(self): sage: o = lattice_polytope.cross_polytope(3) sage: o.points() - doctest:1: DeprecationWarning: points() output will change, + doctest:...: DeprecationWarning: points() output will change, please use points_pc().column_matrix() instead or consider using points_pc() directly! See http://trac.sagemath.org/15240 for details. @@ -3616,7 +3616,7 @@ def vertices(self): sage: o = lattice_polytope.cross_polytope(3) sage: o.vertices() - doctest:1: DeprecationWarning: vertices() output will change, + doctest:...: DeprecationWarning: vertices() output will change, please use vertices_pc().column_matrix() instead or consider using vertices_pc() directly! See http://trac.sagemath.org/15240 for details. @@ -5395,7 +5395,7 @@ def always_use_files(new_state=None): EXAMPLES:: sage: lattice_polytope.always_use_files() - doctest:1: DeprecationWarning: using PALP via pipes is deprecated and + doctest:...: DeprecationWarning: using PALP via pipes is deprecated and will be removed, if you have a use case for this, please email Andrey Novoseltsev See http://trac.sagemath.org/15240 for details. @@ -5545,7 +5545,7 @@ def filter_polytopes(f, polytopes, subseq=None, print_numbers=False): This filters polytopes of dimension at least 4:: sage: lattice_polytope.filter_polytopes(lambda p: p.dim() >= 4, polytopes) - doctest:1: DeprecationWarning: filter_polytopes is deprecated, + doctest:...: DeprecationWarning: filter_polytopes is deprecated, use standard tools instead See http://trac.sagemath.org/15240 for details. [2, 3, 4] @@ -5737,7 +5737,7 @@ def projective_space(dim): EXAMPLES: We construct 3- and 4-dimensional simplexes:: sage: p = lattice_polytope.projective_space(3) - doctest:1: DeprecationWarning: this function is deprecated, + doctest:...: DeprecationWarning: this function is deprecated, perhaps toric_varieties.P(n) is what you are looking for? See http://trac.sagemath.org/15240 for details. sage: p diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index 898ebe9b4e6..c7d5a8164a9 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -2238,7 +2238,7 @@ cdef class CachedMethod(object): :meth:`sage.misc.superseded.deprecated_function_alias`:: sage: a.g() is a.f() - doctest:1: DeprecationWarning: g is deprecated. Please use f instead. + doctest:...: DeprecationWarning: g is deprecated. Please use f instead. See http://trac.sagemath.org/57 for details. True sage: Foo.g(a) is a.f() diff --git a/src/sage/misc/functional.py b/src/sage/misc/functional.py index 087855306ad..0d631b86d6f 100644 --- a/src/sage/misc/functional.py +++ b/src/sage/misc/functional.py @@ -1708,7 +1708,7 @@ def sqrt(x): EXAMPLES:: sage: numerical_sqrt(10.1) - doctest:1: DeprecationWarning: numerical_sqrt is deprecated, use sqrt(x, prec=n) instead + doctest:...: DeprecationWarning: numerical_sqrt is deprecated, use sqrt(x, prec=n) instead See http://trac.sagemath.org/5404 for details. 3.17804971641414 sage: numerical_sqrt(9) diff --git a/src/sage/misc/misc.py b/src/sage/misc/misc.py index c87e5c25ea4..80664c62148 100644 --- a/src/sage/misc/misc.py +++ b/src/sage/misc/misc.py @@ -2370,7 +2370,7 @@ def inject_variable(name, value): sage: from warnings import warn sage: warn("blah") - doctest:1: UserWarning: blah + doctest:...: UserWarning: blah sage: warn("blah") Use with care! diff --git a/src/sage/misc/superseded.py b/src/sage/misc/superseded.py index b1ca15d0958..1fdd62f36d6 100644 --- a/src/sage/misc/superseded.py +++ b/src/sage/misc/superseded.py @@ -82,7 +82,7 @@ def deprecation(trac_number, message): sage: def foo(): ....: sage.misc.superseded.deprecation(13109, 'the function foo is replaced by bar') sage: foo() - doctest:1: DeprecationWarning: the function foo is replaced by bar + doctest:...: DeprecationWarning: the function foo is replaced by bar See http://trac.sagemath.org/13109 for details. """ _check_trac_number(trac_number) @@ -192,7 +192,7 @@ def __call__(self, *args, **kwds): sage: def bla(): return 42 sage: blo = deprecated_function_alias(13109, bla) sage: blo() - doctest:1: DeprecationWarning: blo is deprecated. Please use bla instead. + doctest:...: DeprecationWarning: blo is deprecated. Please use bla instead. See http://trac.sagemath.org/13109 for details. 42 """ diff --git a/src/sage/modules/vector_double_dense.pyx b/src/sage/modules/vector_double_dense.pyx index b7bd05fb179..09e9d46b0df 100644 --- a/src/sage/modules/vector_double_dense.pyx +++ b/src/sage/modules/vector_double_dense.pyx @@ -710,7 +710,7 @@ cdef class Vector_double_dense(free_module_element.FreeModuleElement): 0.953760808... sage: w = vector(CDF, [-1,0,1]) sage: w.norm(p=-1.6) - doctest:2097: RuntimeWarning: divide by zero encountered in power + doctest:...: RuntimeWarning: divide by zero encountered in power 0.0 Return values are in ``RDF``, or an integer when ``p = 0``. :: diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 560ac9048b6..6f20ea39c3c 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -1227,7 +1227,7 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p = MixedIntegerLinearProgram() sage: b = p.new_variable(dim=2) - doctest:839: DeprecationWarning: The 'dim' argument will soon disappear. Fortunately variable[1,2] is easier to use than variable[1][2] + doctest:...: DeprecationWarning: The 'dim' argument will soon disappear. Fortunately variable[1,2] is easier to use than variable[1][2] See http://trac.sagemath.org/15489 for details. sage: p.add_constraint(b[1][2] + b[2][3] == 0) sage: _ = p.solve() diff --git a/src/sage/plot/graphics.py b/src/sage/plot/graphics.py index 1e07333537e..d3f8a47084e 100644 --- a/src/sage/plot/graphics.py +++ b/src/sage/plot/graphics.py @@ -52,7 +52,7 @@ def show_default(default=None): ``False`` in doctests:: sage: show_default() # long time - doctest:1: DeprecationWarning: this is done automatically by the doctest framework + doctest:...: DeprecationWarning: this is done automatically by the doctest framework See http://trac.sagemath.org/14469 for details. False """ diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index cc2226e88e1..c3d1124cd68 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -3156,7 +3156,7 @@ def integral_weierstrass_model(self): sage: E = EllipticCurve('17a1') sage: E.integral_weierstrass_model() #random - doctest:1: DeprecationWarning: integral_weierstrass_model is deprecated, use integral_short_weierstrass_model instead! + doctest:...: DeprecationWarning: integral_weierstrass_model is deprecated, use integral_short_weierstrass_model instead! Elliptic Curve defined by y^2 = x^3 - 11*x - 890 over Rational Field """ from sage.misc.superseded import deprecation @@ -4746,7 +4746,7 @@ def is_reducible(self, p): EXAMPLES:: sage: EllipticCurve('20a1').is_reducible(3) #random - doctest:1: DeprecationWarning: is_reducible is deprecated, use galois_representation().is_reducible(p) instead! + doctest:...: DeprecationWarning: is_reducible is deprecated, use galois_representation().is_reducible(p) instead! True """ @@ -4766,7 +4766,7 @@ def is_irreducible(self, p): EXAMPLES:: sage: EllipticCurve('20a1').is_irreducible(7) #random - doctest:1: DeprecationWarning: is_irreducible is deprecated, use galois_representation().is_irreducible(p) instead! + doctest:...: DeprecationWarning: is_irreducible is deprecated, use galois_representation().is_irreducible(p) instead! True """ @@ -4786,7 +4786,7 @@ def is_surjective(self, p, A=1000): EXAMPLES:: sage: EllipticCurve('20a1').is_surjective(7) #random - doctest:1: DeprecationWarning: is_surjective is deprecated, use galois_representation().is_surjective(p) instead! + doctest:...: DeprecationWarning: is_surjective is deprecated, use galois_representation().is_surjective(p) instead! True """ @@ -4806,7 +4806,7 @@ def reducible_primes(self): EXAMPLES:: sage: EllipticCurve('20a1').reducible_primes() #random - doctest:1: DeprecationWarning: reducible_primes is deprecated, use galois_representation().reducible_primes() instead! + doctest:...: DeprecationWarning: reducible_primes is deprecated, use galois_representation().reducible_primes() instead! [2,3] """ @@ -4826,7 +4826,7 @@ def non_surjective(self, A=1000): EXAMPLES:: sage: EllipticCurve('20a1').non_surjective() #random - doctest:1: DeprecationWarning: non_surjective is deprecated, use galois_representation().non_surjective() instead! + doctest:...: DeprecationWarning: non_surjective is deprecated, use galois_representation().non_surjective() instead! [2,3] """ From cb60470c5e8885ef031ed8bb4a84a38f3e8987f5 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 8 Jul 2014 17:57:32 +0200 Subject: [PATCH 497/546] trac #16622: Broken doc --- src/doc/en/reference/graphs/index.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/doc/en/reference/graphs/index.rst b/src/doc/en/reference/graphs/index.rst index 5beb0896d28..579c664638c 100644 --- a/src/doc/en/reference/graphs/index.rst +++ b/src/doc/en/reference/graphs/index.rst @@ -49,8 +49,7 @@ Hypergraphs :maxdepth: 1 sage/graphs/hypergraph_generators - sage/graphs/hypergraph - + sage/combinat/designs/incidence_structures Libraries of algorithms From 9000fb85706fa54e1dd315b8eab9241e7ea45e8a Mon Sep 17 00:00:00 2001 From: Bruno Grenet Date: Tue, 8 Jul 2014 18:11:29 +0200 Subject: [PATCH 498/546] Add quo_rem method in class Polynomial_generic_sparse --- .../polynomial/polynomial_element_generic.py | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index 39ada32fe02..92a2cdb5f24 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -555,6 +555,59 @@ def shift(self, n): output[index + n] = coeff return self.parent()(output, check=False) + @coerce_binop + def quo_rem(self, other): + """ + Returns the quotient and remainder of the Euclidean division of + ``self`` and ``other``. + + Raises ZerodivisionError if ``other`` is zero. Raises ArithmeticError + if ``other`` has a nonunit leading coefficient. + + EXAMPLES:: + + sage: P. = PolynomialRing(QQ,sparse=True) + sage: R. = PolynomialRing(P,sparse=True) + sage: f = R.random_element(10) + sage: g = y^5+R.random_element(4) + sage: q,r = f.quo_rem(g) + sage: f == q*g + r + True + sage: g = x*y^5 + sage: f.quo_rem(g) + Traceback (most recent call last): + ... + ArithmeticError: Nonunit leading coefficient + sage: g = 0 + sage: f.quo_rem(g) + Traceback (most recent call last): + ... + ZeroDivisionError: Division by zero polynomial + """ + if other.is_zero(): + raise ZeroDivisionError("Division by zero polynomial") + if not other.leading_coefficient().is_unit(): + raise ArithmeticError("Nonunit leading coefficient") + if self.is_zero(): + return self, self + + R = self.parent() + + d = other.degree() + if self.degree() < d: + return R.zero_element(), self + + quo = R.zero_element() + rem = self + inv_lc = R.base_ring().one_element()/other.leading_coefficient() + + while rem.degree() > d: + c = inv_lc*rem.leading_coefficient() + e = rem.degree() - d + quo += c*R.one_element().shift(e) + rem -= c*other.shift(e) + return (quo,rem) + class Polynomial_generic_domain(Polynomial, IntegralDomainElement): def __init__(self, parent, is_gen=False, construct=False): From 128a4f935b9ddd2e631b4bc12e056ff950c8b173 Mon Sep 17 00:00:00 2001 From: drvinceknight Date: Tue, 8 Jul 2014 17:18:43 +0100 Subject: [PATCH 499/546] Some tweaks to docs --- src/sage/game_theory/cooperative_game.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 4073edcca18..808e360b9d8 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -93,17 +93,17 @@ class CooperativeGame(SageObject): sage: letter_game = CooperativeGame(letter_function) Please note that keys should be tuples. ``'1, 2, 3'`` is not a valid key, - neither is ``123``. The correct input would be ``(1, 2, 3)``, be - particulary careful with single element tuples as these should also - contain a comma ie; ``(1,)``, although a check has been implemented to - convert ``(1)`` to ``(1,)``. + neither is ``123``. The correct input would be ``(1, 2, 3)``. Similarly, + for coalitions containing a single element the bracket notation (which + tells Sage that it is a tuple) must be used. So ``(1)``, ``(1,)`` are + correct however simply inputting `1` is not. Characteristic function games can be of various types. A characteristic function game `G = (N, v)` is monotone if it satisfies `v(C_2) \geq v(C_1)` for all `C_1 \subseteq C_2`. A characteristic function game `G = (N, v)` is superadditive if it satisfies - `v(C_1 \cup C_2) \geq v(C_1) v(C_2)` for all `C_1, C_2 \subseteq N` such + `v(C_1 \cup C_2) \geq v(C_1) + v(C_2)` for all `C_1, C_2 \subseteq N` such that `C_1 \cap C_2 = \emptyset`. We can test if a game is monotonic or superadditive. :: @@ -139,7 +139,7 @@ class CooperativeGame(SageObject): of the form `(i, j)`). This payoff vector is "fair" in that it has a collection of properties - referred to as: Efficiency, Symmetry, Linearity and Null player. + referred to as: efficiency, symmetry, additivity and Null player. Some of these properties are considered in this documentation (and tests are implemented in the class) but for a good overview see [CEW2011]_. @@ -194,8 +194,8 @@ class CooperativeGame(SageObject): * Symmetry property - If `v(C \cup i) = v(C \cup j)` for all `C \in 2^{\Omega} \setminus \{i,j\}`, then `x_i = x_j`. - - If players contribute symmetrically then they should get the same payoff:: + If players contribute symmetrically then they should get the same + payoff:: sage: payoff_vector = letter_game.shapley_value() sage: letter_game.is_efficient(payoff_vector) From 7361632afef2d83e045f3fedcf6456df6d0cfa99 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Tue, 8 Jul 2014 13:11:22 -0400 Subject: [PATCH 500/546] Check doctest output for source line numbers --- src/sage/doctest/forker.py | 2 ++ src/sage/doctest/reporting.py | 6 +++++- src/sage/doctest/sources.py | 27 +++++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index ed3ea0c99d3..2e6ea626e4b 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -2105,6 +2105,8 @@ def __call__(self, options, outtmpfile=None, msgfile=None, result_queue=None): if extras['tab']: results.err = 'tab' results.tab_linenos = extras['tab'] + if extras['line_number']: + results.err = 'line_number' results.optionals = extras['optionals'] # We subtract 1 to remove the sig_on_count() tests result = (sum([max(0,len(test.examples) - 1) for test in doctests]), results) diff --git a/src/sage/doctest/reporting.py b/src/sage/doctest/reporting.py index fb8be8e5472..295183289ee 100644 --- a/src/sage/doctest/reporting.py +++ b/src/sage/doctest/reporting.py @@ -14,7 +14,7 @@ - 32: TAB character found - 64: Internal error in the doctesting framework - 128: Testing interrupted, not all tests run - +- 256: Doctest contains explicit source line number AUTHORS: @@ -375,6 +375,10 @@ def report(self, source, timeout, return_code, results, output, pid=None): log(" Error: TAB character found at line%s"%(tabs)) postscript['lines'].append(cmd + " # Tab character found") self.error_status |= 32 + elif result_dict.err == 'line_number': + log(" Error: Source line number found") + postscript['lines'].append(cmd + " # Source line number found") + self.error_status |= 256 elif result_dict.err is not None: # This case should not occur if result_dict.err is True: diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index ce91290292d..6b55ce3afd4 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -52,6 +52,9 @@ sagestart = re.compile(r"^\s*(>>> |sage: )\s*[^#\s]") untested = re.compile("(not implemented|not tested)") +# Source line number in warning output +doctest_line_number = re.compile(r"^\s*doctest:[0-9]") + def get_basename(path): """ @@ -230,6 +233,8 @@ def _create_doctests(self, namespace, tab_okay=None): 40 sage: extras['tab'] False + sage: extras['line_number'] + False """ if tab_okay is None: tab_okay = isinstance(self,TexSource) @@ -244,7 +249,10 @@ def _create_doctests(self, namespace, tab_okay=None): doc = [] start = None tab_locations = [] + contains_line_number = False for lineno, line in self: + if doctest_line_number.search(line) is not None: + contains_line_number = True if "\t" in line: tab_locations.append(str(lineno+1)) if "SAGE_DOCTEST_ALLOW_TABS" in line: @@ -293,8 +301,9 @@ def _create_doctests(self, namespace, tab_okay=None): if unparsed_doc: self._process_doc(doctests, doc, namespace, start) - extras = dict(tab = not tab_okay and tab_locations, - optionals = self.parser.optionals) + extras = dict(tab=not tab_okay and tab_locations, + line_number=contains_line_number, + optionals=self.parser.optionals) if self.options.randorder is not None and self.options.randorder is not False: # we want to randomize even when self.randorder = 0 random.seed(self.options.randorder) @@ -340,6 +349,20 @@ class StringDocTestSource(DocTestSource): 1 sage: extras['tab'] [] + sage: extras['line_number'] + False + + sage: s = "'''\n\tsage: 2 + 2\n\t4\n'''" + sage: PSS = PythonStringSource('', s, DocTestDefaults(), 'runtime') + sage: dt, extras = PSS.create_doctests({}) + sage: extras['tab'] + ['2', '3'] + + sage: s = "'''\n sage: import warnings; warnings.warn('foo')\n doctest:1: UserWarning: foo \n'''" + sage: PSS = PythonStringSource('', s, DocTestDefaults(), 'runtime') + sage: dt, extras = PSS.create_doctests({}) + sage: extras['line_number'] + True """ def __init__(self, basename, source, options, printpath, lineno_shift=0): r""" From c27d8026caf3bfcac9e7a442c841db6dbf432c40 Mon Sep 17 00:00:00 2001 From: Solomon Vishkautsan Date: Tue, 8 Jul 2014 20:19:08 +0300 Subject: [PATCH 501/546] deleted macaulay_resultant.py --- .../rings/polynomial/macaulay_resultant.py | 350 ------------------ 1 file changed, 350 deletions(-) delete mode 100644 src/sage/rings/polynomial/macaulay_resultant.py diff --git a/src/sage/rings/polynomial/macaulay_resultant.py b/src/sage/rings/polynomial/macaulay_resultant.py deleted file mode 100644 index 4cf7197642c..00000000000 --- a/src/sage/rings/polynomial/macaulay_resultant.py +++ /dev/null @@ -1,350 +0,0 @@ -""" -Macaulay Resultant of Multivariate Polynomials - -This is an implementation of the Macaulay Resultant. It computes -the resultant of universal polynomials as well as polynomials -with constant coefficients. This is a project done in -sage days 55. It's based on the implementation in Maple by -Manfred Minimair, which in turn is based on the following: - --Using Algebraic Geometry by Cox, Little, O'Shea --Canny, J., "Generalised characteristic polynomials", p.241--250, - J. Symbolic Comput. Vol. 9, No. 3, 1990 --The algebraic theory of modular systems by Macaulay - - -AUTHORS: - -- Hao Chen -- Solomon Vishkautsan - -""" - -from sage.combinat.integer_vector_weighted import WeightedIntegerVectors -from sage.misc.misc_c import prod -from sage.matrix.constructor import matrix -from sage.rings.integer_ring import ZZ -from sage.rings.arith import binomial -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - -def getS(mon_deg_tuple,dlist): - r""" - In the MR algorithm the list of all monomials of the total degree is partitioned into sets S_i. - This function returns the index i for the set S_i for the inputted given monomial. - - INPUT: - - - `mon_deg_tuple` -- a list representing a monomial of a degree d - - `dlist` -- a list of degrees d_i of the polynomials in question, where - d = sum(dlist) - len(dlist) + 1 - - OUTPUT: - - - the index i such that the input monomial lives in S_i - - EXAMPLES:: - - sage: from sage.rings.polynomial.macaulay_resultant import getS - sage: getS([1,1,0],[2,1,1]) # the monomial xy where the total degree = 2 - 1 - - sage: getS([29,21,8],[10,20,30]) - 0 - - sage: getS(range(0,9)+[10],range(1,11)) - 9 - """ - for i in xrange(len(dlist)): - if mon_deg_tuple[i] - dlist[i] >= 0: - return i - - -def monomials(d,R): - r""" - returns all the monomials of degree d with variables a list of - generators of the ring R - - INPUT: - - -`d` -- a positive integer - -`R` -- a polynoimal ring - - OUTPUT: - - - a list of all monomials of degree d. - - EXAMPLES:: - - sage: from sage.rings.polynomial.macaulay_resultant import monomials - sage: monomials(3, PolynomialRing(QQ,3,'x')) - [x0^3, x0^2*x1, x0^2*x2, x0*x1^2, x0*x1*x2, x0*x2^2, x1^3, x1^2*x2, x1*x2^2, x2^3] - - It's OK if the coefficients of R live in some polynomial ring:: - - sage: U = PolynomialRing(QQ,20,'u') - sage: monomials(2, PolynomialRing(U,5,'x')) - [x0^2, x0*x1, x0*x2, x0*x3, x0*x4, x1^2, x1*x2, x1*x3, x1*x4, x2^2, x2*x3, x2*x4, x3^2, x3*x4, x4^2] - - """ - xlist = R.gens() - n = len(xlist) -1 - one_list = [1 for i in xrange(0,n+1)] - degs = WeightedIntegerVectors(d, one_list) - return [prod([xlist[i]**(deg[n-i]) for i in xrange(0,len(deg))]) - for deg in degs] - -def is_reduced(mon_degs,dlist): - r""" - A monomial in the variables x_0,...,x_n is called reduced with respect to the list of degrees d_0,...,d_n - if the degree of x_i in the monomial is >= d_i for exactly one i. This function checks this property for an inputted monomial. - - INPUT: - - - mon -- a monomial in the variables listed in xlist - - dlist -- a list of degrees with respect to which we check reducedness - - xlist -- a list of variables in some Polynomial ring. - - OUTPUT: - - - True/False - - EXAMPLES:: - - sage: from sage.rings.polynomial.macaulay_resultant import is_reduced - sage: R. = PolynomialRing(QQ,3) - sage: is_reduced([2,3,1],[2,3,3]) # the monomial x^2*y^3*z is not reduced w.r.t. degrees vector [2,3,3] - False - - sage: R. = PolynomialRing(QQ,3) - sage: is_reduced([1,3,2],[2,3,3]) # the monomial x*y^3*z^2 is not reduced w.r.t. degrees vector [2,3,3] - True - """ - #TODO fix comments to reflect change in input parameters - #RRR deg = [mon.degree(xi) for xi in xlist] - diff = [mon_degs[i] - dlist[i] for i in xrange(0,len(dlist))] - return len([1 for d in diff if d >= 0]) == 1 - -def construct_universal_polynomial(dlist): - r""" - Given a list of degrees, this function returns a list of len(dlist) polynomials with len(dlist) variables, - with generic coefficients. This is useful for generating polynomials for tests, - and for getting the general resultant for the given degrees. - - INPUT: - - - dlist -- a list of degrees. - - OUTPUT: - - - a list of polynomials of the given degrees with general coefficients. - - a polynomial ring over ZZ generated by the coefficients of the output polynomials. - - EXAMPLES:: - - sage: from sage.rings.polynomial.macaulay_resultant import construct_universal_polynomial - sage: construct_universal_polynomial([1,1,2]) - ([u0*x0 + u1*x1 + u2*x2, u3*x0 + u4*x1 + u5*x2, u6*x0^2 + u7*x0*x1 + u9*x1^2 + u8*x0*x2 + u10*x1*x2 + u11*x2^2], - Multivariate Polynomial Ring in u0, u1, u2, u3, u4, u5, u6, u7, u8, u9, u10, u11 over Integer Ring) - """ - - n = len(dlist) - 1 - number_of_coeffs = sum([binomial(n+di,di) for di in dlist]) - U = PolynomialRing(ZZ,'u',number_of_coeffs) - d = sum(dlist) - len(dlist) + 1 - flist = [] - R = PolynomialRing(U,'x',n+1) - #TODO remove ugly prints - #print R - ulist = U.gens() - for d in dlist: - # construct a universal polynomial of degree d - # suppose we already have mon_d - mon_d = monomials(d,R) - #print mon_d - f = sum([mon_d[i]*ulist[i] for i in xrange(0,len(mon_d))]) - flist.append (f) - #print f - ulist = ulist[len(mon_d):] - return flist, U - -def macaulay_resultant(flist): - r""" - This is an implementation of the Macaulay Resultant. It computes - the resultant of universal polynomials as well as polynomials - with constant coefficients. This is a project done in - sage days 55. It's based on the implementation in Maple by - Manfred Minimair, which in turn is based on the following: - - -Using Algebraic Geometry by Cox, Little, O'Shea - -Canny, J., "Generalised characteristic polynomials", p.241--250, - J. Symbolic Comput. Vol. 9, No. 3, 1990 - -The algebraic theory of modular systems by Macaulay - - It calculates the Macaulay resultant for a list of Polynomials, - up to sign! - - AUTHORS: - - - Hao Chen - - Solomon Vishkautsan - - - INPUT: - - - flist -- a list of n homogeneous polynomials in n variables - - OUTPUT: - - - the resultant - - EXAMPLES: - - - The number of polynomials has to match the number of variables:: - - sage: R. = PolynomialRing(QQ,3) - sage: macaulay_resultant([y,x+z]) - Traceback (most recent call last): - ... - AssertionError: number of polynomials(= 2) must equal number of variables (= 3) - - The polynomials need to be all homogeneous:: - - sage: from sage.rings.polynomial.macaulay_resultant import * - sage: R. = PolynomialRing(QQ,3) - sage: macaulay_resultant([y, x+z, z+x^3]) - Traceback (most recent call last): - ... - AssertionError: resultant for non-homogeneous polynomials is not supported - - The following example recreates Proposition 2.10 in Ch.3 of Using Algebraic Geometry:: - - sage: flist,_ = construct_universal_polynomial([1,1,2]) - sage: macaulay_resultant(flist) - u2^2*u4^2*u6 - 2*u1*u2*u4*u5*u6 + u1^2*u5^2*u6 - u2^2*u3*u4*u7 + u1*u2*u3*u5*u7 + u0*u2*u4*u5*u7 - u0*u1*u5^2*u7 + u1*u2*u3*u4*u8 - u0*u2*u4^2*u8 - u1^2*u3*u5*u8 + u0*u1*u4*u5*u8 + u2^2*u3^2*u9 - 2*u0*u2*u3*u5*u9 + u0^2*u5^2*u9 - u1*u2*u3^2*u10 + u0*u2*u3*u4*u10 + u0*u1*u3*u5*u10 - u0^2*u4*u5*u10 + u1^2*u3^2*u11 - 2*u0*u1*u3*u4*u11 + u0^2*u4^2*u11 - - The following example degenerates into the determinant of a 3*3 matrix:: - - sage: flist,_ = construct_universal_polynomial([1,1,1]) - sage: macaulay_resultant(flist) - -u2*u4*u6 + u1*u5*u6 + u2*u3*u7 - u0*u5*u7 - u1*u3*u8 + u0*u4*u8 - - The following example is by Patrick Ingram(arxiv:1310.4114):: - - sage: U = PolynomialRing(ZZ,'y',2); y0,y1 = U.gens() - sage: R = PolynomialRing(U,'x',3); x0,x1,x2 = R.gens() - sage: f0 = y0*x2^2 - x0^2 + 2*x1*x2 - sage: f1 = y1*x2^2 - x1^2 + 2*x0*x2 - sage: f2 = x0*x1 - x2^2 - sage: flist = [f0,f1,f2] - sage: macaulay_resultant([f0,f1,f2]) - y0^2*y1^2 - 4*y0^3 - 4*y1^3 + 18*y0*y1 - 27 - - a simple example with constant rational coefficients:: - - sage: R. = PolynomialRing(QQ,4) - sage: macaulay_resultant([w,z,y,x]) - 1 - - an example where the resultant vanishes:: - - sage: R. = PolynomialRing(QQ,3) - sage: macaulay_resultant([x+y,y^2,x]) - 0 - - an example of bad reduction at a prime p = 5:: - - sage: R. = PolynomialRing(QQ,3) - sage: macaulay_resultant([y,x^3+25*y^2*x,5*z]) - 125 - - an example when the coefficients live in a finite field:: - - sage: F = FiniteField(11) - sage: R. = PolynomialRing(F,4) - sage: macaulay_resultant([z,x^3,5*y,w]) - 4 - - - example when the denominator in the algorithm vanishes(in this case - the resultant is the constant term of the quotient of - char polynomials of numerator/denominator):: - - sage: R. = PolynomialRing(QQ,3) - sage: macaulay_resultant([y, x+z, z^2]) - -1 - - when there are only 2 functions, macaulay resultant degenerates to the traditional resultant:: - - sage: R. = PolynomialRing(QQ,1) - sage: f = x^2+1; g = x^5+1 - sage: f.resultant(g) == macaulay_resultant([f.homogenize(),g.homogenize()]) - True - """ - #TODO add test that checks that the output of the function is a polynomial - assert len(flist) > 0, 'you have to input least 1 polynomial in the list' - assert all([f.is_homogeneous() for f in flist]), 'resultant for non-homogeneous polynomials is not supported' - R = flist[0].parent() - dlist = [f.degree() for f in flist] - xlist = R.gens() - assert len(xlist) == len(dlist), 'number of polynomials(= %d) must equal number of variables (= %d)'%(len(dlist),len(xlist)) - n = len(dlist) - 1 - d = sum(dlist) - len(dlist) + 1 - one_list = [1 for i in xrange(0,len(dlist))] - mons = WeightedIntegerVectors(d, one_list) # list of exponent-vectors(/lists) of monomials of degree d - #mon_d = [prod([xlist[i]**(deg[i]) for i in xrange(0,len(deg))]) for deg in mons] - mons_num = len(mons) - mons_to_keep = [] - newflist = [] - result = [] - - for j in xrange(0,mons_num): - if not is_reduced(mons[j],dlist): - mons_to_keep.append(j) - si_mon = getS(mons[j], dlist) - # Monomial is in S_i under the partition, now we reduce the i'th degree of the monomial - new_mon = list(mons[j]) - new_mon[si_mon] -= dlist[si_mon] - quo = prod([xlist[k]**(new_mon[k]) for k in xrange(0,n+1)]) # this produces the actual reduced monomial - new_f = flist[si_mon]*quo - # we strip the coefficients of the new polynomial: - result.append([new_f[mon] for mon in mons]) - - numer_matrix = matrix(result) - denom_matrix = numer_matrix.matrix_from_rows_and_columns(mons_to_keep,mons_to_keep) - if denom_matrix.dimensions()[0] == 0: # here we choose the determinant of an empty matrix to be 1 - return numer_matrix.det() - denom_det = denom_matrix.det() - if denom_det != 0: - return numer_matrix.det()/denom_det - # if we get to this point, the determinant of the denominator was 0, and we get the resultant - # by taking the free coefficient of the quotient of two characteristic polynomials - poly_num = numer_matrix.characteristic_polynomial('T') - poly_denom = denom_matrix.characteristic_polynomial('T') - poly_quo = poly_num.quo_rem(poly_denom)[0] - return poly_quo(0) - - -def macaulay_general_resultant(dlist): - r""" - this is just a wrapper function of macaulay_resultant, where - it takes a list of degrees as input and returns the resultant of - a list of generic polynomials with coefficients in a polynomial ring. - - INPUT: - - - dlist -- a list of degrees - - OUTPUT: - - - the general resultant - - EXAMPLES:: - - sage: from sage.rings.polynomial.macaulay_resultant import macaulay_general_resultant - sage: macaulay_general_resultant([1,1,2]) - u2^2*u4^2*u6 - 2*u1*u2*u4*u5*u6 + u1^2*u5^2*u6 - u2^2*u3*u4*u7 + u1*u2*u3*u5*u7 + u0*u2*u4*u5*u7 - u0*u1*u5^2*u7 + u1*u2*u3*u4*u8 - u0*u2*u4^2*u8 - u1^2*u3*u5*u8 + u0*u1*u4*u5*u8 + u2^2*u3^2*u9 - 2*u0*u2*u3*u5*u9 + u0^2*u5^2*u9 - u1*u2*u3^2*u10 + u0*u2*u3*u4*u10 + u0*u1*u3*u5*u10 - u0^2*u4*u5*u10 + u1^2*u3^2*u11 - 2*u0*u1*u3*u4*u11 + u0^2*u4^2*u11 - """ - assert all([d >=0 for d in dlist]), 'degrees must be non-negative' - flist, U = construct_universal_polynomial(dlist) - return U(macaulay_resultant(flist)) From 2a15c39fe2d10a6eace8d5250291dabae008822c Mon Sep 17 00:00:00 2001 From: Solomon Vishkautsan Date: Tue, 8 Jul 2014 20:30:08 +0300 Subject: [PATCH 502/546] fixed failed doctest --- src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx index e9185dd7133..0c525679071 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx @@ -929,7 +929,6 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): EXAMPLES:: - sage: from sage.rings.polynomial.macaulay_resultant import is_reduced sage: R. = PolynomialRing(QQ,3) sage: R._macaulay_resultant_is_reduced([2,3,1],[2,3,3]) # the monomial x^2*y^3*z is not reduced w.r.t. degrees vector [2,3,3] False From 7e5467fe94099e8a75fdb2fe513dbf2be6feefda Mon Sep 17 00:00:00 2001 From: Solomon Vishkautsan Date: Tue, 8 Jul 2014 21:24:23 +0300 Subject: [PATCH 503/546] fixed another doctest in macaulay_resultant --- .../multi_polynomial_ring_generic.pyx | 30 +++++-------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx index 0c525679071..38ac7a52c73 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx @@ -919,9 +919,8 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): INPUT: - - mon -- a monomial in the variables listed in xlist + - mon_degs -- a monomial represented by a vector of degrees - dlist -- a list of degrees with respect to which we check reducedness - - xlist -- a list of variables in some Polynomial ring. OUTPUT: @@ -937,8 +936,6 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): sage: R._macaulay_resultant_is_reduced([1,3,2],[2,3,3]) # the monomial x*y^3*z^2 is not reduced w.r.t. degrees vector [2,3,3] True """ - #TODO fix comments to reflect change in input parameters - #RRR deg = [mon.degree(xi) for xi in xlist] diff = [mon_degs[i] - dlist[i] for i in xrange(0,len(dlist))] return len([1 for d in diff if d >= 0]) == 1 @@ -975,12 +972,9 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): d = sum(dlist) - len(dlist) + 1 flist = [] R = PolynomialRing(U,'x',n+1) - #TODO remove ugly prints - #print R ulist = U.gens() for d in dlist: xlist = R.gens() - #n = len(xlist) - 1 degs = IntegerVectors(d, n+1) mon_d = [prod([xlist[i]**(deg[i]) for i in xrange(0,len(deg))]) for deg in degs] @@ -1102,7 +1096,10 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): sage: R. = PolynomialRing(QQ,1) sage: f = x^2+1; g = x^5+1 - sage: f.resultant(g) == R.macaulay_resultant([f.homogenize(),g.homogenize()]) + sage: fh = f.homogenize() + sage: gh = g.homogenize() + sage: RH = fh.parent() + sage: f.resultant(g) == RH.macaulay_resultant([fh,gh]) True """ @@ -1113,23 +1110,17 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): #TODO add test that checks that the output of the function is a polynomial assert len(flist) > 0, 'you have to input least 1 polynomial in the list' assert all([f.is_homogeneous() for f in flist]), 'resultant for non-homogeneous polynomials is not supported' - R = flist[0].parent() dlist = [f.degree() for f in flist] - xlist = R.gens() + xlist = self.gens() assert len(xlist) == len(dlist), 'number of polynomials(= %d) must equal number of variables (= %d)'%(len(dlist),len(xlist)) n = len(dlist) - 1 d = sum(dlist) - len(dlist) + 1 - #one_list = [1 for i in xrange(0,len(dlist))] - one_list = [1] * len(dlist) - #mons = WeightedIntegerVectors(d, one_list) # list of exponent-vectors(/lists) of monomials of degree d mons = IntegerVectors(d, n+1).list() # list of exponent-vectors(/lists) of monomials of degree d - #mon_d = [prod([xlist[i]**(deg[i]) for i in xrange(0,len(deg))]) for deg in mons] mons_num = len(mons) mons_to_keep = [] newflist = [] flist=[[f.exponents(),f.coefficients()] for f in flist] # strip coefficients of the input polynomials - #result = [] - numer_matrix = zero_matrix(R.base_ring(),mons_num) + numer_matrix = zero_matrix(self.base_ring(),mons_num) for j in xrange(0,mons_num): if not self._macaulay_resultant_is_reduced(mons[j],dlist): @@ -1139,20 +1130,13 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): new_mon = list(mons[j]) new_mon[si_mon] -= dlist[si_mon] new_f = [[[g[k] + new_mon[k] for k in range(n+1)] for g in flist[si_mon][0]], flist[si_mon][1]] - #quo = prod([xlist[k]**(new_mon[k]) for k in xrange(0,n+1)]) # this produces the actual reduced monomial - #new_f = flist[si_mon]*quo - # we strip the coefficients of the new polynomial: - #result.append([new_f[mon] for mon in mons]) i=0 for mon in new_f[0]: k=mons.index(mon) numer_matrix[j,k]=new_f[1][i] i+=1 - - #numer_matrix = matrix(result) - denom_matrix = numer_matrix.matrix_from_rows_and_columns(mons_to_keep,mons_to_keep) if denom_matrix.dimensions()[0] == 0: # here we choose the determinant of an empty matrix to be 1 return numer_matrix.det() From fa898d1c2761a9b0cb180d890eb490034daad09b Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Wed, 9 Jul 2014 00:02:15 -0400 Subject: [PATCH 504/546] Better plotting of polyhedra * Reinstate the polyhedron.plot(projection_direction=...) option * Fall back to plotting the affine hull if necessary * Implement affine hull * Refactor plotting * Add plots for 1d, 0d polyhedra for completeness. --- src/sage/geometry/polyhedron/base.py | 180 +++++++++++++++-- src/sage/geometry/polyhedron/plot.py | 292 +++++++++++++++++++++------ 2 files changed, 401 insertions(+), 71 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 461395e88ea..5007d28443c 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -441,12 +441,19 @@ def _is_subpolyhedron(self, other): def plot(self, point=None, line=None, polygon=None, # None means unspecified by the user wireframe='blue', fill='green', + projection_direction=None, **kwds): """ Return a graphical representation. INPUT: + - ``projection_direction`` -- coordinate list/tuple/iterable + or ``None`` (default). The direction to use for the + :meth:`schlegel_projection`` of the polytope. If not + specified, no projection is used in dimensions `< 4` and + parallel projection is used in dimension `4`. + - ``point``, ``line``, ``polygon`` -- Parameters to pass to point (0d), line (1d), and polygon (2d) plot commands. Allowed values are: @@ -572,6 +579,46 @@ def plot(self, sage: for p in point.plot(wireframe=False, fill="red"): ... print p.options()['rgbcolor'], p red Point set defined by 1 point(s) + + The ``projection_direction`` option:: + + sage: line3d = Polyhedron([(-1,-1,-1), (1,1,1)]) + sage: print(line3d.plot(projection_direction=[2,3,4]).description()) + Line defined by 2 points: [(-0.00..., 0.126...), (0.131..., -1.93...)] + Point set defined by 2 point(s): [(-0.00..., 0.126...), (0.131..., -1.93...)] + + We try to draw the polytope in 2 or 3 dimensions:: + + sage: type(Polyhedron(ieqs=[(1,)]).plot()) + + sage: type(polytopes.n_cube(1).plot()) + + sage: type(polytopes.n_cube(2).plot()) + + sage: type(polytopes.n_cube(3).plot()) + + + In 4d a projection to 3d is used:: + + sage: type(polytopes.n_cube(4).plot()) + + sage: type(polytopes.n_cube(5).plot()) + Traceback (most recent call last): + ... + NotImplementedError: plotting of 5-dimensional polyhedra not implemented + + If the polyhedron is not full-dimensional, the :meth:`affine_hull` is used if necessary:: + + sage: type(Polyhedron([(0,), (1,)]).plot()) + + sage: type(Polyhedron([(0,0), (1,1)]).plot()) + + sage: type(Polyhedron([(0,0,0), (1,1,1)]).plot()) + + sage: type(Polyhedron([(0,0,0,0), (1,1,1,1)]).plot()) + + sage: type(Polyhedron([(0,0,0,0,0), (1,1,1,1,1)]).plot()) + """ def merge_options(*opts): merged = dict() @@ -593,14 +640,26 @@ def merge_options(*opts): opts = [merge_options(opt1, opt2, kwds) for opt1, opt2 in zip(opts, [point, line, polygon])] - from plot import render_2d, render_3d, render_4d - render_method = [ None, None, render_2d, render_3d, render_4d ] - if self.ambient_dim() < len(render_method): - render = render_method[self.ambient_dim()] - if render is not None: - return render(self, *opts) - raise NotImplementedError('Plotting of '+str(self.ambient_dim())+ - '-dimensional polyhedra not implemented') + def project(polyhedron): + if projection_direction is not None: + return polyhedron.schlegel_projection(projection_direction) + elif polyhedron.ambient_dim() == 4: + # There is no 4-d screen, we must project down to 3d + return polyhedron.schlegel_projection() + else: + return polyhedron.projection() + + projection = project(self) + try: + show_method = projection.show + except AttributeError: + projection = project(self.affine_hull()) + try: + show_method = projection.show + except AttributeError: + raise NotImplementedError('plotting of {0}-dimensional polyhedra not implemented' + .format(self.ambient_dim())) + return show_method(*opts) show = plot @@ -1619,6 +1678,8 @@ def dim(self): else: return self.ambient_dim() - self.n_equations() + dimension = dim + def is_empty(self): """ Test whether the polyhedron is the empty polyhedron @@ -3308,6 +3369,15 @@ def projection(self): """ Return a projection object. + See also + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.schlegel_projection` + for a more interesting projection. + + OUTPUT: + + The identity projection. This is useful for plotting + polyhedra. + EXAMPLES:: sage: p = polytopes.n_cube(3) @@ -3356,10 +3426,33 @@ def render_wireframe(self, **kwds): return proj.render_outline_2d(**kwds) raise ValueError("render_wireframe is only defined for 2 and 3 dimensional polyhedra.") - def schlegel_projection(self, projection_dir = None, height = 1.1): + def schlegel_projection(self, projection_dir=None, height=1.1): """ - Returns a projection object whose transformed coordinates are - a Schlegel projection of the polyhedron. + Return the Schlegel projection. + + * The polyhedron is translated such that its + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.center` + is at the origin. + + * The vertices are then normalized to the unit sphere + + * The normalized points are stereographically projected from a + point slightly outside of the sphere. + + INPUT: + + - ``projection_direction`` -- coordinate list/tuple/iterable + or ``None`` (default). The direction of the Schlegel + projection. For a full-dimensional polyhedron, the default + is the first facet normal; Otherwise, the vector consisting + of the first n primes is chosen. + + - ``height`` -- float (default: `1.1`). How far outside of the + unit sphere the focal point is. + + OUTPUT: + + A :class:`~sage.geometry.polyhedron.plot.Projection` object. EXAMPLES:: @@ -3377,7 +3470,7 @@ def schlegel_projection(self, projection_dir = None, height = 1.1): f0 = [ v.index() for v in facet.incident() ] projection_dir = [sum([vertices[f0[i]][j]/len(f0) for i in range(len(f0))]) for j in range(self.ambient_dim())] - return proj.schlegel(projection_direction = projection_dir, height = height) + return proj.schlegel(projection_direction=projection_dir, height=height) def _volume_lrs(self, verbose=False): """ @@ -4302,6 +4395,69 @@ def edge_label_noncompact(i,j,c_ij): self._restricted_automorphism_group = group return group + def is_full_dimensional(self): + """ + Return whether the polyhedron is full dimensional. + + OUTPUT: + Boolean. Whether the polyhedron is not contained in any strict + affine subspace. + EXAMPLES:: + + sage: polytopes.n_cube(3).is_full_dimensional() + True + sage: Polyhedron(vertices=[(1,2,3)], rays=[(1,0,0)]).is_full_dimensional() + False + """ + return self.dim() == self.ambient_dim() + + def affine_hull(self): + """ + Return the affine hull. + Each polyhedron is contained in some smallest affine subspace + (possibly the entire ambient space). The affine hull is the + same polyhedron but thought of as a full-dimensional + polyhedron in this subspace. + + OUTPUT: + + A full-dimensional polyhedron. + + EXAMPLES:: + + sage: triangle = Polyhedron([(1,0,0), (0,1,0), (0,0,1)]); triangle + A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 3 vertices + sage: triangle.affine_hull() + A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices + + sage: half3d = Polyhedron(vertices=[(3,2,1)], rays=[(1,0,0)]) + sage: half3d.affine_hull().Vrepresentation() + (A ray in the direction (1), A vertex at (3)) + + TESTS:: + + sage: Polyhedron([(2,3,4)]).affine_hull() + A 0-dimensional polyhedron in ZZ^0 defined as the convex hull of 1 vertex + """ + # translate one vertex to the origin + v0 = self.vertices()[0].vector() + gens = [] + for v in self.vertices()[1:]: + gens.append(v.vector() - v0) + for r in self.rays(): + gens.append(r.vector()) + for l in self.lines(): + gens.append(l.vector()) + + # Pick subset of coordinates to coordinatize the affine span + pivots = matrix(gens, base_ring=self.base_ring()).pivots() + def pivot(indexed): + return [indexed[i] for i in pivots] + + vertices = map(pivot, self.vertices()) + rays = map(pivot, self.rays()) + lines = map(pivot, self.lines()) + return Polyhedron(vertices=vertices, rays=rays, lines=lines, base_ring=self.base_ring()) diff --git a/src/sage/geometry/polyhedron/plot.py b/src/sage/geometry/polyhedron/plot.py index 07624500ba7..0452e20ba80 100644 --- a/src/sage/geometry/polyhedron/plot.py +++ b/src/sage/geometry/polyhedron/plot.py @@ -21,7 +21,7 @@ from sage.symbolic.constants import pi from sage.structure.sequence import Sequence -from sage.plot.all import point2d, line2d, arrow, polygon2d +from sage.plot.all import Graphics, point2d, line2d, arrow, polygon2d from sage.plot.plot3d.all import point3d, line3d, arrow3d, polygon3d from sage.plot.plot3d.transform import rotate_arbitrary @@ -30,7 +30,7 @@ ############################################################# -def render_2d(projection, point_opts={}, line_opts={}, polygon_opts={}): +def render_2d(projection, *args, **kwds): """ Return 2d rendering of the projection of a polyhedron into 2-dimensional ambient space. @@ -48,29 +48,20 @@ def render_2d(projection, point_opts={}, line_opts={}, polygon_opts={}): sage: q1.show() + q2.show() + q3.show() + q4.show() sage: from sage.geometry.polyhedron.plot import render_2d sage: q = render_2d(p1.projection()) + doctest:...: DeprecationWarning: use Projection.render_2d instead + See http://trac.sagemath.org/16625 for details. sage: q._objects [Point set defined by 1 point(s), Arrow from (1.0,1.0) to (2.0,2.0), Polygon defined by 3 points] """ + from sage.misc.superseded import deprecation + deprecation(16625, 'use Projection.render_2d instead') if is_Polyhedron(projection): projection = Projection(projection) - from sage.plot.graphics import Graphics - plt = Graphics() - if isinstance(point_opts, dict): - point_opts.setdefault('zorder', 2) - point_opts.setdefault('pointsize', 10) - plt += projection.render_points_2d(**point_opts) - if isinstance(line_opts, dict): - line_opts.setdefault('zorder', 1) - plt += projection.render_outline_2d(**line_opts) - if isinstance(polygon_opts, dict): - polygon_opts.setdefault('zorder', 0) - plt += projection.render_fill_2d(**polygon_opts) - return plt - - -def render_3d(projection, point_opts={}, line_opts={}, polygon_opts={}): + return projection.render_2d(*args, **kwds) + +def render_3d(projection, *args, **kwds): """ Return 3d rendering of a polyhedron projected into 3-dimensional ambient space. @@ -98,19 +89,11 @@ def render_3d(projection, point_opts={}, line_opts={}, polygon_opts={}): sage: Polyhedron(rays=[[0,1,0]]).show() # Half-line in R^3 sage: Polyhedron(vertices=[[1,1,1]]).show() # point in R^3 """ + from sage.misc.superseded import deprecation + deprecation(16625, 'use Projection.render_3d instead') if is_Polyhedron(projection): projection = Projection(projection) - from sage.plot.plot3d.base import Graphics3d - plt = Graphics3d() - if isinstance(point_opts, dict): - point_opts.setdefault('width', 3) - plt += projection.render_vertices_3d(**point_opts) - if isinstance(line_opts, dict): - line_opts.setdefault('width', 3) - plt += projection.render_wireframe_3d(**line_opts) - if isinstance(polygon_opts, dict): - plt += projection.render_solid_3d(**polygon_opts) - return plt + return projection.render_3d(*args, **kwds) def render_4d(polyhedron, point_opts={}, line_opts={}, polygon_opts={}, projection_direction=None): """ @@ -150,10 +133,16 @@ def render_4d(polyhedron, point_opts={}, line_opts={}, polygon_opts={}, projecti sage: from sage.geometry.polyhedron.plot import render_4d sage: p = polytopes.n_cube(4) sage: q = render_4d(p) + doctest:...: DeprecationWarning: use Polyhedron.schlegel_projection instead + See http://trac.sagemath.org/16625 for details. + doctest:...: DeprecationWarning: use Projection.render_3d instead + See http://trac.sagemath.org/16625 for details. sage: tach_str = q.tachyon() sage: tach_str.count('FCylinder') 32 """ + from sage.misc.superseded import deprecation + deprecation(16625, 'use Polyhedron.schlegel_projection instead') if projection_direction is None: for ineq in polyhedron.inequality_generator(): center = [v() for v in ineq.incident() if v.is_vertex()] @@ -379,7 +368,7 @@ class ProjectionFuncSchlegel(): sage: proj(vector([1.1,1.1,1.11]))[0] 0.0302... """ - def __init__(self, projection_direction, height = 1.1): + def __init__(self, projection_direction, height=1.1, center=0): """ Initializes the projection. @@ -392,6 +381,7 @@ def __init__(self, projection_direction, height = 1.1): 0.0302... sage: TestSuite(proj).run(skip='_test_pickling') """ + self.center = center self.projection_dir = vector(RDF, projection_direction) if norm(self.projection_dir).is_zero(): raise ValueError("projection direction must be a non-zero vector.") @@ -420,7 +410,7 @@ def __call__(self, x): sage: proj.__call__([1,2,3]) (0.56162854..., 2.09602626...) """ - v = vector(RDF,x) + v = vector(RDF,x) - self.center if v.is_zero(): raise ValueError("The origin must not be a vertex.") v = v/norm(v) # normalize vertices to unit sphere @@ -578,19 +568,29 @@ def stereographic(self, projection_point=None): return self.__call__(ProjectionFuncStereographic(projection_point)) - def schlegel(self, projection_direction=None, height = 1.1): + def schlegel(self, projection_direction=None, height=1.1): """ Return the Schlegel projection. - The vertices are normalized to the unit sphere, and - stereographically projected from a point slightly outside of - the sphere. + * The polyhedron is translated such that its + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.center` + is at the origin. + + * The vertices are then normalized to the unit sphere + + * The normalized points are stereographically projected from a + point slightly outside of the sphere. INPUT: - - ``projection_direction`` - The direction of the Schlegel - projection. By default, the vector consisting of the first n - primes is chosen. + - ``projection_direction`` -- coordinate list/tuple/iterable + or ``None`` (default). The direction of the Schlegel + projection. For a full-dimensional polyhedron, the default + is the first facet normal; Otherwise, the vector consisting + of the first n primes is chosen. + + - ``height`` -- float (default: `1.1`). How far outside of the + unit sphere the focal point is. EXAMPLES:: @@ -604,20 +604,16 @@ def schlegel(self, projection_direction=None, height = 1.1): sage: Projection(cube4).schlegel() The projection of a polyhedron into 3 dimensions - """ + center = self.parent_polyhedron.center() if projection_direction is None: - for poly in self.polygons: - center = sum([self.coords[i] for i in poly]) / len(poly) - print center, "\n" - if not center.is_zero(): - projection_direction = center - break - if projection_direction is None: - from sage.rings.arith import primes_first_n - projection_direction = primes_first_n(self.polyhedron_ambient_dim) - return self.__call__(ProjectionFuncSchlegel(projection_direction, height = height)) - + if self.parent_polyhedron.is_full_dimensional(): + projection_direction = self.parent_polyhedron.inequality_generator().next().A() + else: + from sage.rings.arith import primes_first_n + projection_direction = primes_first_n(self.polyhedron_ambient_dim) + return self.__call__(ProjectionFuncSchlegel( + projection_direction, height=height, center=center)) def coord_index_of(self, v): """ @@ -676,17 +672,21 @@ def _init_dimension(self): sage: from sage.geometry.polyhedron.plot import Projection, render_2d sage: p = polytopes.n_simplex(2).projection() sage: test = p._init_dimension() - sage: p.show.__doc__ == render_2d.__doc__ + sage: p.show.__doc__ == p.render_2d.__doc__ True """ - self.dimension = len(self.transformed_coords[0]) - - if self.dimension == 2: - self.show = lambda **kwds: render_2d(self,**kwds) - self.show.__doc__ = render_2d.__doc__ + if self.transformed_coords: + self.dimension = len(self.transformed_coords[0]) + else: + self.dimension = 0 + if self.dimension == 0: + self.show = self.render_0d + elif self.dimension == 1: + self.show = self.render_1d + elif self.dimension == 2: + self.show = self.render_2d elif self.dimension == 3: - self.show = lambda **kwds: render_3d(self,**kwds) - self.show.__doc__ = render_3d.__doc__ + self.show = self.render_3d else: try: del self.show @@ -931,6 +931,56 @@ def adjacent_vertices(i): self.polygons.extend( [self.coord_indices_of(p) for p in polygons] ) + def render_points_1d(self, **kwds): + """ + Return the points of a polyhedron in 1d. + + INPUT: + + - ``**kwds`` -- options passed through to + :func:`~sage.plot.point.point2d`. + + OUTPUT: + + A 2-d graphics object. + + EXAMPLES:: + + sage: cube1 = polytopes.n_cube(1) + sage: proj = cube1.projection() + sage: points = proj.render_points_1d() + sage: points._objects + [Point set defined by 2 point(s)] + """ + return point2d([c + [0] for c in self.coordinates_of(self.points)], **kwds) + + def render_line_1d(self, **kwds): + """ + Return the line of a polyhedron in 1d. + + INPUT: + + - ``**kwds`` -- options passed through to + :func:`~sage.plot.line.line2d`. + + OUTPUT: + + A 2-d graphics object. + + EXAMPLES:: + + sage: outline = polytopes.n_cube(1).projection().render_line_1d() + sage: outline._objects[0] + Line defined by 2 points + """ + if len(self.lines) == 0: + return Graphics() + elif len(self.lines) == 1: + line = self.coordinates_of(self.lines[0]) + return line2d([line[0] + [0], line[1] + [0]], **kwds) + else: + assert False # unreachable + def render_points_2d(self, **kwds): """ Return the points of a polyhedron in 2d. @@ -1021,7 +1071,6 @@ def render_wireframe_3d(self, **kwds): wireframe.append(arrow3d(a_coords[0], a_coords[1], **kwds)) return sum(wireframe) - def render_solid_3d(self, **kwds): """ Return solid 3d rendering of a 3d polytope. @@ -1036,6 +1085,131 @@ def render_solid_3d(self, **kwds): return sum([ polygon3d(self.coordinates_of(f), **kwds) for f in self.polygons ]) + def render_0d(self, point_opts={}, line_opts={}, polygon_opts={}): + """ + Return 0d rendering of the projection of a polyhedron into + 2-dimensional ambient space. + + INPUT: + + See + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.plot`. + + OUTPUT: + + A 2-d graphics object. + + EXAMPLES:: + + sage: print(Polyhedron([]).projection().render_0d().description()) + + sage: print(Polyhedron(ieqs=[(1,)]).projection().render_0d().description()) + Point set defined by 1 point(s): [(0.0, 0.0)] + """ + if isinstance(point_opts, dict): + point_opts.setdefault('zorder', 2) + point_opts.setdefault('pointsize', 10) + if self.points: + return point2d([0,0], **point_opts) + else: + return Graphics() + + + def render_1d(self, point_opts={}, line_opts={}, polygon_opts={}): + """ + Return 1d rendering of the projection of a polyhedron into + 2-dimensional ambient space. + + INPUT: + + See + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.plot`. + + OUTPUT: + + A 2-d graphics object. + + EXAMPLES:: + + sage: Polyhedron([(0,), (1,)]).projection().render_1d() + """ + plt = Graphics() + if isinstance(point_opts, dict): + point_opts.setdefault('zorder', 2) + point_opts.setdefault('pointsize', 10) + plt += self.render_points_1d(**point_opts) + if isinstance(line_opts, dict): + line_opts.setdefault('zorder', 1) + plt += self.render_line_1d(**line_opts) + return plt + + def render_2d(self, point_opts={}, line_opts={}, polygon_opts={}): + """ + Return 2d rendering of the projection of a polyhedron into + 2-dimensional ambient space. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[[1,1]], rays=[[1,1]]) + sage: q1 = p1.projection() + sage: p2 = Polyhedron(vertices=[[1,0], [0,1], [0,0]]) + sage: q2 = p2.projection() + sage: p3 = Polyhedron(vertices=[[1,2]]) + sage: q3 = p3.projection() + sage: p4 = Polyhedron(vertices=[[2,0]], rays=[[1,-1]], lines=[[1,1]]) + sage: q4 = p4.projection() + sage: q1.show() + q2.show() + q3.show() + q4.show() + """ + plt = Graphics() + if isinstance(point_opts, dict): + point_opts.setdefault('zorder', 2) + point_opts.setdefault('pointsize', 10) + plt += self.render_points_2d(**point_opts) + if isinstance(line_opts, dict): + line_opts.setdefault('zorder', 1) + plt += self.render_outline_2d(**line_opts) + if isinstance(polygon_opts, dict): + polygon_opts.setdefault('zorder', 0) + plt += self.render_fill_2d(**polygon_opts) + return plt + + def render_3d(self, point_opts={}, line_opts={}, polygon_opts={}): + """ + Return 3d rendering of a polyhedron projected into + 3-dimensional ambient space. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[[1,1,1]], rays=[[1,1,1]]) + sage: p2 = Polyhedron(vertices=[[2,0,0], [0,2,0], [0,0,2]]) + sage: p3 = Polyhedron(vertices=[[1,0,0], [0,1,0], [0,0,1]], rays=[[-1,-1,-1]]) + sage: p1.projection().show() + p2.projection().show() + p3.projection().show() + + It correctly handles various degenerate cases:: + + sage: Polyhedron(lines=[[1,0,0],[0,1,0],[0,0,1]]).show() # whole space + sage: Polyhedron(vertices=[[1,1,1]], rays=[[1,0,0]], + ....: lines=[[0,1,0],[0,0,1]]).show() # half space + sage: Polyhedron(vertices=[[1,1,1]], + ....: lines=[[0,1,0],[0,0,1]]).show() # R^2 in R^3 + sage: Polyhedron(rays=[[0,1,0],[0,0,1]], lines=[[1,0,0]]).show() # quadrant wedge in R^2 + sage: Polyhedron(rays=[[0,1,0]], lines=[[1,0,0]]).show() # upper half plane in R^3 + sage: Polyhedron(lines=[[1,0,0]]).show() # R^1 in R^2 + sage: Polyhedron(rays=[[0,1,0]]).show() # Half-line in R^3 + sage: Polyhedron(vertices=[[1,1,1]]).show() # point in R^3 + """ + from sage.plot.plot3d.base import Graphics3d + plt = Graphics3d() + if isinstance(point_opts, dict): + point_opts.setdefault('width', 3) + plt += self.render_vertices_3d(**point_opts) + if isinstance(line_opts, dict): + line_opts.setdefault('width', 3) + plt += self.render_wireframe_3d(**line_opts) + if isinstance(polygon_opts, dict): + plt += self.render_solid_3d(**polygon_opts) + return plt + def tikz(self, view=[0,0,1], angle=0, scale=2, edge_color='blue!95!black', facet_color='blue!95!black', opacity=0.8, vertex_color='green', axis=False): From 484ae916381c9dca15192c8d9209646543c73980 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 8 Jul 2014 22:48:29 -0700 Subject: [PATCH 505/546] Fixed _indefinite_factorization() for immutable matrix over fields. --- src/sage/matrix/matrix2.pyx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 0c064f9e76f..4ad9762c357 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -11423,6 +11423,20 @@ cdef class Matrix(matrix1.Matrix): sage: isinstance(ds, tuple), isinstance(dh, tuple) (True, True) + We check that :trac:`16633` is fixed:: + + sage: A = matrix(QQ, [[ 4, -2, 4, 2], + ....: [-2, 10, -2, -7], + ....: [ 4, -2, 8, 4], + ....: [ 2, -7, 4, 7]]) + sage: A.set_immutable() + sage: L,d = A._indefinite_factorization('symmetric') + sage: A + [ 4 -2 4 2] + [-2 10 -2 -7] + [ 4 -2 8 4] + [ 2 -7 4 7] + AUTHOR: - Rob Beezer (2012-05-24) @@ -11470,6 +11484,10 @@ cdef class Matrix(matrix1.Matrix): # we need a copy no matter what, so we # (potentially) change to fraction field at the same time L = self.change_ring(F) + # The change ring doesn't necessarily return a copy if ``self`` + # is immutable and ``F`` is the same as the base ring + if L is self: + L = self.__copy__() m = L._nrows zero = F(0) one = F(1) From cd7ba1a82334e5777ef61acae66dd20f5b19066e Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Wed, 9 Jul 2014 10:54:59 +0200 Subject: [PATCH 506/546] trac #16622: More doc fixes --- .../combinat/designs/incidence_structures.py | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index bd2889ad525..8c1bc80089c 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -1,5 +1,5 @@ """ -Incidence structures. +Incidence structures (i.e. hypergraphs, i.e. set systems) An incidence structure is specified by a list of points, blocks, and an incidence matrix ([1]_, [2]_). @@ -20,6 +20,9 @@ (version 0.6) written by Peter Dobcsanyi peter@designtheory.org. - Vincent Delecroix (2014): major rewrite + +Methods +------- """ #*************************************************************************** # Copyright (C) 2007 # @@ -71,17 +74,33 @@ def IncidenceStructureFromMatrix(M, name=None): class IncidenceStructure(object): r""" - A base class for incidence structure (or block design) with explicit ground - set and blocks. + A base class for incidence structures (i.e. hypergraphs, i.e. set systems) + + An incidence structure (i.e. hypergraph, i.e. set system) can be defined + from a collection of blocks (i.e. sets, i.e. edges), optionally with an + explicit ground set (i.e. point set, i.e. vertex set). Alternatively they + can be defined from a binary incidence matrix. INPUT: - - ``points`` -- the underlying set. If ``points`` is an integer `v`, then - the set is considered to be `\{0, ..., v-1\}`. + - ``points`` -- (i.e. ground set, i.e. vertex set) the underlying set. If + ``points`` is an integer `v`, then the set is considered to be `\{0, ..., + v-1\}`. + + .. NOTE:: + + The following syntax, where ``points`` is ommitted, automatically + defines the ground set as the union of the blocks:: + + sage: H = IncidenceStructure([['a','b','c'],['c','d','e']]) + sage: H.ground_set() + ['a', 'b', 'c', 'd', 'e'] - - ``blocks`` -- the blocks (might be any iterable) + - ``blocks`` -- (i.e. edges, i.e. sets) the blocks defining the incidence + structure. Can be any iterable. - - ``incidence_matrix`` -- the incidence matrix + - ``incidence_matrix`` -- a binary incidence matrix. Each column represents + a set. - ``name`` (a string, such as "Fano plane"). @@ -189,16 +208,15 @@ def __init__(self, points=None, blocks=None, incidence_matrix=None, # Reformatting input if isinstance(points, Matrix): - assert incidence_matrix is None - assert blocks is None + assert incidence_matrix is None, "'incidence_matrix' cannot be defined when 'points' is a matrix" + assert blocks is None, "'blocks' cannot be defined when 'points' is a matrix" incidence_matrix = points points = blocks = None elif points and blocks is None: blocks = points points = set().union(*blocks) - assert incidence_matrix is None if points: - assert incidence_matrix is None + assert incidence_matrix is None, "'incidence_matrix' cannot be defined when 'points' is defined" if incidence_matrix: M = matrix(incidence_matrix) @@ -888,7 +906,7 @@ def parameters(self): def block_design_checker(self, t, v, k, lmbda, type=None): """ This method is deprecated and will soon be removed (see :trac:`16553`). - You could use :meth:`is_t_design` or :meth:`t_design_parameters` instead. + You could use :meth:`is_t_design` instead. This is *not* a wrapper for GAP Design's IsBlockDesign. The GAP Design function IsBlockDesign From 84833c9ad72727913a0cec5ca1e9f4f9d9e16d73 Mon Sep 17 00:00:00 2001 From: Bruno Grenet Date: Wed, 9 Jul 2014 13:04:42 +0200 Subject: [PATCH 507/546] Move inversion of unit --- src/sage/rings/polynomial/polynomial_element_generic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index 92a2cdb5f24..825dc867b2d 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -599,10 +599,10 @@ def quo_rem(self, other): quo = R.zero_element() rem = self - inv_lc = R.base_ring().one_element()/other.leading_coefficient() + #inv_lc = R.base_ring().one_element()/other.leading_coefficient() while rem.degree() > d: - c = inv_lc*rem.leading_coefficient() + c = rem.leading_coefficient()/other.leading_coefficient() e = rem.degree() - d quo += c*R.one_element().shift(e) rem -= c*other.shift(e) From 7b23abd31996936f56afbbf94b3b87a4ddbde9a3 Mon Sep 17 00:00:00 2001 From: Solomon Vishkautsan Date: Wed, 9 Jul 2014 16:07:33 +0300 Subject: [PATCH 508/546] added macaulay resultant to multivariate polynomial class --- .../rings/polynomial/multi_polynomial.pyx | 136 ++++++++++++++++++ .../multi_polynomial_ring_generic.pyx | 40 ++++-- 2 files changed, 168 insertions(+), 8 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 49431a7d780..6571b8e7497 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -1212,6 +1212,142 @@ cdef class MPolynomial(CommutativeRingElement): return M + def macaulay_resultant(self, *args): + r""" + This is an implementation of the Macaulay Resultant. It computes + the resultant of universal polynomials as well as polynomials + with constant coefficients. This is a project done in + sage days 55. It's based on the implementation in Maple by + Manfred Minimair, which in turn is based on the following: + + -Using Algebraic Geometry by Cox, Little, O'Shea + -Canny, J., "Generalised characteristic polynomials", p.241--250, + J. Symbolic Comput. Vol. 9, No. 3, 1990 + -The algebraic theory of modular systems by Macaulay + + It calculates the Macaulay resultant for a list of Polynomials, + up to sign! + + AUTHORS: + + - Hao Chen + - Solomon Vishkautsan + - Ben Hutz + + INPUT: + + - args -- a list of n homogeneous polynomials in n variables. + works when args[0] is the list of polynomials, + or args is itself the list of polynomials + + OUTPUT: + + - the macaulay resultant + + EXAMPLES: + + The number of polynomials has to match the number of variables:: + + sage: R. = PolynomialRing(QQ,3) + sage: y.macaulay_resultant(x+z) + Traceback (most recent call last): + ... + AssertionError: number of polynomials(= 2) must equal number of variables (= 3) + + The polynomials need to be all homogeneous:: + + sage: R. = PolynomialRing(QQ,3) + sage: y.macaulay_resultant([x+z, z+x^3]) + Traceback (most recent call last): + ... + AssertionError: resultant for non-homogeneous polynomials is not supported + + All polynomials must be in the same ring:: + + sage: R. = PolynomialRing(QQ,3) + sage: S. = PolynomialRing(QQ, 2) + sage: y.macaulay_resultant(z+x,z) + Traceback (most recent call last): + ... + AssertionError: not all inputs are polynomials in the calling ring + + The following example recreates Proposition 2.10 in Ch.3 of Using Algebraic Geometry:: + + sage: K. = PolynomialRing(ZZ, 2) + sage: flist,R = K._macaulay_resultant_universal_polynomials([1,1,2]) + sage: flist[0].macaulay_resultant(flist[1:]) + u2^2*u4^2*u6 - 2*u1*u2*u4*u5*u6 + u1^2*u5^2*u6 - u2^2*u3*u4*u7 + u1*u2*u3*u5*u7 + u0*u2*u4*u5*u7 - u0*u1*u5^2*u7 + u1*u2*u3*u4*u8 - u0*u2*u4^2*u8 - u1^2*u3*u5*u8 + u0*u1*u4*u5*u8 + u2^2*u3^2*u9 - 2*u0*u2*u3*u5*u9 + u0^2*u5^2*u9 - u1*u2*u3^2*u10 + u0*u2*u3*u4*u10 + u0*u1*u3*u5*u10 - u0^2*u4*u5*u10 + u1^2*u3^2*u11 - 2*u0*u1*u3*u4*u11 + u0^2*u4^2*u11 + + The following example degenerates into the determinant of a 3*3 matrix:: + + sage: K. = PolynomialRing(ZZ, 2) + sage: flist,R = K._macaulay_resultant_universal_polynomials([1,1,1]) + sage: flist[0].macaulay_resultant(flist[1:]) + -u2*u4*u6 + u1*u5*u6 + u2*u3*u7 - u0*u5*u7 - u1*u3*u8 + u0*u4*u8 + + The following example is by Patrick Ingram(arxiv:1310.4114):: + + sage: U = PolynomialRing(ZZ,'y',2); y0,y1 = U.gens() + sage: R = PolynomialRing(U,'x',3); x0,x1,x2 = R.gens() + sage: f0 = y0*x2^2 - x0^2 + 2*x1*x2 + sage: f1 = y1*x2^2 - x1^2 + 2*x0*x2 + sage: f2 = x0*x1 - x2^2 + sage: f0.macaulay_resultant(f1,f2) + y0^2*y1^2 - 4*y0^3 - 4*y1^3 + 18*y0*y1 - 27 + + a simple example with constant rational coefficients:: + + sage: R. = PolynomialRing(QQ,4) + sage: w.macaulay_resultant([z,y,x]) + 1 + + an example where the resultant vanishes:: + + sage: R. = PolynomialRing(QQ,3) + sage: (x+y).macaulay_resultant([y^2,x]) + 0 + + an example of bad reduction at a prime p = 5:: + + sage: R. = PolynomialRing(QQ,3) + sage: y.macaulay_resultant([x^3+25*y^2*x,5*z]) + 125 + + The input can given as an unpacked list of polynomials:: + + sage: R. = PolynomialRing(QQ,3) + sage: y.macaulay_resultant(x^3+25*y^2*x,5*z) + 125 + + an example when the coefficients live in a finite field:: + + sage: F = FiniteField(11) + sage: R. = PolynomialRing(F,4) + sage: z.macaulay_resultant([x^3,5*y,w]) + 4 + + example when the denominator in the algorithm vanishes(in this case + the resultant is the constant term of the quotient of + char polynomials of numerator/denominator):: + + sage: R. = PolynomialRing(QQ,3) + sage: y.macaulay_resultant([x+z, z^2]) + -1 + + when there are only 2 polynomials, macaulay resultant degenerates to the traditional resultant:: + + sage: R. = PolynomialRing(QQ,1) + sage: f = x^2+1; g = x^5+1 + sage: fh = f.homogenize() + sage: gh = g.homogenize() + sage: RH = fh.parent() + sage: f.resultant(g) == fh.macaulay_resultant(gh) + True + + """ + if len(args) == 1 and isinstance(args[0],list): + return self.parent().macaulay_resultant(self, *args[0]) + return self.parent().macaulay_resultant(self, *args) def denominator(self): """ diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx index 38ac7a52c73..ec9f6ee8a5b 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx @@ -984,7 +984,7 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): ulist = ulist[len(mon_d):] return flist, R - def macaulay_resultant(self, flist): + def macaulay_resultant(self, *args): r""" This is an implementation of the Macaulay Resultant. It computes the resultant of universal polynomials as well as polynomials @@ -1008,11 +1008,13 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): INPUT: - - flist -- a list of n homogeneous polynomials in n variables + - args -- a list of n homogeneous polynomials in n variables. + works when args[0] is the list of polynomials, + or args is itself the list of polynomials OUTPUT: - - the resultant + - the macaulay resultant EXAMPLES: @@ -1033,6 +1035,15 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): ... AssertionError: resultant for non-homogeneous polynomials is not supported + All polynomials must be in the same ring:: + + sage: S. = PolynomialRing(QQ, 2) + sage: R. = PolynomialRing(QQ,3) + sage: S.macaulay_resultant([y, z+x]) + Traceback (most recent call last): + ... + AssertionError: not all inputs are polynomials in the calling ring + The following example recreates Proposition 2.10 in Ch.3 of Using Algebraic Geometry:: sage: K. = PolynomialRing(ZZ, 2) @@ -1076,6 +1087,12 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): sage: R.macaulay_resultant([y,x^3+25*y^2*x,5*z]) 125 + The input can given as an unpacked list of polynomials:: + + sage: R. = PolynomialRing(QQ,3) + sage: R.macaulay_resultant(y,x^3+25*y^2*x,5*z) + 125 + an example when the coefficients live in a finite field:: sage: F = FiniteField(11) @@ -1083,7 +1100,6 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): sage: R.macaulay_resultant([z,x^3,5*y,w]) 4 - example when the denominator in the algorithm vanishes(in this case the resultant is the constant term of the quotient of char polynomials of numerator/denominator):: @@ -1101,15 +1117,23 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): sage: RH = fh.parent() sage: f.resultant(g) == RH.macaulay_resultant([fh,gh]) True + """ from sage.matrix.constructor import matrix from sage.matrix.constructor import zero_matrix from sage.combinat.integer_vector import IntegerVectors - #TODO add test that checks that the output of the function is a polynomial + if len(args) == 1 and isinstance(args[0],list): + flist = args[0] + else: + flist = args + assert len(flist) > 0, 'you have to input least 1 polynomial in the list' assert all([f.is_homogeneous() for f in flist]), 'resultant for non-homogeneous polynomials is not supported' + assert all([self.is_parent_of(f) for f in flist]), 'not all inputs are polynomials in the calling ring' + + U = self.base_ring() # ring of coefficients of self dlist = [f.degree() for f in flist] xlist = self.gens() assert len(xlist) == len(dlist), 'number of polynomials(= %d) must equal number of variables (= %d)'%(len(dlist),len(xlist)) @@ -1139,16 +1163,16 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): denom_matrix = numer_matrix.matrix_from_rows_and_columns(mons_to_keep,mons_to_keep) if denom_matrix.dimensions()[0] == 0: # here we choose the determinant of an empty matrix to be 1 - return numer_matrix.det() + return U(numer_matrix.det()) denom_det = denom_matrix.det() if denom_det != 0: - return numer_matrix.det()/denom_det + return U(numer_matrix.det()/denom_det) # if we get to this point, the determinant of the denominator was 0, and we get the resultant # by taking the free coefficient of the quotient of two characteristic polynomials poly_num = numer_matrix.characteristic_polynomial('T') poly_denom = denom_matrix.characteristic_polynomial('T') poly_quo = poly_num.quo_rem(poly_denom)[0] - return poly_quo(0) + return U(poly_quo(0)) #################### # Leave *all* old versions! From 8a213a725a158e44ffb12bb1dffd5b6bf43f6514 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Wed, 9 Jul 2014 11:50:25 -0400 Subject: [PATCH 509/546] Make projection objects display as graphics They are only used in the graphical display, so let them display as graphics by default if possible. Also, rename Projection.show->plot for consistency (show = plot + run viewer). --- src/sage/geometry/polyhedron/base.py | 6 +++--- src/sage/geometry/polyhedron/plot.py | 31 +++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 5007d28443c..6aa34303055 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -651,15 +651,15 @@ def project(polyhedron): projection = project(self) try: - show_method = projection.show + plot_method = projection.plot except AttributeError: projection = project(self.affine_hull()) try: - show_method = projection.show + plot_method = projection.plot except AttributeError: raise NotImplementedError('plotting of {0}-dimensional polyhedra not implemented' .format(self.ambient_dim())) - return show_method(*opts) + return plot_method(*opts) show = plot diff --git a/src/sage/geometry/polyhedron/plot.py b/src/sage/geometry/polyhedron/plot.py index 0452e20ba80..d4aa0364da9 100644 --- a/src/sage/geometry/polyhedron/plot.py +++ b/src/sage/geometry/polyhedron/plot.py @@ -680,19 +680,40 @@ def _init_dimension(self): else: self.dimension = 0 if self.dimension == 0: - self.show = self.render_0d + self.plot = self.render_0d elif self.dimension == 1: - self.show = self.render_1d + self.plot = self.render_1d elif self.dimension == 2: - self.show = self.render_2d + self.plot = self.render_2d elif self.dimension == 3: - self.show = self.render_3d + self.plot = self.render_3d else: try: - del self.show + del self.plot except AttributeError: pass + def show(self, *args, **kwds): + from sage.misc.superseded import deprecation + deprecation(16625, 'use Projection.plot instead') + return self.plot(*args, **kwds) + + def _graphics_(self): + """ + Display projection graphically on the Sage command line. + + See :meth:`~sage.plot.graphics.Graphics._graphics_`. + + EXAMPLES:: + + sage: polytopes.n_cube(3).projection()._graphics_() + True + """ + try: + self.plot().show() + return True + except AttributeError: + return False def _init_from_2d(self, polyhedron): """ From cf41e5b4608202dfe4fca529322f73c64738d0b5 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Wed, 9 Jul 2014 12:15:00 -0400 Subject: [PATCH 510/546] Fix deprecations and doctests --- src/sage/geometry/polyhedron/plot.py | 61 +++++++++++++++------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/src/sage/geometry/polyhedron/plot.py b/src/sage/geometry/polyhedron/plot.py index d4aa0364da9..b19aef483ec 100644 --- a/src/sage/geometry/polyhedron/plot.py +++ b/src/sage/geometry/polyhedron/plot.py @@ -45,7 +45,7 @@ def render_2d(projection, *args, **kwds): sage: q3 = p3.projection() sage: p4 = Polyhedron(vertices=[[2,0]], rays=[[1,-1]], lines=[[1,1]]) sage: q4 = p4.projection() - sage: q1.show() + q2.show() + q3.show() + q4.show() + sage: q1.plot() + q2.plot() + q3.plot() + q4.plot() sage: from sage.geometry.polyhedron.plot import render_2d sage: q = render_2d(p1.projection()) doctest:...: DeprecationWarning: use Projection.render_2d instead @@ -76,18 +76,18 @@ def render_3d(projection, *args, **kwds): sage: p1 = Polyhedron(vertices=[[1,1,1]], rays=[[1,1,1]]) sage: p2 = Polyhedron(vertices=[[2,0,0], [0,2,0], [0,0,2]]) sage: p3 = Polyhedron(vertices=[[1,0,0], [0,1,0], [0,0,1]], rays=[[-1,-1,-1]]) - sage: p1.projection().show() + p2.projection().show() + p3.projection().show() + sage: p1.projection().plot() + p2.projection().plot() + p3.projection().plot() It correctly handles various degenerate cases:: - sage: Polyhedron(lines=[[1,0,0],[0,1,0],[0,0,1]]).show() # whole space - sage: Polyhedron(vertices=[[1,1,1]], rays=[[1,0,0]], lines=[[0,1,0],[0,0,1]]).show() # half space - sage: Polyhedron(vertices=[[1,1,1]], lines=[[0,1,0],[0,0,1]]).show() # R^2 in R^3 - sage: Polyhedron(rays=[[0,1,0],[0,0,1]], lines=[[1,0,0]]).show() # quadrant wedge in R^2 - sage: Polyhedron(rays=[[0,1,0]], lines=[[1,0,0]]).show() # upper half plane in R^3 - sage: Polyhedron(lines=[[1,0,0]]).show() # R^1 in R^2 - sage: Polyhedron(rays=[[0,1,0]]).show() # Half-line in R^3 - sage: Polyhedron(vertices=[[1,1,1]]).show() # point in R^3 + sage: Polyhedron(lines=[[1,0,0],[0,1,0],[0,0,1]]).plot() # whole space + sage: Polyhedron(vertices=[[1,1,1]], rays=[[1,0,0]], lines=[[0,1,0],[0,0,1]]).plot() # half space + sage: Polyhedron(vertices=[[1,1,1]], lines=[[0,1,0],[0,0,1]]).plot() # R^2 in R^3 + sage: Polyhedron(rays=[[0,1,0],[0,0,1]], lines=[[1,0,0]]).plot() # quadrant wedge in R^2 + sage: Polyhedron(rays=[[0,1,0]], lines=[[1,0,0]]).plot() # upper half plane in R^3 + sage: Polyhedron(lines=[[1,0,0]]).plot() # R^1 in R^2 + sage: Polyhedron(rays=[[0,1,0]]).plot() # Half-line in R^3 + sage: Polyhedron(vertices=[[1,1,1]]).plot() # point in R^3 """ from sage.misc.superseded import deprecation deprecation(16625, 'use Projection.render_3d instead') @@ -123,9 +123,9 @@ def render_4d(polyhedron, point_opts={}, line_opts={}, polygon_opts={}, projecti sage: poly = polytopes.twenty_four_cell() sage: poly A 4-dimensional polyhedron in QQ^4 defined as the convex hull of 24 vertices - sage: poly.show() - sage: poly.show(projection_direction=[2,5,11,17]) - sage: type( poly.show() ) + sage: poly.plot() + sage: poly.plot(projection_direction=[2,5,11,17]) + sage: type( poly.plot() ) TESTS:: @@ -458,11 +458,11 @@ def __init__(self, polyhedron, proj=projection_func_identity): The projection of a polyhedron into 2 dimensions sage: Projection(p, lambda x: [x[1],x[2]] ) # another way of doing the same projection The projection of a polyhedron into 2 dimensions - sage: _.show() # plot of the projected icosahedron in 2d + sage: _.plot() # plot of the projected icosahedron in 2d sage: proj = Projection(p) sage: proj.stereographic([1,2,3]) The projection of a polyhedron into 2 dimensions - sage: proj.show() + sage: proj.plot() sage: TestSuite(proj).run(skip='_test_pickling') """ self.parent_polyhedron = polyhedron @@ -559,7 +559,7 @@ def stereographic(self, projection_point=None): sage: proj = Projection(polytopes.buckyball()) #long time sage: proj #long time The projection of a polyhedron into 3 dimensions - sage: proj.stereographic([5,2,3]).show() #long time + sage: proj.stereographic([5,2,3]).plot() #long time sage: Projection( polytopes.twenty_four_cell() ).stereographic([2,0,0,0]) The projection of a polyhedron into 3 dimensions """ @@ -598,7 +598,7 @@ def schlegel(self, projection_direction=None, height=1.1): sage: from sage.geometry.polyhedron.plot import Projection sage: Projection(cube4).schlegel([1,0,0,0]) The projection of a polyhedron into 3 dimensions - sage: _.show() + sage: _.plot() TESTS:: @@ -672,7 +672,7 @@ def _init_dimension(self): sage: from sage.geometry.polyhedron.plot import Projection, render_2d sage: p = polytopes.n_simplex(2).projection() sage: test = p._init_dimension() - sage: p.show.__doc__ == p.render_2d.__doc__ + sage: p.plot.__doc__ == p.render_2d.__doc__ True """ if self.transformed_coords: @@ -707,8 +707,11 @@ def _graphics_(self): EXAMPLES:: sage: polytopes.n_cube(3).projection()._graphics_() - True + False """ + from sage.doctest import DOCTEST_MODE + if DOCTEST_MODE: + return False try: self.plot().show() return True @@ -1179,7 +1182,7 @@ def render_2d(self, point_opts={}, line_opts={}, polygon_opts={}): sage: q3 = p3.projection() sage: p4 = Polyhedron(vertices=[[2,0]], rays=[[1,-1]], lines=[[1,1]]) sage: q4 = p4.projection() - sage: q1.show() + q2.show() + q3.show() + q4.show() + sage: q1.plot() + q2.plot() + q3.plot() + q4.plot() """ plt = Graphics() if isinstance(point_opts, dict): @@ -1204,20 +1207,20 @@ def render_3d(self, point_opts={}, line_opts={}, polygon_opts={}): sage: p1 = Polyhedron(vertices=[[1,1,1]], rays=[[1,1,1]]) sage: p2 = Polyhedron(vertices=[[2,0,0], [0,2,0], [0,0,2]]) sage: p3 = Polyhedron(vertices=[[1,0,0], [0,1,0], [0,0,1]], rays=[[-1,-1,-1]]) - sage: p1.projection().show() + p2.projection().show() + p3.projection().show() + sage: p1.projection().plot() + p2.projection().plot() + p3.projection().plot() It correctly handles various degenerate cases:: - sage: Polyhedron(lines=[[1,0,0],[0,1,0],[0,0,1]]).show() # whole space + sage: Polyhedron(lines=[[1,0,0],[0,1,0],[0,0,1]]).plot() # whole space sage: Polyhedron(vertices=[[1,1,1]], rays=[[1,0,0]], - ....: lines=[[0,1,0],[0,0,1]]).show() # half space + ....: lines=[[0,1,0],[0,0,1]]).plot() # half space sage: Polyhedron(vertices=[[1,1,1]], - ....: lines=[[0,1,0],[0,0,1]]).show() # R^2 in R^3 - sage: Polyhedron(rays=[[0,1,0],[0,0,1]], lines=[[1,0,0]]).show() # quadrant wedge in R^2 - sage: Polyhedron(rays=[[0,1,0]], lines=[[1,0,0]]).show() # upper half plane in R^3 - sage: Polyhedron(lines=[[1,0,0]]).show() # R^1 in R^2 - sage: Polyhedron(rays=[[0,1,0]]).show() # Half-line in R^3 - sage: Polyhedron(vertices=[[1,1,1]]).show() # point in R^3 + ....: lines=[[0,1,0],[0,0,1]]).plot() # R^2 in R^3 + sage: Polyhedron(rays=[[0,1,0],[0,0,1]], lines=[[1,0,0]]).plot() # quadrant wedge in R^2 + sage: Polyhedron(rays=[[0,1,0]], lines=[[1,0,0]]).plot() # upper half plane in R^3 + sage: Polyhedron(lines=[[1,0,0]]).plot() # R^1 in R^2 + sage: Polyhedron(rays=[[0,1,0]]).plot() # Half-line in R^3 + sage: Polyhedron(vertices=[[1,1,1]]).plot() # point in R^3 """ from sage.plot.plot3d.base import Graphics3d plt = Graphics3d() From 8baa0984e422ce04713bda0f5be32d2bdf7d12d6 Mon Sep 17 00:00:00 2001 From: Solomon Vishkautsan Date: Wed, 9 Jul 2014 21:41:53 +0300 Subject: [PATCH 511/546] improved docs and general cleanup --- .../rings/polynomial/multi_polynomial.pyx | 27 +++++---- .../multi_polynomial_ring_generic.pyx | 55 ++++++++++--------- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 6571b8e7497..f5b2e2d04c5 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -1218,16 +1218,21 @@ cdef class MPolynomial(CommutativeRingElement): the resultant of universal polynomials as well as polynomials with constant coefficients. This is a project done in sage days 55. It's based on the implementation in Maple by - Manfred Minimair, which in turn is based on the following: - - -Using Algebraic Geometry by Cox, Little, O'Shea - -Canny, J., "Generalised characteristic polynomials", p.241--250, - J. Symbolic Comput. Vol. 9, No. 3, 1990 - -The algebraic theory of modular systems by Macaulay - + Manfred Minimair, which in turn is based on the references listed below: It calculates the Macaulay resultant for a list of Polynomials, up to sign! + REFERENCES: + + .. [CLO] D. Cox, J. Little, D. O'Shea. Using Algebraic Geometry. + Springer, 2005. + + .. [Can] J. Canny. Generalised characteristic polynomials. + J. Symbolic Comput. Vol. 9, No. 3, 1990, 241--250. + + .. [Mac] F.S. Macaulay. The algebraic theory of modular systems + Cambridge university press, 1916. + AUTHORS: - Hao Chen @@ -1236,8 +1241,8 @@ cdef class MPolynomial(CommutativeRingElement): INPUT: - - args -- a list of n homogeneous polynomials in n variables. - works when args[0] is the list of polynomials, + - ``args`` -- a list of n homogeneous polynomials in n variables. + works when ``args[0]`` is the list of polynomials, or args is itself the list of polynomials OUTPUT: @@ -1278,7 +1283,7 @@ cdef class MPolynomial(CommutativeRingElement): sage: flist[0].macaulay_resultant(flist[1:]) u2^2*u4^2*u6 - 2*u1*u2*u4*u5*u6 + u1^2*u5^2*u6 - u2^2*u3*u4*u7 + u1*u2*u3*u5*u7 + u0*u2*u4*u5*u7 - u0*u1*u5^2*u7 + u1*u2*u3*u4*u8 - u0*u2*u4^2*u8 - u1^2*u3*u5*u8 + u0*u1*u4*u5*u8 + u2^2*u3^2*u9 - 2*u0*u2*u3*u5*u9 + u0^2*u5^2*u9 - u1*u2*u3^2*u10 + u0*u2*u3*u4*u10 + u0*u1*u3*u5*u10 - u0^2*u4*u5*u10 + u1^2*u3^2*u11 - 2*u0*u1*u3*u4*u11 + u0^2*u4^2*u11 - The following example degenerates into the determinant of a 3*3 matrix:: + The following example degenerates into the determinant of a `3*3` matrix:: sage: K. = PolynomialRing(ZZ, 2) sage: flist,R = K._macaulay_resultant_universal_polynomials([1,1,1]) @@ -1307,7 +1312,7 @@ cdef class MPolynomial(CommutativeRingElement): sage: (x+y).macaulay_resultant([y^2,x]) 0 - an example of bad reduction at a prime p = 5:: + an example of bad reduction at a prime ``p = 5``:: sage: R. = PolynomialRing(QQ,3) sage: y.macaulay_resultant([x^3+25*y^2*x,5*z]) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx index ec9f6ee8a5b..89cf1e29ce7 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx @@ -882,18 +882,18 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): def _macaulay_resultant_getS(self,mon_deg_tuple,dlist): r""" - In the Macaulay resultant algorithm the list of all monomials of the total degree is partitioned into sets S_i. - This function returns the index i for the set S_i for the inputted given monomial. + In the Macaulay resultant algorithm the list of all monomials of the total degree is partitioned into sets `S_i`. + This function returns the index i for the set `S_i` for the inputted given monomial. INPUT: - - `mon_deg_tuple` -- a list representing a monomial of a degree d - - `dlist` -- a list of degrees d_i of the polynomials in question, where - d = sum(dlist) - len(dlist) + 1 + - ``mon_deg_tuple`` -- a list representing a monomial of a degree d + - ``dlist`` -- a list of degrees `d_i` of the polynomials in question, where + ``d = sum(dlist) - len(dlist) + 1`` OUTPUT: - - the index i such that the input monomial lives in S_i + - the index `i` such that the input monomial lives in `S_i` EXAMPLES:: @@ -914,13 +914,13 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): def _macaulay_resultant_is_reduced(self,mon_degs,dlist): r""" Helper function for the Macaulay resultant algorithm. - A monomial in the variables x_0,...,x_n is called reduced with respect to the list of degrees d_0,...,d_n - if the degree of x_i in the monomial is >= d_i for exactly one i. This function checks this property for an inputted monomial. + A monomial in the variables `x_0,...,x_n` is called reduced with respect to the list of degrees `d_0,...,d_n` + if the degree of `x_i` in the monomial is `>= d_i` for exactly one `i`. This function checks this property for an inputted monomial. INPUT: - - mon_degs -- a monomial represented by a vector of degrees - - dlist -- a list of degrees with respect to which we check reducedness + - ``mon_degs`` -- a monomial represented by a vector of degrees + - ``dlist`` -- a list of degrees with respect to which we check reducedness OUTPUT: @@ -941,13 +941,13 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): def _macaulay_resultant_universal_polynomials(self, dlist): r""" - Given a list of degrees, this function returns a list of len(dlist) polynomials with len(dlist) variables, + Given a list of degrees, this function returns a list of ``len(dlist)`` polynomials with ``len(dlist)`` variables, with generic coefficients. This is useful for generating polynomials for tests, and for getting a universal macaulay resultant for the given degrees. INPUT: - - dlist -- a list of degrees. + - ``dlist`` -- a list of degrees. OUTPUT: @@ -990,16 +990,21 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): the resultant of universal polynomials as well as polynomials with constant coefficients. This is a project done in sage days 55. It's based on the implementation in Maple by - Manfred Minimair, which in turn is based on the following: - - -Using Algebraic Geometry by Cox, Little, O'Shea - -Canny, J., "Generalised characteristic polynomials", p.241--250, - J. Symbolic Comput. Vol. 9, No. 3, 1990 - -The algebraic theory of modular systems by Macaulay - + Manfred Minimair, which in turn is based on the references listed below: It calculates the Macaulay resultant for a list of Polynomials, up to sign! + REFERENCES: + + .. [CLO] D. Cox, J. Little, D. O'Shea. Using Algebraic Geometry. + Springer, 2005. + + .. [Can] J. Canny. Generalised characteristic polynomials. + J. Symbolic Comput. Vol. 9, No. 3, 1990, 241--250. + + .. [Mac] F.S. Macaulay. The algebraic theory of modular systems + Cambridge university press, 1916. + AUTHORS: - Hao Chen @@ -1008,9 +1013,9 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): INPUT: - - args -- a list of n homogeneous polynomials in n variables. - works when args[0] is the list of polynomials, - or args is itself the list of polynomials + - ``args`` -- a list of n homogeneous polynomials in n variables. + works when ``args[0]`` is the list of polynomials, + or ``args`` is itself the list of polynomials OUTPUT: @@ -1044,14 +1049,14 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): ... AssertionError: not all inputs are polynomials in the calling ring - The following example recreates Proposition 2.10 in Ch.3 of Using Algebraic Geometry:: + The following example recreates Proposition 2.10 in Ch.3 in [CLO]:: sage: K. = PolynomialRing(ZZ, 2) sage: flist,R = K._macaulay_resultant_universal_polynomials([1,1,2]) sage: R.macaulay_resultant(flist) u2^2*u4^2*u6 - 2*u1*u2*u4*u5*u6 + u1^2*u5^2*u6 - u2^2*u3*u4*u7 + u1*u2*u3*u5*u7 + u0*u2*u4*u5*u7 - u0*u1*u5^2*u7 + u1*u2*u3*u4*u8 - u0*u2*u4^2*u8 - u1^2*u3*u5*u8 + u0*u1*u4*u5*u8 + u2^2*u3^2*u9 - 2*u0*u2*u3*u5*u9 + u0^2*u5^2*u9 - u1*u2*u3^2*u10 + u0*u2*u3*u4*u10 + u0*u1*u3*u5*u10 - u0^2*u4*u5*u10 + u1^2*u3^2*u11 - 2*u0*u1*u3*u4*u11 + u0^2*u4^2*u11 - The following example degenerates into the determinant of a 3*3 matrix:: + The following example degenerates into the determinant of a `3*3` matrix:: sage: K. = PolynomialRing(ZZ, 2) sage: flist,R = K._macaulay_resultant_universal_polynomials([1,1,1]) @@ -1081,7 +1086,7 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): sage: R.macaulay_resultant([x+y,y^2,x]) 0 - an example of bad reduction at a prime p = 5:: + an example of bad reduction at a prime `p = 5`:: sage: R. = PolynomialRing(QQ,3) sage: R.macaulay_resultant([y,x^3+25*y^2*x,5*z]) From 97ee6f284baa43bc348c42a8b0a4ca254024e5f5 Mon Sep 17 00:00:00 2001 From: Ben Hutz Date: Wed, 9 Jul 2014 18:08:52 -0400 Subject: [PATCH 512/546] 15382: fixed some minor doc issues --- .../rings/polynomial/multi_polynomial.pyx | 17 ++--------- .../multi_polynomial_ring_generic.pyx | 30 +++++++++---------- 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index f5b2e2d04c5..35b9e858e19 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -1218,21 +1218,10 @@ cdef class MPolynomial(CommutativeRingElement): the resultant of universal polynomials as well as polynomials with constant coefficients. This is a project done in sage days 55. It's based on the implementation in Maple by - Manfred Minimair, which in turn is based on the references listed below: + Manfred Minimair, which in turn is based on the references [CLO], [Can], [Mac]. It calculates the Macaulay resultant for a list of Polynomials, up to sign! - REFERENCES: - - .. [CLO] D. Cox, J. Little, D. O'Shea. Using Algebraic Geometry. - Springer, 2005. - - .. [Can] J. Canny. Generalised characteristic polynomials. - J. Symbolic Comput. Vol. 9, No. 3, 1990, 241--250. - - .. [Mac] F.S. Macaulay. The algebraic theory of modular systems - Cambridge university press, 1916. - AUTHORS: - Hao Chen @@ -1241,9 +1230,9 @@ cdef class MPolynomial(CommutativeRingElement): INPUT: - - ``args`` -- a list of n homogeneous polynomials in n variables. + - ``args`` -- a list of `n-1` homogeneous polynomials in `n` variables. works when ``args[0]`` is the list of polynomials, - or args is itself the list of polynomials + or ``args`` is itself the list of polynomials OUTPUT: diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx index 89cf1e29ce7..9635a88276d 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx @@ -883,17 +883,17 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): def _macaulay_resultant_getS(self,mon_deg_tuple,dlist): r""" In the Macaulay resultant algorithm the list of all monomials of the total degree is partitioned into sets `S_i`. - This function returns the index i for the set `S_i` for the inputted given monomial. + This function returns the index `i` for the set `S_i` for the given monomial. INPUT: - - ``mon_deg_tuple`` -- a list representing a monomial of a degree d + - ``mon_deg_tuple`` -- a list representing a monomial of a degree `d` - ``dlist`` -- a list of degrees `d_i` of the polynomials in question, where - ``d = sum(dlist) - len(dlist) + 1`` + `d = sum(dlist) - len(dlist) + 1` OUTPUT: - - the index `i` such that the input monomial lives in `S_i` + - the index `i` such that the input monomial is in `S_i` EXAMPLES:: @@ -915,7 +915,7 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): r""" Helper function for the Macaulay resultant algorithm. A monomial in the variables `x_0,...,x_n` is called reduced with respect to the list of degrees `d_0,...,d_n` - if the degree of `x_i` in the monomial is `>= d_i` for exactly one `i`. This function checks this property for an inputted monomial. + if the degree of `x_i` in the monomial is `>= d_i` for exactly one `i`. This function checks this property for a monomial. INPUT: @@ -952,7 +952,7 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): OUTPUT: - a list of polynomials of the given degrees with general coefficients. - - a polynomial ring over self generated by the coefficients of the output polynomials. + - a polynomial ring over ``self`` generated by the coefficients of the output polynomials. EXAMPLES:: @@ -973,7 +973,7 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): flist = [] R = PolynomialRing(U,'x',n+1) ulist = U.gens() - for d in dlist: + for d in dlist: xlist = R.gens() degs = IntegerVectors(d, n+1) mon_d = [prod([xlist[i]**(deg[i]) for i in xrange(0,len(deg))]) @@ -991,7 +991,7 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): with constant coefficients. This is a project done in sage days 55. It's based on the implementation in Maple by Manfred Minimair, which in turn is based on the references listed below: - It calculates the Macaulay resultant for a list of Polynomials, + It calculates the Macaulay resultant for a list of polynomials, up to sign! REFERENCES: @@ -1013,13 +1013,13 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): INPUT: - - ``args`` -- a list of n homogeneous polynomials in n variables. + - ``args`` -- a list of `n` homogeneous polynomials in `n` variables. works when ``args[0]`` is the list of polynomials, or ``args`` is itself the list of polynomials OUTPUT: - - the macaulay resultant + - the macaulay resultant, an element of the base ring of ``self`` EXAMPLES: @@ -1142,7 +1142,7 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): dlist = [f.degree() for f in flist] xlist = self.gens() assert len(xlist) == len(dlist), 'number of polynomials(= %d) must equal number of variables (= %d)'%(len(dlist),len(xlist)) - n = len(dlist) - 1 + n = len(dlist) - 1 d = sum(dlist) - len(dlist) + 1 mons = IntegerVectors(d, n+1).list() # list of exponent-vectors(/lists) of monomials of degree d mons_num = len(mons) @@ -1164,7 +1164,7 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): for mon in new_f[0]: k=mons.index(mon) numer_matrix[j,k]=new_f[1][i] - i+=1 + i+=1 denom_matrix = numer_matrix.matrix_from_rows_and_columns(mons_to_keep,mons_to_keep) if denom_matrix.dimensions()[0] == 0: # here we choose the determinant of an empty matrix to be 1 @@ -1174,9 +1174,9 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): return U(numer_matrix.det()/denom_det) # if we get to this point, the determinant of the denominator was 0, and we get the resultant # by taking the free coefficient of the quotient of two characteristic polynomials - poly_num = numer_matrix.characteristic_polynomial('T') - poly_denom = denom_matrix.characteristic_polynomial('T') - poly_quo = poly_num.quo_rem(poly_denom)[0] + poly_num = numer_matrix.characteristic_polynomial('T') + poly_denom = denom_matrix.characteristic_polynomial('T') + poly_quo = poly_num.quo_rem(poly_denom)[0] return U(poly_quo(0)) #################### From 69b285d7fb1ebf189aea0b7cde311880ec884d6d Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Thu, 10 Jul 2014 08:27:58 +0200 Subject: [PATCH 513/546] Restore possibly positional arguments. Users might theoretically be passing these arguments by position, not by keyword, in which case incorporating them into a keyword argument dictionary will break backwards compatibility. Let's not do that until after we have deprecated them for some time, which this change does not yet do either. --- src/sage/plot/graphics.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sage/plot/graphics.py b/src/sage/plot/graphics.py index 169e194c110..18db0553f9a 100644 --- a/src/sage/plot/graphics.py +++ b/src/sage/plot/graphics.py @@ -3220,7 +3220,8 @@ def append(self, g): raise NotImplementedError('Appending to a graphics array is not yet implemented') - def save(self, filename=None, dpi=DEFAULT_DPI, figsize=None, **kwds): + def save(self, filename=None, dpi=DEFAULT_DPI, figsize=None, axes=None, + **kwds): r""" Save the ``graphics_array`` to a png called ``filename``. @@ -3270,9 +3271,9 @@ def save(self, filename=None, dpi=DEFAULT_DPI, figsize=None, **kwds): for i,g in zip(range(1, dims+1), glist): subplot = figure.add_subplot(rows, cols, i) g.matplotlib(filename, figure=figure, sub=subplot, - verify=do_verify, **kwds) + verify=do_verify, axes = axes, **kwds) g.save(filename, dpi=dpi, figure=figure, sub=subplot, - verify=do_verify, **kwds) + verify=do_verify, axes = axes, **kwds) def save_image(self, filename=None, *args, **kwds): r""" @@ -3297,7 +3298,8 @@ def save_image(self, filename=None, *args, **kwds): self.save(filename, *args, **kwds) - def show(self, filename=None, **kwds): + def show(self, filename=None, dpi=DEFAULT_DPI, figsize=None, + axes = None, **kwds): r""" Show this graphics array using the default viewer. @@ -3326,7 +3328,7 @@ def show(self, filename=None, **kwds): """ if filename is None: filename = graphics_filename() - self.save(filename, **kwds) + self.save(filename, dpi=dpi, figsize=figsize, axes = axes, **kwds) if not sage.doctest.DOCTEST_MODE and not sage.plot.plot.EMBEDDED_MODE: os.system('%s %s 2>/dev/null 1>/dev/null &'%( sage.misc.viewer.png_viewer(), filename)) From 20b3b4ae81823ebb6ba6a365c54358143838ec78 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 10 Jul 2014 09:31:45 +0200 Subject: [PATCH 514/546] trac #16622: HyperGraphGenerators --> HypergraphGenerators --- src/sage/graphs/hypergraph_generators.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/hypergraph_generators.py b/src/sage/graphs/hypergraph_generators.py index 0498189270b..79f25372b86 100644 --- a/src/sage/graphs/hypergraph_generators.py +++ b/src/sage/graphs/hypergraph_generators.py @@ -6,7 +6,7 @@ isomorphism. """ -class HyperGraphGenerators(): +class HypergraphGenerators(): r""" A class consisting of constructors for common hypergraphs. """ @@ -159,4 +159,4 @@ def nauty(self, number_of_sets, number_of_vertices, yield tuple( tuple( x for x in G.neighbors(v)) for v in range(number_of_vertices, total)) -hypergraphs = HyperGraphGenerators() +hypergraphs = HypergraphGenerators() From fb1d21cbee05d6be577bf66120aa20d3bd498ace Mon Sep 17 00:00:00 2001 From: Bruno Grenet Date: Thu, 10 Jul 2014 10:31:25 +0200 Subject: [PATCH 515/546] Add doc tests --- .../polynomial/polynomial_element_generic.py | 47 +++++++++++++++---- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index 825dc867b2d..fb7b7ed1e0a 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -561,17 +561,17 @@ def quo_rem(self, other): Returns the quotient and remainder of the Euclidean division of ``self`` and ``other``. - Raises ZerodivisionError if ``other`` is zero. Raises ArithmeticError + Raises ZerodivisionError if ``other`` is zero. Raises ArithmeticError if ``other`` has a nonunit leading coefficient. EXAMPLES:: - sage: P. = PolynomialRing(QQ,sparse=True) + sage: P. = PolynomialRing(ZZ,sparse=True) sage: R. = PolynomialRing(P,sparse=True) sage: f = R.random_element(10) sage: g = y^5+R.random_element(4) sage: q,r = f.quo_rem(g) - sage: f == q*g + r + sage: f == q*g + r and r.degree() < g.degree() True sage: g = x*y^5 sage: f.quo_rem(g) @@ -583,6 +583,32 @@ def quo_rem(self, other): Traceback (most recent call last): ... ZeroDivisionError: Division by zero polynomial + + TESTS:: + + sage: P. = PolynomialRing(ZZ,sparse=True) + sage: f = x^10-4*x^6-5 + sage: g = 17*x^22+x^15-3*x^5+1 + sage: q,r = g.quo_rem(f) + sage: g == f*q + r and r.degree() < f.degree() + True + sage: zero = P(0) + sage: zero.quo_rem(f) + (0, 0) + sage: Q. = IntegerModRing(14)[] + sage: f = y^10-4*y^6-5 + sage: g = 17*y^22+y^15-3*y^5+1 + sage: q,r = g.quo_rem(f) + sage: g == f*q + r and r.degree() < f.degree() + True + sage: f += 2*y^10 # 3 is invertible mod 14 + sage: q,r = g.quo_rem(f) + sage: g == f*q + r and r.degree() < f.degree() + True + + AUTHORS: + + - Bruno Grenet (2014-07-09) """ if other.is_zero(): raise ZeroDivisionError("Division by zero polynomial") @@ -592,20 +618,23 @@ def quo_rem(self, other): return self, self R = self.parent() - + d = other.degree() if self.degree() < d: return R.zero_element(), self quo = R.zero_element() rem = self - #inv_lc = R.base_ring().one_element()/other.leading_coefficient() + inv_lc = R.base_ring().one_element()/other.leading_coefficient() + + while rem.degree() >= d: - while rem.degree() > d: - c = rem.leading_coefficient()/other.leading_coefficient() + c = rem.leading_coefficient()*inv_lc e = rem.degree() - d - quo += c*R.one_element().shift(e) - rem -= c*other.shift(e) + quo += c*R.one_element().shift(e) + # we know that the leading coefficient of rem vanishes + # thus we avoid doing a useless computation + rem = rem[:rem.degree()] - c*other[:d].shift(e) return (quo,rem) From 9f30e9400f6fda042bf579e720ffee772c751f1e Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Fri, 11 Jul 2014 10:01:56 +0200 Subject: [PATCH 516/546] Always use graphics_filename instead of tmp_filename. Since graphics_filename was changed to return a tmp_filename except when run in embedded mode, we may always call that and leave the case distinction to it. This re-enables the automatic display of images in notebook if the gif or ffmpeg methods are called without a filename. --- src/sage/plot/animate.py | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/sage/plot/animate.py b/src/sage/plot/animate.py index 9160edb04c3..ecc48a37b21 100644 --- a/src/sage/plot/animate.py +++ b/src/sage/plot/animate.py @@ -97,7 +97,7 @@ import os from sage.structure.sage_object import SageObject -from sage.misc.temporary_file import tmp_filename, tmp_dir, graphics_filename +from sage.misc.temporary_file import tmp_dir, graphics_filename import plot import sage.misc.misc import sage.misc.viewer @@ -560,7 +560,7 @@ def gif(self, delay=20, savefile=None, iterations=0, show_path=False, raise OSError(msg) else: if not savefile: - savefile = tmp_filename(ext='.gif') + savefile = graphics_filename(ext='gif') if not savefile.endswith('.gif'): savefile += '.gif' savefile = os.path.abspath(savefile) @@ -631,19 +631,9 @@ def show(self, delay=20, iterations=0): See www.imagemagick.org and www.ffmpeg.org for more information. """ - if sage.doctest.DOCTEST_MODE: - filename = tmp_filename(ext='.gif') - self.gif(savefile=filename, delay=delay, iterations=iterations) - return - - if plot.EMBEDDED_MODE: - # graphics_filename is used so that notebook knows - # what file to display - filename = graphics_filename(ext='.gif') - self.gif(savefile=filename, delay=delay, iterations=iterations) - else: - filename = tmp_filename(ext='.gif') - self.gif(delay=delay, savefile=filename, iterations=iterations) + filename = graphics_filename(ext='gif') + self.gif(savefile=filename, delay=delay, iterations=iterations) + if not (sage.doctest.DOCTEST_MODE or plot.EMBEDDED_MODE): os.system('%s %s 2>/dev/null 1>/dev/null &'%( sage.misc.viewer.browser(), filename)) @@ -752,7 +742,7 @@ def ffmpeg(self, savefile=None, show_path=False, output_format=None, else: if output_format[0] != '.': output_format = '.'+output_format - savefile = tmp_filename(ext=output_format) + savefile = graphics_filename(ext=output_format[1:]) else: if output_format is None: suffix = os.path.splitext(savefile)[1] From bb7307f0649cfa90916f74c102d1d09a424b759a Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Fri, 11 Jul 2014 11:20:40 +0200 Subject: [PATCH 517/546] Prevent doctest from leaking file into current working directory. Now that the special case for DOCTEST_MODE in Graphics.show is gone, we have to be more careful about where we place our output files. --- src/sage/plot/animate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/plot/animate.py b/src/sage/plot/animate.py index ecc48a37b21..3fa450fb34a 100644 --- a/src/sage/plot/animate.py +++ b/src/sage/plot/animate.py @@ -461,7 +461,8 @@ def graphics_array(self, ncols=3): sage: g = a.graphics_array(ncols=2); print g Graphics Array of size 2 x 2 - sage: g.show('sage.png') # optional + sage: f = sage.misc.temporary_file.tmp_filename(ext='.png') + sage: g.show(f) # optional Frames can be specified as a generator too; it is internally converted to a list:: From f1ec984c85ef10089392f32385745f629a4fb29f Mon Sep 17 00:00:00 2001 From: Solomon Vishkautsan Date: Fri, 11 Jul 2014 12:31:07 +0300 Subject: [PATCH 518/546] replaced assert with raise. cleaned up for loops. added reverse lookup of monomials --- .../rings/polynomial/multi_polynomial.pyx | 6 +- .../multi_polynomial_ring_generic.pyx | 63 +++++++++++-------- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 35b9e858e19..26d2a78235e 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -1246,7 +1246,7 @@ cdef class MPolynomial(CommutativeRingElement): sage: y.macaulay_resultant(x+z) Traceback (most recent call last): ... - AssertionError: number of polynomials(= 2) must equal number of variables (= 3) + TypeError: number of polynomials(= 2) must equal number of variables (= 3) The polynomials need to be all homogeneous:: @@ -1254,7 +1254,7 @@ cdef class MPolynomial(CommutativeRingElement): sage: y.macaulay_resultant([x+z, z+x^3]) Traceback (most recent call last): ... - AssertionError: resultant for non-homogeneous polynomials is not supported + TypeError: resultant for non-homogeneous polynomials is not supported All polynomials must be in the same ring:: @@ -1263,7 +1263,7 @@ cdef class MPolynomial(CommutativeRingElement): sage: y.macaulay_resultant(z+x,z) Traceback (most recent call last): ... - AssertionError: not all inputs are polynomials in the calling ring + TypeError: not all inputs are polynomials in the calling ring The following example recreates Proposition 2.10 in Ch.3 of Using Algebraic Geometry:: diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx index 9635a88276d..d2aaa00110d 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx @@ -17,6 +17,11 @@ import polynomial_ring from sage.categories.commutative_rings import CommutativeRings _CommutativeRings = CommutativeRings() from sage.rings.polynomial.polynomial_ring_constructor import polynomial_default_category +# added for macaulay_resultant: +from sage.misc.misc_c import prod +from sage.combinat.integer_vector import IntegerVectors +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.arith import binomial def is_MPolynomialRing(x): return bool(PY_TYPE_CHECK(x, MPolynomialRing_generic)) @@ -960,12 +965,6 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): sage: R._macaulay_resultant_universal_polynomials([1,1,2]) ([u0*x0 + u1*x1 + u2*x2, u3*x0 + u4*x1 + u5*x2, u6*x0^2 + u7*x0*x1 + u9*x1^2 + u8*x0*x2 + u10*x1*x2 + u11*x2^2], Multivariate Polynomial Ring in x0, x1, x2 over Multivariate Polynomial Ring in u0, u1, u2, u3, u4, u5, u6, u7, u8, u9, u10, u11 over Integer Ring) """ - from sage.misc.misc_c import prod - from sage.combinat.integer_vector import IntegerVectors - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - from sage.rings.arith import binomial - from sage.rings.integer_ring import ZZ - n = len(dlist) - 1 number_of_coeffs = sum([binomial(n+di,di) for di in dlist]) U = PolynomialRing(ZZ,'u',number_of_coeffs) @@ -984,7 +983,7 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): ulist = ulist[len(mon_d):] return flist, R - def macaulay_resultant(self, *args): + def macaulay_resultant(self, *args, **kwds): r""" This is an implementation of the Macaulay Resultant. It computes the resultant of universal polynomials as well as polynomials @@ -1017,10 +1016,20 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): works when ``args[0]`` is the list of polynomials, or ``args`` is itself the list of polynomials + kwds: + + - ``sparse`` -- boolean (optional - default: ``False``) + if ``True`` function creates sparse matrices. + OUTPUT: - the macaulay resultant, an element of the base ring of ``self`` + .. TODO:: + Working with sparse matrices should usually give faster results, + but with the current implementation it actually works slower. + There should be a way to improve performance with regards to this. + EXAMPLES: @@ -1030,7 +1039,7 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): sage: R.macaulay_resultant([y,x+z]) Traceback (most recent call last): ... - AssertionError: number of polynomials(= 2) must equal number of variables (= 3) + TypeError: number of polynomials(= 2) must equal number of variables (= 3) The polynomials need to be all homogeneous:: @@ -1038,7 +1047,7 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): sage: R.macaulay_resultant([y, x+z, z+x^3]) Traceback (most recent call last): ... - AssertionError: resultant for non-homogeneous polynomials is not supported + TypeError: resultant for non-homogeneous polynomials is not supported All polynomials must be in the same ring:: @@ -1047,7 +1056,7 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): sage: S.macaulay_resultant([y, z+x]) Traceback (most recent call last): ... - AssertionError: not all inputs are polynomials in the calling ring + TypeError: not all inputs are polynomials in the calling ring The following example recreates Proposition 2.10 in Ch.3 in [CLO]:: @@ -1124,47 +1133,51 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): True """ - from sage.matrix.constructor import matrix from sage.matrix.constructor import zero_matrix - from sage.combinat.integer_vector import IntegerVectors if len(args) == 1 and isinstance(args[0],list): flist = args[0] else: flist = args - assert len(flist) > 0, 'you have to input least 1 polynomial in the list' - assert all([f.is_homogeneous() for f in flist]), 'resultant for non-homogeneous polynomials is not supported' - assert all([self.is_parent_of(f) for f in flist]), 'not all inputs are polynomials in the calling ring' + if len(flist) <= 0: + raise TypeError('input list should contain at least 1 polynomial') + if not all([f.is_homogeneous() for f in flist]): + raise TypeError('resultant for non-homogeneous polynomials is not supported') + if not all([self.is_parent_of(f) for f in flist]): + raise TypeError('not all inputs are polynomials in the calling ring') + + sparse = kwds.pop('sparse', False) U = self.base_ring() # ring of coefficients of self dlist = [f.degree() for f in flist] xlist = self.gens() - assert len(xlist) == len(dlist), 'number of polynomials(= %d) must equal number of variables (= %d)'%(len(dlist),len(xlist)) + if len(xlist) != len(dlist): + raise TypeError('number of polynomials(= %d) must equal number of variables (= %d)'%(len(dlist),len(xlist))) n = len(dlist) - 1 d = sum(dlist) - len(dlist) + 1 mons = IntegerVectors(d, n+1).list() # list of exponent-vectors(/lists) of monomials of degree d + mons_idx = { str(mon) : idx for idx,mon in enumerate(mons)} # a reverse index lookup for monomials mons_num = len(mons) mons_to_keep = [] newflist = [] flist=[[f.exponents(),f.coefficients()] for f in flist] # strip coefficients of the input polynomials - numer_matrix = zero_matrix(self.base_ring(),mons_num) + numer_matrix = zero_matrix(self.base_ring(),mons_num, sparse=sparse) - for j in xrange(0,mons_num): - if not self._macaulay_resultant_is_reduced(mons[j],dlist): + for j,mon in enumerate(mons): + # if monomial is not reduced, then we keep it in the denominator matrix: + if not self._macaulay_resultant_is_reduced(mon,dlist): mons_to_keep.append(j) - si_mon = self._macaulay_resultant_getS(mons[j], dlist) + si_mon = self._macaulay_resultant_getS(mon, dlist) # Monomial is in S_i under the partition, now we reduce the i'th degree of the monomial - new_mon = list(mons[j]) + new_mon = list(mon) new_mon[si_mon] -= dlist[si_mon] new_f = [[[g[k] + new_mon[k] for k in range(n+1)] for g in flist[si_mon][0]], flist[si_mon][1]] - i=0 - for mon in new_f[0]: - k=mons.index(mon) + for i,mon in enumerate(new_f[0]): + k = mons_idx[str(mon)] numer_matrix[j,k]=new_f[1][i] - i+=1 denom_matrix = numer_matrix.matrix_from_rows_and_columns(mons_to_keep,mons_to_keep) if denom_matrix.dimensions()[0] == 0: # here we choose the determinant of an empty matrix to be 1 From fbc55f498f01beace1622d3cae176f841a8d1cbf Mon Sep 17 00:00:00 2001 From: Punarbasu Purkayastha Date: Fri, 11 Jul 2014 21:35:43 +0800 Subject: [PATCH 519/546] add a doctest --- src/sage/plot/graphics.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/plot/graphics.py b/src/sage/plot/graphics.py index d22f64b84e8..530f83c9455 100644 --- a/src/sage/plot/graphics.py +++ b/src/sage/plot/graphics.py @@ -2868,6 +2868,11 @@ def save(self, filename=None, **kwds): sage: a = plot_vector_field((x,-y),(x,-1,1),(y,-1,1)) sage: filename=os.path.join(SAGE_TMP, 'test2.png') sage: a.save(filename) + + The following plot should show the axes; fixes :trac:`14782` :: + + sage: plot(x^2, (x, 1, 2), ticks=[[], []]) + """ options = dict() options.update(self.SHOW_OPTIONS) From db145e34be5d524b10f1504e5892e256dd921d05 Mon Sep 17 00:00:00 2001 From: Ben Hutz Date: Fri, 11 Jul 2014 12:16:15 -0400 Subject: [PATCH 520/546] removed trailing whitespaces --- .../rings/polynomial/multi_polynomial.pyx | 4 +--- .../multi_polynomial_ring_generic.pyx | 19 ++++++++----------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 26d2a78235e..ffb7697505f 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -1224,9 +1224,7 @@ cdef class MPolynomial(CommutativeRingElement): AUTHORS: - - Hao Chen - - Solomon Vishkautsan - - Ben Hutz + - Hao Chen, Solomon Vishkautsan (7-2014) INPUT: diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx index d2aaa00110d..9dd9af35db5 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx @@ -943,7 +943,7 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): """ diff = [mon_degs[i] - dlist[i] for i in xrange(0,len(dlist))] return len([1 for d in diff if d >= 0]) == 1 - + def _macaulay_resultant_universal_polynomials(self, dlist): r""" Given a list of degrees, this function returns a list of ``len(dlist)`` polynomials with ``len(dlist)`` variables, @@ -998,7 +998,7 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): .. [CLO] D. Cox, J. Little, D. O'Shea. Using Algebraic Geometry. Springer, 2005. - .. [Can] J. Canny. Generalised characteristic polynomials. + .. [Can] J. Canny. Generalised characteristic polynomials. J. Symbolic Comput. Vol. 9, No. 3, 1990, 241--250. .. [Mac] F.S. Macaulay. The algebraic theory of modular systems @@ -1006,9 +1006,7 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): AUTHORS: - - Hao Chen - - Solomon Vishkautsan - - Ben Hutz + - Hao Chen, Solomon Vishkautsan (7-2014) INPUT: @@ -1027,12 +1025,11 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): .. TODO:: Working with sparse matrices should usually give faster results, - but with the current implementation it actually works slower. + but with the current implementation it actually works slower. There should be a way to improve performance with regards to this. EXAMPLES: - The number of polynomials has to match the number of variables:: sage: R. = PolynomialRing(QQ,3) @@ -1138,14 +1135,14 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): if len(args) == 1 and isinstance(args[0],list): flist = args[0] - else: + else: flist = args if len(flist) <= 0: raise TypeError('input list should contain at least 1 polynomial') - if not all([f.is_homogeneous() for f in flist]): + if not all([f.is_homogeneous() for f in flist]): raise TypeError('resultant for non-homogeneous polynomials is not supported') - if not all([self.is_parent_of(f) for f in flist]): + if not all([self.is_parent_of(f) for f in flist]): raise TypeError('not all inputs are polynomials in the calling ring') sparse = kwds.pop('sparse', False) @@ -1153,7 +1150,7 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): U = self.base_ring() # ring of coefficients of self dlist = [f.degree() for f in flist] xlist = self.gens() - if len(xlist) != len(dlist): + if len(xlist) != len(dlist): raise TypeError('number of polynomials(= %d) must equal number of variables (= %d)'%(len(dlist),len(xlist))) n = len(dlist) - 1 d = sum(dlist) - len(dlist) + 1 From b971d2ad679e8288fafb952cfa017e0dffaf1423 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Fri, 11 Jul 2014 20:32:12 -0400 Subject: [PATCH 521/546] Python 2.7.8 --- build/pkgs/python/checksums.ini | 6 +++--- build/pkgs/python/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/python/checksums.ini b/build/pkgs/python/checksums.ini index 9d5d10e5e99..7a51b3a186c 100644 --- a/build/pkgs/python/checksums.ini +++ b/build/pkgs/python/checksums.ini @@ -1,4 +1,4 @@ tarball=python-VERSION.tar.gz -sha1=1db01d7f325d8ceaf986976800106018b82ae45a -md5=cf842800b67841d64e7fb3cd8acb5663 -cksum=559226099 +sha1=511960dd78451a06c9df76509635aeec05b2051a +md5=d4bca0159acb0b44a781292b5231936f +cksum=3938213866 diff --git a/build/pkgs/python/package-version.txt b/build/pkgs/python/package-version.txt index 1f7da99d4e1..6a81b4c8379 100644 --- a/build/pkgs/python/package-version.txt +++ b/build/pkgs/python/package-version.txt @@ -1 +1 @@ -2.7.7 +2.7.8 From 5e816069ca935085490ffbdf2a379cc6148b2197 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Fri, 11 Jul 2014 20:51:07 -0400 Subject: [PATCH 522/546] Fix more source line numbers from #16622 --- src/sage/combinat/designs/incidence_structures.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 8c1bc80089c..f35de38520d 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -950,19 +950,19 @@ def block_design_checker(self, t, v, k, lmbda, type=None): True sage: BD.block_design_checker(2, 7, 3, 1,"binary") - doctest:1: DeprecationWarning: .block_design_checker(type='binary') is + doctest:...: DeprecationWarning: .block_design_checker(type='binary') is deprecated; use .is_binary() instead See http://trac.sagemath.org/16553 for details. True sage: BD.block_design_checker(2, 7, 3, 1,"connected") - doctest:1: DeprecationWarning: block_design_checker(type='connected') is + doctest:...: DeprecationWarning: block_design_checker(type='connected') is deprecated, please use .is_connected() instead See http://trac.sagemath.org/16553 for details. True sage: BD.block_design_checker(2, 7, 3, 1,"simple") - doctest:1: DeprecationWarning: .block_design_checker(type='simple') + doctest:...: DeprecationWarning: .block_design_checker(type='simple') is deprecated; all designs here are simple! See http://trac.sagemath.org/16553 for details. True From 8e376875d77f519a221ba5bdb2aa15c0d78b4517 Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Sat, 12 Jul 2014 09:53:13 +0200 Subject: [PATCH 523/546] Latex formatting for algebraic numbers. --- src/sage/rings/qqbar.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 0d66856d122..d3e860f2402 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -3134,6 +3134,10 @@ def _repr_(self): 2/7*I + 1/3 sage: QQbar.zeta(4) + 5 I + 5 + sage: QQbar.zeta(4) + 1*I + sage: 3*QQbar.zeta(4) + 3*I sage: QQbar.zeta(17) 0.9324722294043558? + 0.3612416661871530?*I sage: AA(19).sqrt() @@ -3150,6 +3154,36 @@ def _repr_(self): else: return repr(RIF(self._value)) + def _latex_(self): + r""" + Returns the latex representation of this number. + + EXAMPLES:: + + sage: latex(AA(22/7)) + \frac{22}{7} + sage: latex(QQbar(1/3 + 2/7*I)) + \frac{2}{7} \sqrt{-1} + \frac{1}{3} + sage: latex(QQbar.zeta(4) + 5) + \sqrt{-1} + 5 + sage: latex(QQbar.zeta(4)) + 1 \sqrt{-1} + sage: latex(3*QQbar.zeta(4)) + 3 \sqrt{-1} + sage: latex(QQbar.zeta(17)) + 0.9324722294043558? + 0.3612416661871530? \sqrt{-1} + sage: latex(AA(19).sqrt()) + 4.358898943540674? + """ + from sage.misc.latex import latex + if self._descr.is_rational(): + return latex(self._descr._value) + if isinstance(self._descr, ANRootOfUnity) and self._descr._angle == QQ_1_4: + return r'%s \sqrt{-1}'%self._descr._scale + if isinstance(self._descr, ANExtensionElement) and self._descr._generator is QQbar_I_generator: + return latex(self._descr._value) + return repr(self).replace('*I', r' \sqrt{-1}') + def _sage_input_(self, sib, coerce): r""" Produce an expression which will reproduce this value when evaluated. From 2c5f58bd16352a82c7c7b9ad6f1c17cd86a33571 Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Sun, 13 Jul 2014 09:36:55 +0200 Subject: [PATCH 524/546] Avoid unneccessary qualification of tmp_filename in doctests. Volker Braun pointed this out in comment:6:ticket:16645. --- src/sage/plot/animate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/plot/animate.py b/src/sage/plot/animate.py index 3fa450fb34a..0a0eb141766 100644 --- a/src/sage/plot/animate.py +++ b/src/sage/plot/animate.py @@ -29,7 +29,7 @@ Animate using ffmpeg instead of ImageMagick:: - sage: f = sage.misc.temporary_file.tmp_filename(ext='.gif') + sage: f = tmp_filename(ext='.gif') sage: a.save(filename=f,use_ffmpeg=True) # optional -- ffmpeg An animated :class:`sage.plot.graphics.GraphicsArray` of rotating ellipses:: @@ -461,7 +461,7 @@ def graphics_array(self, ncols=3): sage: g = a.graphics_array(ncols=2); print g Graphics Array of size 2 x 2 - sage: f = sage.misc.temporary_file.tmp_filename(ext='.png') + sage: f = tmp_filename(ext='.png') sage: g.show(f) # optional Frames can be specified as a generator too; it is internally converted to a list:: From 893fd06dfeba8b58065117aa7e175dd44f2cf273 Mon Sep 17 00:00:00 2001 From: Luis Felipe Tabera Alonso Date: Mon, 14 Jul 2014 17:15:09 +0200 Subject: [PATCH 525/546] Updated documentation. --- src/sage/rings/quotient_ring_element.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/quotient_ring_element.py b/src/sage/rings/quotient_ring_element.py index 5e62b93d6de..a5d8cf368ec 100644 --- a/src/sage/rings/quotient_ring_element.py +++ b/src/sage/rings/quotient_ring_element.py @@ -421,7 +421,21 @@ def _div_(self, right): def _im_gens_(self, codomain, im_gens): """ - TESTS: + The image of ``self`` in ``codomain`` under the map that sends + ``self.parent().gens()`` to ``im_gens``. + + INPUT: + + - ``codomain``: a ring. + - ``im_gens``: The image of ``self.parent().gens()`` in ``codomain``. + + OUPUT: + + The natural image of ``self`` in ``codomain`` under the + map that sends the images of the generators of the parent of + ``self`` to the tuple of elements of ``im_gens``. + + EXAMPLES: Ring homomorphisms whose domain is the fraction field of a quotient ring work correctly (see :trac:`16135`):: @@ -432,7 +446,11 @@ def _im_gens_(self, codomain, im_gens): sage: f = K.hom((t^3, t^2)) sage: map(f, K.gens()) [t^3, t^2] - + sage: xbar, ybar = K.gens() + sage: f(1/ybar) + 1/t^2 + sage: f(xbar/ybar) + t """ return self.lift()._im_gens_(codomain, im_gens) From 63176d83a459e58ebe11a4035d1eb68880501ea4 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Mon, 14 Jul 2014 17:33:27 +0100 Subject: [PATCH 526/546] Trac 16135: further documentation improvements --- src/sage/rings/quotient_ring_element.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/quotient_ring_element.py b/src/sage/rings/quotient_ring_element.py index a5d8cf368ec..4fa21390b1a 100644 --- a/src/sage/rings/quotient_ring_element.py +++ b/src/sage/rings/quotient_ring_element.py @@ -421,19 +421,21 @@ def _div_(self, right): def _im_gens_(self, codomain, im_gens): """ - The image of ``self`` in ``codomain`` under the map that sends - ``self.parent().gens()`` to ``im_gens``. + Return the image of ``self`` in ``codomain`` under the map + that sends ``self.parent().gens()`` to ``im_gens``. INPUT: - - ``codomain``: a ring. - - ``im_gens``: The image of ``self.parent().gens()`` in ``codomain``. + - ``codomain`` -- a ring + + - ``im_gens`` -- a tuple of elements `f(x)` in ``codomain``, + one for each `x` in ``self.parent().gens()``, that define + a homomorphism `f` from ``self.parent()`` to ``codomain`` OUPUT: - The natural image of ``self`` in ``codomain`` under the - map that sends the images of the generators of the parent of - ``self`` to the tuple of elements of ``im_gens``. + The image of ``self`` in ``codomain`` under the above + homomorphism `f`. EXAMPLES: From 44321ffcf5f2017adb12c306741e8062aa0da6e1 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Wed, 16 Jul 2014 13:23:11 +0200 Subject: [PATCH 527/546] New common transducer Wait Introduction of a new transducer Wait which writes False until reading the threshold-th occurrence of a true input letter and which then writes True. This might be useful together with the subblock counting transducer. --- .../finite_state_machine_generators.py | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/sage/combinat/finite_state_machine_generators.py b/src/sage/combinat/finite_state_machine_generators.py index b5aa35a63d0..80228948f95 100644 --- a/src/sage/combinat/finite_state_machine_generators.py +++ b/src/sage/combinat/finite_state_machine_generators.py @@ -27,6 +27,7 @@ :meth:`~TransducerGenerators.add` | Returns a transducer realizing addition. :meth:`~TransducerGenerators.sub` | Returns a transducer realizing subtraction. :meth:`~TransducerGenerators.CountSubblockOccurrences` | Returns a transducer counting the occurrences of a subblock. + :meth:`~TransducerGenerators.Wait` | Returns a transducer writing ``False`` until first (or k-th) true input is read. :meth:`~TransducerGenerators.weight` | Returns a transducer realizing the Hamming weight :meth:`~TransducerGenerators.GrayCode` | Returns a transducer realizing binary Gray code. @@ -77,6 +78,7 @@ class TransducerGenerators(object): - :meth:`~add` - :meth:`~sub` - :meth:`~CountSubblockOccurrences` + - :meth:`~Wait` - :meth:`~GrayCode` """ @@ -240,6 +242,49 @@ def transition_function(read, input): s.is_final = True return T + def Wait(self, input_alphabet, threshold=1): + r""" + Writes ``False`` until reading the ``threshold``-th occurrence + of a true input letter; then writes ``True``. + + INPUT: + + - ``input_alphabet`` -- a list or other iterable. + + - ``threshold`` -- a positive integer specifying how many + occurrences of True inputs are waited for. + + OUTPUT: + + A transducer writing ``False`` until the ``threshold``-th true + (Python's standard conversion to booleanis used to convert the + actual input to boolean) input is read. Subsequently, the + transducer writes ``True``. + + EXAMPLES:: + + sage: T = transducers.Wait([0, 1]) + sage: T([0, 0, 1, 0, 1, 0]) + [False, False, True, True, True, True] + sage: T2 = transducers.Wait([0, 1], threshold=2) + sage: T2([0, 0, 1, 0, 1, 0]) + [False, False, False, False, True, True] + """ + def transition(state, input): + if state == threshold: + return (threshold, True) + if not input: + return (state, False) + return (state+1, state + 1 == threshold) + + T = Transducer(transition, + input_alphabet=input_alphabet, + initial_states=[0]) + for s in T.iter_states(): + s.is_final = True + + return T + def operator(self, operator, input_alphabet, number_of_operands=2): r""" From d9c48e12ae093121eb5977c12e75cc1eee53dd54 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Wed, 16 Jul 2014 15:07:17 +0200 Subject: [PATCH 528/546] New common transducers "all" and "or" to realize logical "and" and "or" operations --- .../finite_state_machine_generators.py | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/src/sage/combinat/finite_state_machine_generators.py b/src/sage/combinat/finite_state_machine_generators.py index b5aa35a63d0..9ca35facbf4 100644 --- a/src/sage/combinat/finite_state_machine_generators.py +++ b/src/sage/combinat/finite_state_machine_generators.py @@ -24,6 +24,8 @@ :meth:`~TransducerGenerators.Identity` | Returns a transducer realizing the identity map. :meth:`~TransducerGenerators.abs` | Returns a transducer realizing absolute value. :meth:`~TransducerGenerators.operator` | Returns a transducer realizing a binary operation. + :meth:`~TransducerGenerators.all` | Returns a transducer realizing logical ``and``. + :meth:`~TransducerGenerators.any` | Returns a transducer realizing logical ``or``. :meth:`~TransducerGenerators.add` | Returns a transducer realizing addition. :meth:`~TransducerGenerators.sub` | Returns a transducer realizing subtraction. :meth:`~TransducerGenerators.CountSubblockOccurrences` | Returns a transducer counting the occurrences of a subblock. @@ -74,6 +76,8 @@ class TransducerGenerators(object): - :meth:`~Identity` - :meth:`~abs` - :meth:`~TransducerGenerators.operator` + - :meth:`~all` + - :meth:`~any` - :meth:`~add` - :meth:`~sub` - :meth:`~CountSubblockOccurrences` @@ -317,6 +321,111 @@ def transition_function(state, operands): final_states=[0]) + def all(self, input_alphabet, number_of_operands=2): + """ + Returns a transducer which realizes logical ``and`` over the given + input alphabet. + + INPUT: + + - ``input_alphabet`` -- a list or other iterable. + + - ``number_of_operands`` -- (default: `2`) specifies the number + of input arguments for the ``and`` operation. + + OUTPUT: + + A transducer mapping an input word + `(i_{01}, \ldots, i_{0d})\ldots (i_{k1}, \ldots, i_{kd})` to the word + `(i_{01} \land \cdots \land i_{0d})\ldots (i_{k1} \land \cdots \land i_{kd})`. + + The input alphabet of the generated transducer is the cartesian + product of ``number_of_operands`` copies of ``input_alphabet``. + + EXAMPLE: + + The following transducer realizes letter-wise + logical ``and``:: + + sage: T = transducers.all([False, True]) + sage: T.transitions() + [Transition from 0 to 0: (False, False)|False, + Transition from 0 to 0: (False, True)|False, + Transition from 0 to 0: (True, False)|False, + Transition from 0 to 0: (True, True)|True] + sage: T.input_alphabet + [(False, False), (False, True), (True, False), (True, True)] + sage: T.initial_states() + [0] + sage: T.final_states() + [0] + sage: T([(False, False), (False, True), (True, False), (True, True)]) + [False, False, False, True] + + More than two operands and other input alphabets (with + conversion to boolean) are also possible:: + + sage: T3 = transducers.all([0, 1], number_of_operands=3) + sage: T3([(0, 0, 0), (1, 0, 0), (1, 1, 1)]) + [False, False, True] + """ + def logical_and(*args): + return all(args) + return self.operator(logical_and, input_alphabet, number_of_operands) + + + def any(self, input_alphabet, number_of_operands=2): + """ + Returns a transducer which realizes logical ``or`` over the given + input alphabet. + + INPUT: + + - ``input_alphabet`` -- a list or other iterable. + + - ``number_of_operands`` -- (default: `2`) specifies the number + of input arguments for the ``or`` operation. + + OUTPUT: + + A transducer mapping an input word + `(i_{01}, \ldots, i_{0d})\ldots (i_{k1}, \ldots, i_{kd})` to the word + `(i_{01} \lor \cdots \lor i_{0d})\ldots (i_{k1} \lor \cdots \lor i_{kd})`. + + The input alphabet of the generated transducer is the cartesian + product of ``number_of_operands`` copies of ``input_alphabet``. + + EXAMPLE: + + The following transducer realizes letter-wise + logical ``or``:: + + sage: T = transducers.any([False, True]) + sage: T.transitions() + [Transition from 0 to 0: (False, False)|False, + Transition from 0 to 0: (False, True)|True, + Transition from 0 to 0: (True, False)|True, + Transition from 0 to 0: (True, True)|True] + sage: T.input_alphabet + [(False, False), (False, True), (True, False), (True, True)] + sage: T.initial_states() + [0] + sage: T.final_states() + [0] + sage: T([(False, False), (False, True), (True, False), (True, True)]) + [False, True, True, True] + + More than two operands and other input alphabets (with + conversion to boolean) are also possible:: + + sage: T3 = transducers.any([0, 1], number_of_operands=3) + sage: T3([(0, 0, 0), (1, 0, 0), (1, 1, 1)]) + [False, True, True] + """ + def logical_or(*args): + return any(args) + return self.operator(logical_or, input_alphabet, number_of_operands) + def add(self, input_alphabet): """ Returns a transducer which realizes addition on pairs over the From 02b4566096ba9ff6a250782167003b443e0ec659 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Wed, 16 Jul 2014 15:43:51 +0200 Subject: [PATCH 529/546] trac #16186: transducers.add for arbitrary length input vectors --- .../finite_state_machine_generators.py | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/finite_state_machine_generators.py b/src/sage/combinat/finite_state_machine_generators.py index 9ca35facbf4..51e20da4266 100644 --- a/src/sage/combinat/finite_state_machine_generators.py +++ b/src/sage/combinat/finite_state_machine_generators.py @@ -426,7 +426,8 @@ def logical_or(*args): return any(args) return self.operator(logical_or, input_alphabet, number_of_operands) - def add(self, input_alphabet): + + def add(self, input_alphabet, number_of_operands=2): """ Returns a transducer which realizes addition on pairs over the given input alphabet. @@ -435,13 +436,17 @@ def add(self, input_alphabet): - ``input_alphabet`` -- a list or other iterable. + - ``number_of_operands`` -- (default: `2`) it specifies the number + of input arguments the operator takes. + OUTPUT: - A transducer mapping an input word `(i_0, i'_0)\ldots (i_k, i'_k)` - to the word `(i_0 + i'_0)\ldots (i_k + i'_k)`. + A transducer mapping an input word + `(i_{01}, \ldots, i_{0d})\ldots (i_{k1}, \ldots, i_{kd})` to the word + `(i_{01} + \cdots + i_{0d})\ldots (i_{k1} + \cdots + i_{kd})`. The input alphabet of the generated transducer is the cartesian - product of two copies of ``input_alphabet``. + product of ``number_of_operands`` copies of ``input_alphabet``. EXAMPLE: @@ -462,9 +467,21 @@ def add(self, input_alphabet): [0] sage: T([(0, 0), (0, 1), (1, 0), (1, 1)]) [0, 1, 1, 2] + + More than two operators can also be handled, + cf. :trac:`16186`:: + + sage: T3 = transducers.add([0, 1], number_of_operands=3) + sage: T3.input_alphabet + [(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), + (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)] + sage: T3([(0, 0, 0), (0, 1, 0), (0, 1, 1), (1, 1, 1)]) + [0, 1, 2, 3] """ - import operator - return self.operator(operator.add, input_alphabet) + def multioperand_add(*args): + return sum(args) + return self.operator(multioperand_add, input_alphabet, + number_of_operands=number_of_operands) def sub(self, input_alphabet): From f7f37555460bcf62c657cba260f7151a9d5c6576 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Wed, 16 Jul 2014 20:46:19 +0100 Subject: [PATCH 530/546] Trac 11474: fix doctest that depended on the extended Cremona database --- src/sage/databases/cremona.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/databases/cremona.py b/src/sage/databases/cremona.py index 965c4e20fd9..754ea2d535a 100644 --- a/src/sage/databases/cremona.py +++ b/src/sage/databases/cremona.py @@ -847,15 +847,15 @@ def data_from_coefficients(self, ainvs): EXAMPLES:: - sage: d = CremonaDatabase().data_from_coefficients([0, 0, 1, 3182, -53895]) + sage: d = CremonaDatabase().data_from_coefficients([1, -1, 1, 31, 128]) sage: d['conductor'] - 137411 + 1953 sage: d['cremona_label'] - '137411a1' + '1953c1' sage: d['rank'] 1 sage: d['torsion_order'] - 1 + 2 """ ainvs = str(list(ainvs)) if self.get_skeleton() == _miniCremonaSkeleton: From 02bc4122b7a49400e327f3a0259890eb0cce6705 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Wed, 16 Jul 2014 21:28:16 +0100 Subject: [PATCH 531/546] Trac 12880: reviewer patch --- .../elliptic_curves/ell_curve_isogeny.py | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index 08d4c839295..cab5ef303b8 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -700,7 +700,6 @@ class EllipticCurveIsogeny(Morphism): True sage: phi_s.rational_maps() == phi.rational_maps() True - However only cyclic normalized isogenies can be constructed this way. So it won't find the isogeny [3]:: @@ -737,22 +736,22 @@ class EllipticCurveIsogeny(Morphism): sage: phi.rational_maps() (((4/25*i + 3/25)*x^5 + (4/5*i - 2/5)*x^3 - x)/(x^4 + (-4/5*i + 2/5)*x^2 + (-4/25*i - 3/25)), ((11/125*i + 2/125)*x^6*y + (-23/125*i + 64/125)*x^4*y + (141/125*i + 162/125)*x^2*y + (3/25*i - 4/25)*y)/(x^6 + (-6/5*i + 3/5)*x^4 + (-12/25*i - 9/25)*x^2 + (2/125*i - 11/125))) - + Domain and codomain tests (see :trac:`12880`):: - - sage: E = EllipticCurve(QQ, [0,0,0,1,0]) - sage: phi = EllipticCurveIsogeny(E, E(0,0)) - sage: phi.domain() == E - True - sage: phi.codomain() - Elliptic Curve defined by y^2 = x^3 - 4*x over Rational Field - - sage: E = EllipticCurve(GF(31), [1,0,0,1,2]) - sage: phi = EllipticCurveIsogeny(E, [17, 1]) - sage: phi.domain() - Elliptic Curve defined by y^2 + x*y = x^3 + x + 2 over Finite Field of size 31 - sage: phi.codomain() - Elliptic Curve defined by y^2 + x*y = x^3 + 24*x + 6 over Finite Field of size 31 + + sage: E = EllipticCurve(QQ, [0,0,0,1,0]) + sage: phi = EllipticCurveIsogeny(E, E(0,0)) + sage: phi.domain() == E + True + sage: phi.codomain() + Elliptic Curve defined by y^2 = x^3 - 4*x over Rational Field + + sage: E = EllipticCurve(GF(31), [1,0,0,1,2]) + sage: phi = EllipticCurveIsogeny(E, [17, 1]) + sage: phi.domain() + Elliptic Curve defined by y^2 + x*y = x^3 + x + 2 over Finite Field of size 31 + sage: phi.codomain() + Elliptic Curve defined by y^2 + x*y = x^3 + 24*x + 6 over Finite Field of size 31 """ #################### From 74468b451992a6fc6b56c82dbf8afbf685fc210b Mon Sep 17 00:00:00 2001 From: Jan Keitel Date: Wed, 16 Jul 2014 23:44:12 +0200 Subject: [PATCH 532/546] Fixed docstring formatting. --- .../rings/function_field/function_field.py | 22 ++++++++++--------- src/sage/rings/function_field/maps.py | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index ac2706fc46b..ead5ac73835 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -8,7 +8,7 @@ - Robert Bradshaw (2010-05-30): added is_finite() - Julian Rueth (2011-06-08, 2011-09-14, 2014-06-23): fixed hom(), extension(); -use @cached_method; added derivation() + use @cached_method; added derivation() - Maarten Derickx (2011-09-11): added doctests @@ -390,7 +390,7 @@ def __init__(self, polynomial, names, - ``names`` -- variable names (as a tuple of length 1 or string) - ``category`` -- a category (defaults to category of function fields) - EXAMPLES:: + EXAMPLES: We create an extension of a function field:: @@ -1457,13 +1457,14 @@ def genus(self): @cached_method def derivation(self): - """ + r""" Return a generator of the space of derivations over the constant base field of this function field. - A derivation on `R` is map `R\to R` with - `D(\alpha+\beta)=D(\alpha)+D(\beta)` and `D(\alpha\beta)=\beta - D(\alpha)+\alpha D(\beta)` for all `\alpha,\beta\in R`. For a function + A derivation on `R` is a map `R \to R` with + `D(\alpha + \beta) = D(\alpha) + D(\beta)` and + `D(\alpha \beta) = \beta D(\alpha)+\alpha D(\beta)` + for all `\alpha, \beta \in R`. For a function field `K(x)` with `K` perfect, the derivations form a one-dimensional `K`-vector space generated by the extension of the usual derivation on `K[x]` (cf. Proposition 10 in [GT1996]_.) @@ -1472,11 +1473,12 @@ def derivation(self): An endofunction on this function field. - REFERENCES:: + REFERENCES: - .. [GT1996] Gianni, P., & Trager, B. (1996). Square-free algorithms in - positive characteristic. Applicable Algebra in Engineering, - Communication and Computing, 7(1), 1-14. + .. [GT1996] + Gianni, P., & Trager, B. (1996). Square-free algorithms in + positive characteristic. Applicable Algebra in Engineering, + Communication and Computing, 7(1), 1-14. EXAMPLES:: diff --git a/src/sage/rings/function_field/maps.py b/src/sage/rings/function_field/maps.py index d66f3363a2c..9981e345f04 100644 --- a/src/sage/rings/function_field/maps.py +++ b/src/sage/rings/function_field/maps.py @@ -6,7 +6,7 @@ - William Stein (2010): initial version - Julian Rueth (2011-09-14, 2014-06-23): refactored class hierarchy; added -derivation classes + derivation classes EXAMPLES:: From bdb1803badb326172dd310a79033ecbdb49904b6 Mon Sep 17 00:00:00 2001 From: Andrey Novoseltsev Date: Wed, 16 Jul 2014 16:57:13 -0600 Subject: [PATCH 533/546] Reorder arguments description. --- src/sage/geometry/polyhedron/base.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 6aa34303055..d5eddb6aa8d 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -448,12 +448,6 @@ def plot(self, INPUT: - - ``projection_direction`` -- coordinate list/tuple/iterable - or ``None`` (default). The direction to use for the - :meth:`schlegel_projection`` of the polytope. If not - specified, no projection is used in dimensions `< 4` and - parallel projection is used in dimension `4`. - - ``point``, ``line``, ``polygon`` -- Parameters to pass to point (0d), line (1d), and polygon (2d) plot commands. Allowed values are: @@ -472,7 +466,13 @@ def plot(self, objects in the dimension of the polytope (or of dimension 2 for higher dimensional polytopes) and ``wireframe`` is used for all lower-dimensional graphics objects - (default: 'green' for fill and 'blue' for wireframe) + (default: 'green' for ``fill`` and 'blue' for ``wireframe``) + + - ``projection_direction`` -- coordinate list/tuple/iterable + or ``None`` (default). The direction to use for the + :meth:`schlegel_projection`` of the polytope. If not + specified, no projection is used in dimensions `< 4` and + parallel projection is used in dimension `4`. - ``**kwds`` -- optional keyword parameters that are passed to all graphics objects. From 95dcda13e465a7af045dc7a55e07e7f1aa5bad34 Mon Sep 17 00:00:00 2001 From: drvinceknight Date: Thu, 17 Jul 2014 08:08:00 +0100 Subject: [PATCH 534/546] Fixed repetition of error for super additive --- src/sage/game_theory/cooperative_game.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 808e360b9d8..dcd4b9de1f1 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -103,7 +103,7 @@ class CooperativeGame(SageObject): A characteristic function game `G = (N, v)` is monotone if it satisfies `v(C_2) \geq v(C_1)` for all `C_1 \subseteq C_2`. A characteristic function game `G = (N, v)` is superadditive if it satisfies - `v(C_1 \cup C_2) \geq v(C_1) + v(C_2)` for all `C_1, C_2 \subseteq N` such + `v(C_1 \cup C_2) \geq v(C_1) + v(C_2)` for all `C_1, C_2 \subseteq 2^{\Omega}` such that `C_1 \cap C_2 = \emptyset`. We can test if a game is monotonic or superadditive. :: @@ -480,9 +480,10 @@ def is_superadditive(self): r""" Return ``True`` if ``self`` is superadditive. - A game `G = (N, v)` is superadditive if it satisfies - `v(C_2) \geq v(C_1)` for all `C_1 \subseteq C_2` such - that `C_1 \cap C_2 = \emptyset`. + A characteristic function game `G = (N, v)` is superadditive + if it satisfies `v(C_1 \cup C_2) \geq v(C_1) + v(C_2)` for + all `C_1, C_2 \subseteq 2^{\Omega}` such that `C_1 \cap C_2 + = \emptyset`. EXAMPLES: From d45f16bfc4bc4cb22a9c64615dccc97c8412ce78 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Flori Date: Thu, 17 Jul 2014 01:26:48 -0700 Subject: [PATCH 535/546] Let Singular build with clang. --- build/pkgs/singular/SPKG.txt | 3 +++ .../singular-3.1.6-no_return_type.patch | 22 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 build/pkgs/singular/patches/singular-3.1.6-no_return_type.patch diff --git a/build/pkgs/singular/SPKG.txt b/build/pkgs/singular/SPKG.txt index 1df072d061f..fc3aa67a530 100644 --- a/build/pkgs/singular/SPKG.txt +++ b/build/pkgs/singular/SPKG.txt @@ -53,6 +53,9 @@ See spkg-src. nomials. Collection / subset of upstream commits, latest being 28f4fe9464722511718050dfab7cd61d29898968. (Cf. #16462 and Singular trac #621.) + * singular-3.1.6-no_return_type.patch: + State return type of main functions so that Singular builds + with clang. Other notes * The option '--without-dynamic-kernel' is used on *all* diff --git a/build/pkgs/singular/patches/singular-3.1.6-no_return_type.patch b/build/pkgs/singular/patches/singular-3.1.6-no_return_type.patch new file mode 100644 index 00000000000..82ff99b3866 --- /dev/null +++ b/build/pkgs/singular/patches/singular-3.1.6-no_return_type.patch @@ -0,0 +1,22 @@ +--- latest/Singular/libparse.l 2012-12-19 22:01:16.000000000 +0100 ++++ latest/Singular/libparse.l 2014-05-17 18:24:37.707569492 +0200 +@@ -963,7 +963,7 @@ + } + + #ifdef STANDALONE_PARSER +-main( int argc, char *argv[] ) ++int main( int argc, char *argv[] ) + { + lib_style_types lib_style; + main_init(argc, argv); +--- latest/Singular/libparse.cc 2012-12-19 22:01:16.000000000 +0100 ++++ latest/Singular/libparse.cc 2014-05-17 18:45:54.543560513 +0200 +@@ -3504,7 +3504,7 @@ + } + + #ifdef STANDALONE_PARSER +-main( int argc, char *argv[] ) ++int main( int argc, char *argv[] ) + { + lib_style_types lib_style; + main_init(argc, argv); From 9c027241b60c176a3f106d903b16122c18c87e16 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Flori Date: Thu, 17 Jul 2014 03:08:40 -0700 Subject: [PATCH 536/546] Force upgrade of Singular and fix doctests on 64 bits. --- build/pkgs/singular/package-version.txt | 2 +- src/doc/en/constructions/algebraic_geometry.rst | 4 ++-- src/doc/en/developer/coding_in_other.rst | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/singular/package-version.txt b/build/pkgs/singular/package-version.txt index 4c600c1442e..14e03557af2 100644 --- a/build/pkgs/singular/package-version.txt +++ b/build/pkgs/singular/package-version.txt @@ -1 +1 @@ -3.1.6.p2 +3.1.6.p3 diff --git a/src/doc/en/constructions/algebraic_geometry.rst b/src/doc/en/constructions/algebraic_geometry.rst index 9966873979b..ecd32630ac5 100644 --- a/src/doc/en/constructions/algebraic_geometry.rst +++ b/src/doc/en/constructions/algebraic_geometry.rst @@ -144,8 +144,8 @@ Other methods sage: # Here you have all the points : sage: print L [1]: - _[1]=y^2+y+1 - _[2]=x+1 + _[1]=y + _[2]=x ... - Another way to compute rational points is to use Singular's diff --git a/src/doc/en/developer/coding_in_other.rst b/src/doc/en/developer/coding_in_other.rst index adbc1a8a5d6..f0cd6d0e80f 100644 --- a/src/doc/en/developer/coding_in_other.rst +++ b/src/doc/en/developer/coding_in_other.rst @@ -441,7 +441,7 @@ interface to Singular:: -2 # 64-bit [2]: 2 # 32-bit - 1 # 64-bit + -1 # 64-bit [3]: 1 ... From ef5ab0e906bbfc71421e3a6b6f0319e35d473fa1 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Thu, 17 Jul 2014 13:06:43 +0200 Subject: [PATCH 537/546] Enhance is_Markov_chain to handle symbolic probabilities When the probabilities of a Markov chain are variables of a polynomial ring, they may not sum up to one without additional information. Therefore, we now provide a parameter is_zero. --- src/sage/combinat/finite_state_machine.py | 54 ++++++++++++++++++++--- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 30b80ea6661..f94143e56c6 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -2781,21 +2781,26 @@ def __contains__(self, item): return False - def is_Markov_chain(self): + def is_Markov_chain(self, is_zero=None): """ Checks whether ``self`` is a Markov chain where the transition probabilities are modeled as input labels. INPUT: - Nothing. + - ``is_zero`` -- by default (``is_zero==None``), checking for + zero is simply done by + :meth:`~sage.structure.element.Element.is_zero`. This + parameter can be used to provide a more sophisticated check + for zero, e.g. in the case of symbolic probabilities, see + the examples below. OUTPUT: - True or False. + ``True`` or ``False``. - ``on_duplicate_transition`` must be - ``duplicate_transition_add_input`` and the sum of the input + :attr:`on_duplicate_transition` must be + :func:`duplicate_transition_add_input` and the sum of the input weights of the transitions leaving a state must add up to 1. EXAMPLES:: @@ -2807,7 +2812,8 @@ def is_Markov_chain(self): sage: F.is_Markov_chain() True - ``on_duplicate_transition`` must be ``duplicate_transition_add_input``:: + :attr:`on_duplicate_transition` must be + :func:`duplicate_transition_add_input`:: sage: F = Transducer([[0, 0, 1/4, 0], [0, 1, 3/4, 1], ....: [1, 0, 1/2, 0], [1, 1, 1/2, 1]]) @@ -2821,12 +2827,46 @@ def is_Markov_chain(self): ....: on_duplicate_transition=duplicate_transition_add_input) sage: F.is_Markov_chain() False + + If the probabilities are variables in the symbolic ring, + :func:`~sage.symbolic.assumptions.assume` will do the trick:: + + sage: var('p q') + (p, q) + sage: F = Transducer([(0, 0, p, 1), (0, 0, q, 0)], + ....: on_duplicate_transition=duplicate_transition_add_input) + sage: assume(p + q == 1) + sage: (p + q - 1).is_zero() + True + sage: F.is_Markov_chain() + True + sage: forget() + sage: del(p, q) + + If the probabilities are variables in some polynomial ring, + the parameter ``is_zero`` can be used:: + + sage: R. = PolynomialRing(QQ) + sage: def is_zero_polynomial(polynomial): + ....: return polynomial in (p + q - 1)*R + sage: F = Transducer([(0, 0, p, 1), (0, 0, q, 0)], + ....: on_duplicate_transition=duplicate_transition_add_input) + sage: F.is_Markov_chain() + False + sage: F.is_Markov_chain(is_zero_polynomial) + True """ + def default_is_zero(expression): + return expression.is_zero() + + is_zero_function = default_is_zero + if is_zero is not None: + is_zero_function = is_zero if self.on_duplicate_transition != duplicate_transition_add_input: return False - return all((sum(t.word_in[0] for t in state.transitions) - 1).is_zero() + return all(is_zero_function(sum(t.word_in[0] for t in state.transitions) - 1) for state in self.states()) From 46daf042155ac3a64dae90504513625d471710a0 Mon Sep 17 00:00:00 2001 From: Jan Keitel Date: Thu, 17 Jul 2014 15:00:21 +0200 Subject: [PATCH 538/546] Include labels if not lattice polytope is not fully-dimensional. --- src/sage/geometry/polyhedron/ppl_lattice_polytope.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/ppl_lattice_polytope.py b/src/sage/geometry/polyhedron/ppl_lattice_polytope.py index 84ea4479500..64f43c66b76 100644 --- a/src/sage/geometry/polyhedron/ppl_lattice_polytope.py +++ b/src/sage/geometry/polyhedron/ppl_lattice_polytope.py @@ -1066,9 +1066,18 @@ def lattice_automorphism_group(self, points=None, point_labels=None): ((0, 0), (1, 1), (1, 2), (2, 1), (2, 2), (3, 3)) sage: Z3square.lattice_automorphism_group(points, point_labels=(1,2,3,4,5,6)) Permutation Group with generators [(), (3,4), (1,6)(2,5), (1,6)(2,5)(3,4)] + + Point labels also work for lattice polytopes that are not + full-dimensional, see trac:`16669`:: + + sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL + sage: lp = LatticePolytope_PPL((1,0,0),(0,1,0),(-1,-1,0)) + sage: lp.lattice_automorphism_group(point_labels=(0,1,2)) + Permutation Group with generators [(), (1,2), (0,1), (0,1,2), (0,2,1), (0,2)] """ if not self.is_full_dimensional(): - return self.affine_lattice_polytope().lattice_automorphism_group() + return self.affine_lattice_polytope().lattice_automorphism_group( + point_labels=point_labels) if points is None: points = self.vertices() From 291e70b3d40026e59571ddb45c208e4ea1a1b3f1 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Thu, 17 Jul 2014 19:07:50 +0100 Subject: [PATCH 539/546] Trac 13331: fix doctests on 32 bits --- src/doc/en/constructions/algebraic_geometry.rst | 6 ++++-- src/doc/en/developer/coding_in_other.rst | 6 ++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/doc/en/constructions/algebraic_geometry.rst b/src/doc/en/constructions/algebraic_geometry.rst index ecd32630ac5..f7eff32e57a 100644 --- a/src/doc/en/constructions/algebraic_geometry.rst +++ b/src/doc/en/constructions/algebraic_geometry.rst @@ -144,8 +144,10 @@ Other methods sage: # Here you have all the points : sage: print L [1]: - _[1]=y - _[2]=x + _[1]=y+1 # 32-bit + _[2]=x+1 # 32-bit + _[1]=y # 64-bit + _[2]=x # 64-bit ... - Another way to compute rational points is to use Singular's diff --git a/src/doc/en/developer/coding_in_other.rst b/src/doc/en/developer/coding_in_other.rst index f0cd6d0e80f..ceb1709ae22 100644 --- a/src/doc/en/developer/coding_in_other.rst +++ b/src/doc/en/developer/coding_in_other.rst @@ -437,11 +437,9 @@ interface to Singular:: 0 [2]: [1]: - 2 # 32-bit - -2 # 64-bit + -2 [2]: - 2 # 32-bit - -1 # 64-bit + -1 [3]: 1 ... From dbddb31334c9d60e9e30cdb8a689e0e41b8f88e6 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 18 Jul 2014 10:53:16 +0200 Subject: [PATCH 540/546] make def logical_and, def logical_or inline (lambda) functions --- src/sage/combinat/finite_state_machine_generators.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/finite_state_machine_generators.py b/src/sage/combinat/finite_state_machine_generators.py index 9ca35facbf4..0d3b4f686a0 100644 --- a/src/sage/combinat/finite_state_machine_generators.py +++ b/src/sage/combinat/finite_state_machine_generators.py @@ -369,9 +369,8 @@ def all(self, input_alphabet, number_of_operands=2): sage: T3([(0, 0, 0), (1, 0, 0), (1, 1, 1)]) [False, False, True] """ - def logical_and(*args): - return all(args) - return self.operator(logical_and, input_alphabet, number_of_operands) + return self.operator(lambda *args: all(args), + input_alphabet, number_of_operands) def any(self, input_alphabet, number_of_operands=2): @@ -422,9 +421,9 @@ def any(self, input_alphabet, number_of_operands=2): sage: T3([(0, 0, 0), (1, 0, 0), (1, 1, 1)]) [False, True, True] """ - def logical_or(*args): - return any(args) - return self.operator(logical_or, input_alphabet, number_of_operands) + return self.operator(lambda *args: any(args), + input_alphabet, number_of_operands) + def add(self, input_alphabet): """ From 9fa937c8c6f38085ce36fffbaa70f8c6006248a9 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 18 Jul 2014 11:15:19 +0200 Subject: [PATCH 541/546] rewrote def multioperand_add to lambda-function --- src/sage/combinat/finite_state_machine_generators.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/finite_state_machine_generators.py b/src/sage/combinat/finite_state_machine_generators.py index 51e20da4266..e4d923c7a35 100644 --- a/src/sage/combinat/finite_state_machine_generators.py +++ b/src/sage/combinat/finite_state_machine_generators.py @@ -478,9 +478,8 @@ def add(self, input_alphabet, number_of_operands=2): sage: T3([(0, 0, 0), (0, 1, 0), (0, 1, 1), (1, 1, 1)]) [0, 1, 2, 3] """ - def multioperand_add(*args): - return sum(args) - return self.operator(multioperand_add, input_alphabet, + return self.operator(lambda *args: sum(args), + input_alphabet, number_of_operands=number_of_operands) From 93c23f665a21c3c0218da716ed3dc40c65b2eabb Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 18 Jul 2014 11:16:30 +0200 Subject: [PATCH 542/546] removed trac reference, since this does not give additional information; operators-->operands --- src/sage/combinat/finite_state_machine_generators.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/combinat/finite_state_machine_generators.py b/src/sage/combinat/finite_state_machine_generators.py index e4d923c7a35..501c47fa238 100644 --- a/src/sage/combinat/finite_state_machine_generators.py +++ b/src/sage/combinat/finite_state_machine_generators.py @@ -468,8 +468,7 @@ def add(self, input_alphabet, number_of_operands=2): sage: T([(0, 0), (0, 1), (1, 0), (1, 1)]) [0, 1, 1, 2] - More than two operators can also be handled, - cf. :trac:`16186`:: + More than two operands can also be handled:: sage: T3 = transducers.add([0, 1], number_of_operands=3) sage: T3.input_alphabet From 737041aacd4d8df0a6f7965e72c11a192275407a Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 18 Jul 2014 11:29:31 +0200 Subject: [PATCH 543/546] one Type, one ``...```, one PEP8 --- src/sage/combinat/finite_state_machine_generators.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/finite_state_machine_generators.py b/src/sage/combinat/finite_state_machine_generators.py index 80228948f95..283d44247f4 100644 --- a/src/sage/combinat/finite_state_machine_generators.py +++ b/src/sage/combinat/finite_state_machine_generators.py @@ -252,12 +252,12 @@ def Wait(self, input_alphabet, threshold=1): - ``input_alphabet`` -- a list or other iterable. - ``threshold`` -- a positive integer specifying how many - occurrences of True inputs are waited for. + occurrences of ``True`` inputs are waited for. OUTPUT: A transducer writing ``False`` until the ``threshold``-th true - (Python's standard conversion to booleanis used to convert the + (Python's standard conversion to boolean is used to convert the actual input to boolean) input is read. Subsequently, the transducer writes ``True``. @@ -275,7 +275,7 @@ def transition(state, input): return (threshold, True) if not input: return (state, False) - return (state+1, state + 1 == threshold) + return (state + 1, state + 1 == threshold) T = Transducer(transition, input_alphabet=input_alphabet, From 71246e4429e7454d81ba32217da0a6300b032e45 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 18 Jul 2014 12:18:13 +0200 Subject: [PATCH 544/546] in doc of input-parameter: is_zero=None (= instead of ==) --- src/sage/combinat/finite_state_machine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index f94143e56c6..36b96ef38c3 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -2788,7 +2788,7 @@ def is_Markov_chain(self, is_zero=None): INPUT: - - ``is_zero`` -- by default (``is_zero==None``), checking for + - ``is_zero`` -- by default (``is_zero=None``), checking for zero is simply done by :meth:`~sage.structure.element.Element.is_zero`. This parameter can be used to provide a more sophisticated check From 72706eb6697fba361e138ddaca0765f8415eb8f3 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Fri, 18 Jul 2014 16:25:03 +0100 Subject: [PATCH 545/546] Trac 12947: handle numerical noise correctly --- src/sage/interfaces/maxima_lib.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 039c75fc3cb..7848b94c388 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -760,8 +760,11 @@ def sr_integral(self,*args): The following integral was computed incorrectly in versions of Maxima before 5.27 (see :trac:`12947`):: - sage: integrate(x*cos(x^3),(x,0,1/2)).n() # abs tol 1e-16 + sage: a = integrate(x*cos(x^3),(x,0,1/2)).n() + sage: a.real() 0.124756040961038 + sage: a.imag().abs() < 3e-17 + True """ try: From 169b80c85f7dead04eb9428659a76539b06bf600 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sat, 19 Jul 2014 01:01:39 -0400 Subject: [PATCH 546/546] Updated Sage version to 6.3.beta6 --- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- src/bin/sage-banner | 2 +- src/bin/sage-version.sh | 4 ++-- src/sage/version.py | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index acf22e3ba21..e4fcb02a56f 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -Sage version 6.3.beta5, released 2014-07-01 +Sage version 6.3.beta6, released 2014-07-19 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 35794f11de9..fe665dab861 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=4d10c539769b8aa53c2bd97f9d9ae6b8a139d356 -md5=b9ea7956de216fc5e5965e17b14bab3f -cksum=2387138201 +sha1=3265062e02ba06ebe5897596eea08fb04cb3c554 +md5=f3a75a886044f86dab6565b399141bde +cksum=666076577 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 8f92bfdd497..7facc89938b 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -35 +36 diff --git a/src/bin/sage-banner b/src/bin/sage-banner index 16a7975da37..424effdf028 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ Sage Version 6.3.beta5, Release Date: 2014-07-01 │ +│ Sage Version 6.3.beta6, Release Date: 2014-07-19 │ │ Type "notebook()" for the browser-based notebook interface. │ │ Type "help()" for help. │ └────────────────────────────────────────────────────────────────────┘ diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 94875bbe5ba..7cd997b942a 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,4 +1,4 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='6.3.beta5' -SAGE_RELEASE_DATE='2014-07-01' +SAGE_VERSION='6.3.beta6' +SAGE_RELEASE_DATE='2014-07-19' diff --git a/src/sage/version.py b/src/sage/version.py index 72e854cfd25..2e7f4e794ce 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,4 +1,4 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '6.3.beta5' -date = '2014-07-01' +version = '6.3.beta6' +date = '2014-07-19'