From 62be17eb6eccffcddcc61e4f18a2123ec0c364d8 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sun, 25 May 2014 22:27:27 +0100 Subject: [PATCH 001/163] print_sorted and math_sorted utility functions --- src/sage/symbolic/comparison.pxd | 11 ++ src/sage/symbolic/comparison.pyx | 223 +++++++++++++++++++++++++++++++ src/sage/symbolic/expression.pxd | 1 + src/sage/symbolic/expression.pyx | 5 +- src/sage/symbolic/ginac.pxd | 4 - 5 files changed, 239 insertions(+), 5 deletions(-) create mode 100644 src/sage/symbolic/comparison.pxd create mode 100644 src/sage/symbolic/comparison.pyx diff --git a/src/sage/symbolic/comparison.pxd b/src/sage/symbolic/comparison.pxd new file mode 100644 index 00000000000..36a57d8635b --- /dev/null +++ b/src/sage/symbolic/comparison.pxd @@ -0,0 +1,11 @@ +from ginac cimport * +from sage.symbolic.expression cimport Expression + + +cpdef int print_order(lhs, rhs) except -2 +cdef int print_order_c(Expression lhs, Expression rhs) + +cpdef print_sorted(expression_list) + +# cpdef int math_order_c(Expression lhs, Expression rhs) except -2 + diff --git a/src/sage/symbolic/comparison.pyx b/src/sage/symbolic/comparison.pyx new file mode 100644 index 00000000000..c00d4355515 --- /dev/null +++ b/src/sage/symbolic/comparison.pyx @@ -0,0 +1,223 @@ +""" +Comparison of Symbolic Expressions + +There are two useful ways to compare symbolic expressions: + +* :func:`print_order` is how the terms are ordered. This is always + defined. If you need a fast comparison, this is it. + +* :func:`math_order` is the "mathematical" comparison. This may raise + an exception if the answer is unknown (to Sage) or cannot, in + principle, evaluated to a boolean (for example, if it involves + symbolic variables). Can be very slow as it potentially calls + Maxima to prove the inequality. +""" + +include "sage/ext/python.pxi" + +from sage.symbolic.ring import SR +from sage.symbolic.expression cimport is_Expression + + +cdef int print_order_c(Expression lhs, Expression rhs): + """ + Print comparison. + + See :meth:`print_order` for details. + """ + return print_order_compare((lhs)._gobj, (rhs)._gobj) + + +cpdef int print_order(lhs, rhs) except -2: + """ + Comparison in the print order + + INPUT: + + - ``lhs``, ``rhs`` -- two symbolic expressions or something that + can be converted to one. + + OUTPUT: + + Either `-1`, `0`, or `+1` indicating the comparison. An exception + is raised if the arguments cannot be converted into the symbolic + ring. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import print_order + sage: print_order(1, oo) + 1 + sage: print_order(e, oo) + -1 + sage: print_order(pi, oo) + 1 + sage: print_order(1, sqrt(2)) + 1 + + Known bug, see :trac:`12967` :: + + sage: cmp(SR(oo), sqrt(2)) + Traceback (most recent call last): + ... + RuntimeError: comparing typeid's + """ + if not is_Expression(lhs): + lhs = SR(lhs) + if not is_Expression(rhs): + rhs = SR(rhs) + return print_order_c(lhs, rhs) + + +class _print_key(object): + + def __init__(self, ex): + """ + Sort key to sort in print order. + + INPUT: + + - ``ex`` -- symbolic expression or something that can be + converted into one. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import _print_key + sage: _print_key(1) + + """ + self.ex = ex if is_Expression(ex) else SR(ex) + + def __lt__(self, other): + """ + Implement "less than" to make the key comparable. + + INPUT: + + - ``other`` -- another :class:`_print_key` instance. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import print_order, _print_key + sage: print_order(1, 2) + -1 + sage: _print_key(1) < _print_key(2) + True + sage: print_order(1, sqrt(2)) + 1 + sage: _print_key(1) < _print_key(sqrt(2)) + False + """ + return print_order_c(self.ex, other.ex) < 0 + + +cpdef print_sorted(expressions): + """ + Sort a list in print order + + INPUT: + + - ``expressions`` -- a list/tuple/iterable of symbolic + expressions, or something that can be converted to one. + + OUTPUT: + + The list sorted by :meth:`print_order`. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import print_sorted + sage: print_sorted([SR(1), SR(e), SR(pi), sqrt(2)]) + [e, sqrt(2), pi, 1] + """ + return sorted(expressions, key=_print_key) + + +class _math_key(object): + + def __init__(self, ex): + """ + Sort key to sort in "Mathematics" order. + + INPUT: + + - ``ex`` -- symbolic expression or something that can be + converted into one. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import _math_key + sage: _math_key(1) + + """ + self.ex = ex if is_Expression(ex) else SR(ex) + + def __lt__(self, other): + """ + Implement "less than" to make the key comparable. + + INPUT: + + - ``other`` -- another :class:`_print_key` instance. + + OUTPUT: + + Boolean. A ``ValueError`` is raised if we do not know how to + perform the comparison. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import _math_key + sage: _math_key(1) < _math_key(2) + True + sage: _math_key(1) < _math_key(sqrt(2)) + True + + Known bug, see :trac:`12967` :: + + sage: _math_key(1) < _math_key(oo) + Traceback (most recent call last): + ... + ValueError: cannot compare 1 and +Infinity + """ + less_than = bool(self.ex < other.ex) + greater_than = bool(self.ex > other.ex) + if less_than: + if not greater_than: + return True + else: + assert False # unreachable + else: + if greater_than: + return False + else: + raise ValueError('cannot compare {0} and {1}'.format(self.ex, other.ex)) + + +cpdef math_sorted(expressions): + """ + Sort a list of symbolic numbers in the "Mathematics" order + + INPUT: + + - ``expressions`` -- a list/tuple/iterable of symbolic + expressions, or something that can be converted to one. + + OUTPUT: + + The list sorted by ascending (real) value. If an entry does not + define a real value (or plus/minus infinity), or if the comparison + is not known, a ``ValueError`` is raised. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import math_sorted + sage: math_sorted([SR(1), SR(e), SR(pi), sqrt(2)]) + [e, sqrt(2), pi, 1] + """ + return sorted(expressions, key=_math_key) + diff --git a/src/sage/symbolic/expression.pxd b/src/sage/symbolic/expression.pxd index c5390ef72bc..6df0fab4f91 100644 --- a/src/sage/symbolic/expression.pxd +++ b/src/sage/symbolic/expression.pxd @@ -1,5 +1,6 @@ from ginac cimport * +cdef class Expression from sage.structure.element cimport CommutativeRingElement cdef class Expression(CommutativeRingElement): diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 28c7275339e..59826ee8b72 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -145,6 +145,7 @@ from sage.structure.element cimport ModuleElement, RingElement, Element from sage.symbolic.getitem cimport OperandsWrapper from sage.symbolic.complexity_measures import string_length from sage.symbolic.function import get_sfunction_from_serial, SymbolicFunction +cimport sage.symbolic.comparison from sage.rings.rational import Rational # Used for sqrt. from sage.misc.derivative import multi_derivative from sage.rings.infinity import AnInfinity, infinity, minus_infinity, unsigned_infinity @@ -152,6 +153,7 @@ from sage.misc.decorators import rename_keyword from sage.misc.superseded import deprecated_function_alias from sage.structure.dynamic_class import dynamic_class + # a small overestimate of log(10,2) LOG_TEN_TWO_PLUS_EPSILON = 3.321928094887363 @@ -3061,6 +3063,7 @@ cdef class Expression(CommutativeRingElement): sage: t.subs(x=I*x).subs(x=0).is_positive() False """ + #print 'cmp' return (left)._cmp(right) cdef int _cmp_c_impl(left, Element right) except -2: @@ -3110,7 +3113,7 @@ cdef class Expression(CommutativeRingElement): TypeError: Argument 'right' has incorrect type (expected sage.symbolic.expression.Expression, got sage.rings.integer.Integer) """ - return print_order_compare(left._gobj, right._gobj) + return sage.symbolic.comparison.print_order_c(left, right) cpdef int _cmp_mul(Expression left, Expression right) except -2: """ diff --git a/src/sage/symbolic/ginac.pxd b/src/sage/symbolic/ginac.pxd index 29aef54e8b2..ee702da70c9 100644 --- a/src/sage/symbolic/ginac.pxd +++ b/src/sage/symbolic/ginac.pxd @@ -559,7 +559,3 @@ cdef extern from "pynac/order.h": (GEx left, GEx right) except + bint print_order_compare_mul "GiNaC::print_order_mul().compare" \ (GEx left, GEx right) except + - bint print_order "GiNaC::print_order()" \ - (GEx left, GEx right) except + - bint print_order_mul "GiNaC::print_order_mul()" \ - (GEx left, GEx right) except + From af5f800fdc616022d503b5e845ec81ebfef7c25e Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Fri, 5 Sep 2014 18:21:32 +0200 Subject: [PATCH 002/163] 16397: use print_sorted() in two further places; fix doctest --- src/sage/symbolic/comparison.pyx | 2 +- src/sage/symbolic/expression.pyx | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/symbolic/comparison.pyx b/src/sage/symbolic/comparison.pyx index c00d4355515..3f3d0782bd4 100644 --- a/src/sage/symbolic/comparison.pyx +++ b/src/sage/symbolic/comparison.pyx @@ -217,7 +217,7 @@ cpdef math_sorted(expressions): sage: from sage.symbolic.comparison import math_sorted sage: math_sorted([SR(1), SR(e), SR(pi), sqrt(2)]) - [e, sqrt(2), pi, 1] + [1, sqrt(2), e, pi] """ return sorted(expressions, key=_math_key) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 6ffc12e9db0..37e03026aaa 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -4052,6 +4052,7 @@ cdef class Expression(CommutativeRingElement): sage: ((x^y)^z).find(w0^w1) [(x^y)^z] """ + from sage.symbolic.comparison import print_sorted cdef Expression p = self.coerce_in(pattern) cdef GExList found self._gobj.find(p._gobj, found) @@ -4060,7 +4061,7 @@ cdef class Expression(CommutativeRingElement): while itr.is_not_equal(found.end()): res.append(new_Expression_from_GEx(self._parent, itr.obj())) itr.inc() - res.sort(cmp) + res = print_sorted(res) return res def has(self, pattern): @@ -4412,6 +4413,7 @@ cdef class Expression(CommutativeRingElement): """ from sage.symbolic.ring import SR + from sage.symbolic.comparison import print_sorted cdef GExSet sym_set g_list_symbols(self._gobj, sym_set) res = [] @@ -4419,7 +4421,7 @@ cdef class Expression(CommutativeRingElement): while itr.is_not_equal(sym_set.end()): res.append(new_Expression_from_GEx(SR, itr.obj())) itr.inc() - res.sort(cmp=lambda x,y: -cmp(x,y)) + res = print_sorted(res)[::-1] return tuple(res) def arguments(self): From 98b7ca12cfe9335f9c871eda34ff9dc2ce03d3cf Mon Sep 17 00:00:00 2001 From: Bruno Grenet Date: Sun, 13 Jul 2014 20:09:52 +0200 Subject: [PATCH 003/163] Rebased on 6.9beta1 --- .../rings/polynomial/polynomial_element.pyx | 36 +++++++++++++++---- .../polynomial/polynomial_element_generic.py | 15 +++++--- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 85bd0e705c9..91bfcf21710 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -7913,8 +7913,13 @@ cdef class Polynomial_generic_dense(Polynomial): 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. + Raises ZerodivisionError if ``other`` is zero. Raises ArithmeticError if the division is not exact. + + AUTHORS: + + - Kwankyu Lee (2013-06-02) + + - Bruno Grenet (2014-07-13) EXAMPLES:: @@ -7929,17 +7934,31 @@ cdef class Polynomial_generic_dense(Polynomial): sage: f.quo_rem(g) Traceback (most recent call last): ... - ArithmeticError: Nonunit leading coefficient + ArithmeticError: Division non exact (consider coercing to polynomials over the fraction field) sage: g = 0 sage: f.quo_rem(g) Traceback (most recent call last): ... ZeroDivisionError: Division by zero polynomial + + TESTS:: + + The following shows that :trac:`16649` is indeed fixed. :: + + sage: P. = QQ[] + sage: R. = P[] + sage: f = (2*x^3+1)*y^2 + (x^2-x+3)*y + (3*x+2) + sage: g = (-1/13*x^2 - x)*y^2 + (-x^2 + 3*x - 155/4)*y - x - 1 + sage: h = f * g + sage: h.quo_rem(f) + ((-1/13*x^2 - x)*y^2 + (-x^2 + 3*x - 155/4)*y - x - 1, 0) + sage: h += (2/3*x^2-3*x+1)*y + 7/17*x+6/5 + sage: q,r = h.quo_rem(f) + sage: h == q*f + r and r.degree() < f.degree() + True """ 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 @@ -7953,8 +7972,11 @@ cdef class Polynomial_generic_dense(Polynomial): quo = list() for k from m-n >= k >= 0: - q = x[n+k-1]/y[n-1] - x[n+k-1] = R.zero() + try: + q = R(x[n+k-1]/y[n-1]) + except TypeError: + raise ArithmeticError("Division non exact (consider coercing to polynomials over the fraction field)") + x[n+k-1] = R.zero_element() for j from n+k-2 >= j >= k: x[j] -= q * y[j-k] quo.insert(0,q) diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index 7c261dc649a..6031612e32b 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -695,14 +695,18 @@ def quo_rem(self, other): sage: g == f*q + r and r.degree() < f.degree() True + The following shows that :trac:`16649` is indeed fixed. :: + + sage: P. = PolynomialRing(ZZ, sparse=True) + sage: (4*x).quo_rem(2*x) + (2, 0) + AUTHORS: - Bruno Grenet (2014-07-09) """ 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 @@ -714,11 +718,12 @@ def quo_rem(self, other): quo = R.zero() rem = self - inv_lc = R.base_ring().one()/other.leading_coefficient() while rem.degree() >= d: - - c = rem.leading_coefficient()*inv_lc + try: + c = R(rem.leading_coefficient() * ~other.leading_coefficient()) + except TypeError: + raise ArithmeticError("Division non exact (consider coercing to polynomials over the fraction field)") e = rem.degree() - d quo += c*R.one().shift(e) # we know that the leading coefficient of rem vanishes From 6d0da248d4151d354a47e0a407aa3530dd008e77 Mon Sep 17 00:00:00 2001 From: Bruno Grenet Date: Fri, 7 Aug 2015 17:56:55 +0200 Subject: [PATCH 004/163] One last zero_element removed + correct one doctest --- src/sage/rings/polynomial/polynomial_element.pyx | 2 +- src/sage/rings/polynomial/polynomial_element_generic.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 91bfcf21710..acca61415c7 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -7976,7 +7976,7 @@ cdef class Polynomial_generic_dense(Polynomial): q = R(x[n+k-1]/y[n-1]) except TypeError: raise ArithmeticError("Division non exact (consider coercing to polynomials over the fraction field)") - x[n+k-1] = R.zero_element() + x[n+k-1] = R.zero() for j from n+k-2 >= j >= k: x[j] -= q * y[j-k] quo.insert(0,q) diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index 6031612e32b..7715af23ae3 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -666,7 +666,7 @@ def quo_rem(self, other): sage: f.quo_rem(g) Traceback (most recent call last): ... - ArithmeticError: Nonunit leading coefficient + ArithmeticError: Division non exact (consider coercing to polynomials over the fraction field) sage: g = 0 sage: f.quo_rem(g) Traceback (most recent call last): From dbfb5186d55ad1682014ffc92907fa1af9fc488f Mon Sep 17 00:00:00 2001 From: Bruno Grenet Date: Fri, 7 Aug 2015 18:24:58 +0200 Subject: [PATCH 005/163] Remove useless colons --- src/sage/rings/polynomial/polynomial_element.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index acca61415c7..076cfdfb4d2 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -7941,7 +7941,7 @@ cdef class Polynomial_generic_dense(Polynomial): ... ZeroDivisionError: Division by zero polynomial - TESTS:: + TESTS: The following shows that :trac:`16649` is indeed fixed. :: From f4d7ffc4e5499913919ec6c8efe4140b91502134 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Tue, 25 Aug 2015 16:27:13 +0900 Subject: [PATCH 006/163] libSingular functions' ring parameter defaults to the last used ring. --- src/sage/libs/singular/decl.pxd | 1 + src/sage/libs/singular/function.pyx | 117 ++++++++++++--------- src/sage/libs/singular/function_factory.py | 2 +- src/sage/libs/singular/singular.pyx | 2 +- 4 files changed, 70 insertions(+), 52 deletions(-) diff --git a/src/sage/libs/singular/decl.pxd b/src/sage/libs/singular/decl.pxd index 37cc222c852..4fca93d9c59 100644 --- a/src/sage/libs/singular/decl.pxd +++ b/src/sage/libs/singular/decl.pxd @@ -372,6 +372,7 @@ cdef extern from "libsingular.h": cdef ring *currRing cdef ideal *currQuotient + # omalloc bin for numbers cdef omBin *rnumber_bin diff --git a/src/sage/libs/singular/function.pyx b/src/sage/libs/singular/function.pyx index 4421d269ff7..311093089f8 100644 --- a/src/sage/libs/singular/function.pyx +++ b/src/sage/libs/singular/function.pyx @@ -485,7 +485,6 @@ def all_vectors(s): return False return True - cdef class Converter(SageObject): """ A :class:`Converter` interfaces between Sage objects and Singular @@ -1153,11 +1152,16 @@ cdef class KernelCallHandler(BaseCallHandler): """ return True +# The Sage ring used in the last call of SingularFunction instances. +cdef object last_ring + cdef class SingularFunction(SageObject): """ The base class for Singular functions either from the kernel or from the library. """ + + def __init__(self, name): """ INPUT: @@ -1170,6 +1174,8 @@ cdef class SingularFunction(SageObject): sage: SingularFunction('foobar') foobar (singular function) """ + global last_ring + self._name = name global currRingHdl if currRingHdl == NULL: @@ -1177,6 +1183,9 @@ cdef class SingularFunction(SageObject): if currRingHdl == NULL: currRingHdl = enterid("my_awesome_sage_ring", 0, RING_CMD, &IDROOT, 1) currRingHdl.data.uring.ref += 1 + if last_ring == None: + from sage.all import QQ, PolynomialRing + last_ring = PolynomialRing(QQ,2,names='x,y') # seems a reasonable default cdef BaseCallHandler get_call_handler(self): """ @@ -1203,19 +1212,23 @@ cdef class SingularFunction(SageObject): def __call__(self, *args, ring=None, bint interruptible=True, attributes=None): """ Call this function with the provided arguments ``args`` in the - ring ``R``. + ``ring``. INPUT: - - ``args`` - a list of arguments - - ``ring`` - a multivariate polynomial ring - - ``interruptible`` - if ``True`` pressing Ctrl-C during the + - ``args`` -- a list of arguments + - ``ring`` -- a multivariate polynomial ring + - ``interruptible`` -- if ``True`` pressing Ctrl-C during the execution of this function will interrupt the computation (default: ``True``) - - ``attributes`` - a dictionary of optional Singular + - ``attributes`` -- a dictionary of optional Singular attributes assigned to Singular objects (default: ``None``) + If ``ring`` is not specified, it is guessed from the given arguments. + If this is not possible, then the ring in the last call of a Singular + function is used. + EXAMPLE:: sage: from sage.libs.singular.function import singular_function @@ -1228,19 +1241,17 @@ cdef class SingularFunction(SageObject): sage: size(2, ring=P) 1 sage: size(2) - Traceback (most recent call last): - ... - ValueError: Could not detect ring. - sage: size(Ideal([a*b + c, a + 1]), ring=P) + 1 + sage: size(Ideal([a*b + c, a + 1])) 2 sage: size(Ideal([a*b + c, a + 1])) 2 - sage: size(1,2, ring=P) + sage: size(1,2) Traceback (most recent call last): ... RuntimeError: Error in Singular function call 'size': Wrong number of arguments - sage: size('foobar', ring=P) + sage: size('foobar') 6 Show the usage of the optional ``attributes`` parameter:: @@ -1296,11 +1307,19 @@ cdef class SingularFunction(SageObject): sage: triangL(G,attributes={G:{'isSB':1}}) [[e + d + c + b + a, ...]] """ + global last_ring + if ring is None: ring = self.common_ring(args, ring) - if not (isinstance(ring, MPolynomialRing_libsingular) or \ - isinstance(ring, NCPolynomialRing_plural)): - raise TypeError("Cannot call Singular function '%s' with ring parameter of type '%s'"%(self._name,type(ring))) + if ring is None: + ring = last_ring + else: + if isinstance(ring, MPolynomialRing_libsingular) or isinstance(ring, NCPolynomialRing_plural): + last_ring = ring + else: + raise TypeError("Cannot call Singular function '%s' with ring parameter of type '%s'"%(self._name,type(ring))) + else: + last_ring = ring return call_function(self, args, ring, interruptible, attributes) def _sage_doc_(self): @@ -1320,27 +1339,30 @@ function '%s'. This wrapper takes care of converting Sage datatypes to Singular datatypes and vice versa. In addition to whatever parameters the -underlying Singular function accepts when called this function also +underlying Singular function accepts when called, this function also accepts the following keyword parameters: INPUT: -- ``args`` - a list of arguments -- ``ring`` - a multivariate polynomial ring -- ``interruptible`` - if ``True`` pressing Ctrl-C during the - execution of this function will - interrupt the computation (default: ``True``) -- ``attributes`` - a dictionary of optional Singular - attributes assigned to Singular objects (default: ``None``) +- ``args`` -- a list of arguments +- ``ring`` -- a multivariate polynomial ring +- ``interruptible`` -- if ``True`` pressing Ctrl-C during the + execution of this function will interrupt the computation + (default: ``True``) +- ``attributes`` -- a dictionary of optional Singular attributes + assigned to Singular objects (default: ``None``) + +If ``ring`` is not specified, it is guessed from the given arguments. +If this is not possible, then the ring in the last call of a Singular +function is used. -EXAMPLE:: +EXAMPLES:: sage: groebner = sage.libs.singular.function_factory.ff.groebner sage: P. = PolynomialRing(QQ) sage: I = P.ideal(x^2-y, y+x) sage: groebner(I) [x + y, y^2 - y] - sage: triangL = sage.libs.singular.function_factory.ff.triang__lib.triangL sage: P. = PolynomialRing(QQ, order='lex') sage: f1 = 1/2*((x1^2 + 2*x1 - 4)*x2^2 + 2*(x1^2 + x1)*x2 + x1^2) @@ -1368,12 +1390,12 @@ The Singular documentation for '%s' is given below. If ``ring`` is not ``None`` this routine checks whether it is the parent/ring of all members of ``args`` instead. - If no common ring was found a ``ValueError`` is raised. + If no common ring was found, None is returned. INPUT: - - ``args`` - a list of Python objects - - ``ring`` - an optional ring to check + - ``args`` -- a list of Python objects + - ``ring`` -- an optional ring to check """ from sage.matrix.matrix_mpolynomial_dense import Matrix_mpolynomial_dense from sage.matrix.matrix_integer_dense import Matrix_integer_dense @@ -1412,8 +1434,6 @@ The Singular documentation for '%s' is given below. ring = ring2 elif ring is not ring2: raise ValueError("Rings do not match up.") - if ring is None: - raise ValueError("Could not detect ring.") return ring def __reduce__(self): @@ -1613,11 +1633,11 @@ def singular_function(name): sage: std(I) [3*y - 8*z - 4, 4*x + 1] sage: size = singular_function("size") - sage: size([2, 3, 3], ring=P) + sage: size([2, 3, 3]) 3 - sage: size("sage", ring=P) + sage: size("sage") 4 - sage: size(["hello", "sage"], ring=P) + sage: size(["hello", "sage"]) 2 sage: factorize = singular_function("factorize") sage: factorize(f) @@ -1627,7 +1647,7 @@ def singular_function(name): We give a wrong number of arguments:: - sage: factorize(ring=P) + sage: factorize() Traceback (most recent call last): ... RuntimeError: Error in Singular function call 'factorize': @@ -1647,13 +1667,13 @@ def singular_function(name): arguments:: sage: singular_list = singular_function("list") - sage: singular_list(2, 3, 6, ring=P) + sage: singular_list(2, 3, 6) [2, 3, 6] - sage: singular_list(ring=P) + sage: singular_list() [] - sage: singular_list(1, ring=P) + sage: singular_list(1) [1] - sage: singular_list(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ring=P) + sage: singular_list(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] We try to define a non-existing function:: @@ -1668,9 +1688,9 @@ def singular_function(name): sage: from sage.libs.singular.function import lib as singular_lib sage: singular_lib('general.lib') sage: number_e = singular_function('number_e') - sage: number_e(10r,ring=P) + sage: number_e(10r) 67957045707/25000000000 - sage: RR(number_e(10r,ring=P)) + sage: RR(number_e(10r)) 2.71828182828000 :: @@ -1688,7 +1708,7 @@ def singular_function(name): sage: l [0, ['x', 'y', 'z'], [['dp', (1, 1, 1)], ['C', (0,)]], [0]] sage: ring=singular_function("ring") - sage: ring(l, ring=P) + sage: ring(l) sage: matrix = Matrix(P,2,2) sage: matrix.randomize(terms=1) @@ -1699,12 +1719,11 @@ def singular_function(name): sage: coeffs(x*y+y+1,y) [ 1] [x + 1] - sage: F. = GF(3)[] sage: intmat = Matrix(ZZ, 2,2, [100,2,3,4]) - sage: det(intmat, ring=F) + sage: det(intmat) 394 sage: random = singular_function("random") - sage: A = random(10,2,3, ring =F); A.nrows(), max(A.list()) <= 10 + sage: A = random(10,2,3); A.nrows(), max(A.list()) <= 10 (2, True) sage: P. = PolynomialRing(QQ) sage: M=P**3 @@ -1747,7 +1766,7 @@ def singular_function(name): doctest... sage: M [(x + y, x*y)] - sage: syz(M, ring=P) + sage: syz(M) [(0)] sage: mres(I, 0) @@ -1763,16 +1782,14 @@ def singular_function(name): sage: l = ringlist(P) sage: len(l) 6 - sage: ring(l, ring=P) + sage: ring(l) sage: I=twostd(I) sage: l[3]=I - sage: ring(l, ring=P) + sage: ring(l) - """ - cdef SingularFunction fnc try: return SingularKernelFunction(name) except NameError: @@ -1784,7 +1801,7 @@ def lib(name): INPUT: - - ``name`` - a Singular library name + - ``name`` -- a Singular library name EXAMPLE:: diff --git a/src/sage/libs/singular/function_factory.py b/src/sage/libs/singular/function_factory.py index 6d3169a8ff3..ea489b0d1dc 100644 --- a/src/sage/libs/singular/function_factory.py +++ b/src/sage/libs/singular/function_factory.py @@ -1,5 +1,5 @@ """ -libSingular: Function Factory. +libSingular: Function Factory AUTHORS: diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index 8401b9d3c4e..f3842e0307a 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -1,5 +1,5 @@ """ -libSingular conversion routines and initialisation. +libSingular: Conversion Routines and Initialisation AUTHOR: From 85f2d849c50d40c4119c80df7db543a5127b476d Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Fri, 4 Sep 2015 13:23:04 +0900 Subject: [PATCH 007/163] Dummy ring is now fixed. --- src/sage/libs/singular/function.pyx | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/sage/libs/singular/function.pyx b/src/sage/libs/singular/function.pyx index 311093089f8..8f99cb34cd5 100644 --- a/src/sage/libs/singular/function.pyx +++ b/src/sage/libs/singular/function.pyx @@ -1152,8 +1152,8 @@ cdef class KernelCallHandler(BaseCallHandler): """ return True -# The Sage ring used in the last call of SingularFunction instances. -cdef object last_ring +# The Sage ring used as a dummy for singular function calls. +cdef object dummy_ring cdef class SingularFunction(SageObject): """ @@ -1174,8 +1174,6 @@ cdef class SingularFunction(SageObject): sage: SingularFunction('foobar') foobar (singular function) """ - global last_ring - self._name = name global currRingHdl if currRingHdl == NULL: @@ -1183,9 +1181,6 @@ cdef class SingularFunction(SageObject): if currRingHdl == NULL: currRingHdl = enterid("my_awesome_sage_ring", 0, RING_CMD, &IDROOT, 1) currRingHdl.data.uring.ref += 1 - if last_ring == None: - from sage.all import QQ, PolynomialRing - last_ring = PolynomialRing(QQ,2,names='x,y') # seems a reasonable default cdef BaseCallHandler get_call_handler(self): """ @@ -1226,8 +1221,8 @@ cdef class SingularFunction(SageObject): attributes assigned to Singular objects (default: ``None``) If ``ring`` is not specified, it is guessed from the given arguments. - If this is not possible, then the ring in the last call of a Singular - function is used. + If this is not possible, then a dummy ring, univariate polynomial ring + over ``QQ``, is used. EXAMPLE:: @@ -1307,19 +1302,20 @@ cdef class SingularFunction(SageObject): sage: triangL(G,attributes={G:{'isSB':1}}) [[e + d + c + b + a, ...]] """ - global last_ring + global dummy_ring if ring is None: ring = self.common_ring(args, ring) if ring is None: - ring = last_ring + if dummy_ring is None: + from sage.all import QQ, PolynomialRing + dummy_ring = PolynomialRing(QQ,"dummy",1) # seems a reasonable default dummy_ring = + ring = dummy_ring else: if isinstance(ring, MPolynomialRing_libsingular) or isinstance(ring, NCPolynomialRing_plural): last_ring = ring else: raise TypeError("Cannot call Singular function '%s' with ring parameter of type '%s'"%(self._name,type(ring))) - else: - last_ring = ring return call_function(self, args, ring, interruptible, attributes) def _sage_doc_(self): @@ -1353,8 +1349,8 @@ INPUT: assigned to Singular objects (default: ``None``) If ``ring`` is not specified, it is guessed from the given arguments. -If this is not possible, then the ring in the last call of a Singular -function is used. +If this is not possible, then a dummy ring, univariate polynomial ring +over ``QQ``, is used. EXAMPLES:: From a4b8329a63d48ed197a5bc1e1006a712f7d2802c Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Fri, 4 Sep 2015 13:53:11 +0900 Subject: [PATCH 008/163] Corrected errors --- src/sage/libs/singular/function.pyx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/sage/libs/singular/function.pyx b/src/sage/libs/singular/function.pyx index 8f99cb34cd5..cddd01a602a 100644 --- a/src/sage/libs/singular/function.pyx +++ b/src/sage/libs/singular/function.pyx @@ -1311,11 +1311,8 @@ cdef class SingularFunction(SageObject): from sage.all import QQ, PolynomialRing dummy_ring = PolynomialRing(QQ,"dummy",1) # seems a reasonable default dummy_ring = ring = dummy_ring - else: - if isinstance(ring, MPolynomialRing_libsingular) or isinstance(ring, NCPolynomialRing_plural): - last_ring = ring - else: - raise TypeError("Cannot call Singular function '%s' with ring parameter of type '%s'"%(self._name,type(ring))) + if not (isinstance(ring, MPolynomialRing_libsingular) or isinstance(ring, NCPolynomialRing_plural)): + raise TypeError("Cannot call Singular function '%s' with ring parameter of type '%s'"%(self._name,type(ring))) return call_function(self, args, ring, interruptible, attributes) def _sage_doc_(self): From 7a168a2e9d1b8b4a4607121cf5c80e3fb77d7368 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Fri, 4 Sep 2015 13:59:44 +0900 Subject: [PATCH 009/163] Corrected one more error --- src/sage/libs/singular/function.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/libs/singular/function.pyx b/src/sage/libs/singular/function.pyx index cddd01a602a..69e3392f5b8 100644 --- a/src/sage/libs/singular/function.pyx +++ b/src/sage/libs/singular/function.pyx @@ -1309,7 +1309,7 @@ cdef class SingularFunction(SageObject): if ring is None: if dummy_ring is None: from sage.all import QQ, PolynomialRing - dummy_ring = PolynomialRing(QQ,"dummy",1) # seems a reasonable default dummy_ring = + dummy_ring = PolynomialRing(QQ,"dummy",1) # seems a reasonable default ring = dummy_ring if not (isinstance(ring, MPolynomialRing_libsingular) or isinstance(ring, NCPolynomialRing_plural)): raise TypeError("Cannot call Singular function '%s' with ring parameter of type '%s'"%(self._name,type(ring))) From 6b6aa4748b85f4e05fbf207436726e498b5eeb72 Mon Sep 17 00:00:00 2001 From: Adrien Boussicault Date: Wed, 21 Oct 2015 18:19:36 +0200 Subject: [PATCH 010/163] Add implementation of Hook statistic --- src/sage/combinat/binary_tree.py | 76 ++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/sage/combinat/binary_tree.py b/src/sage/combinat/binary_tree.py index 74bf98c99af..a455d47f2ce 100644 --- a/src/sage/combinat/binary_tree.py +++ b/src/sage/combinat/binary_tree.py @@ -2035,6 +2035,82 @@ def single_edge_cut_shapes(self): resu += [(L + 1, L + 2, R)] return resu + def comb(self, side=0): + r''' + Return the comb of a tree. + + There are two combs in a binary tree : a left comb and a right comb. + Consider all the vertices of the leftmost (resp. rightmost) branch of + the root. The left (resp. right) comb is the list of right (resp. left) + subtree of each of these vertices. + + INPUT:: + - ``side`` -- set to 0 to obtain a left comb, and to 1 to obain + a right comb. + + OUTPUT:: + A list of binary trees. + + EXAMPLES:: + + sage: BT = BinaryTree( '.' ) + sage: [BT.comb(0), BT.comb(1)] + [[], []] + sage: BT = BinaryTree( '[.,.]' ) + sage: [BT.comb(0), BT.comb(1)] + [[], []] + sage: BT = BinaryTree( '[[[.,.], .], [.,.]]' ) + sage: BT.comb(0) + [., .] + sage: BT.comb(1) + [.] + sage: BT = BinaryTree( '[[[[., [., .]], .], [[., .], [[[., .], [., .]], [., .]]]], [., [[[., .], [[[., .], [., .]], .]], .]]]' ) + sage: BT.comb(0) + [[[., .], [[[., .], [., .]], [., .]]], ., [., .]] + sage: BT.comb(1) + [., [[., .], [[[., .], [., .]], .]]] + ''' + if self.is_empty(): + return [] + tree = self[side] + res = [] + while not tree.is_empty(): + res.append(tree[1-side]) + tree = tree[side] + return res + + def hook_number(self): + """ + Return the number of hooks. + + The hook of a vertex v is the union of {v}, its leftmost and + rightmost branches. + + There is a unique way to partition the vertices in hooks. + The number of hooks in such a partition is the hook number of the tree. + + We can obtain this partition recursively by extracting the root's hook + and iterating the processus on each tree of the remaining forest. + + EXAMPLES:: + + sage: BT = BinaryTree( '.' ) + sage: BT.hook_number() + 0 + sage: BT = BinaryTree( '[.,.]' ) + sage: BT.hook_number() + 1 + sage: BT = BinaryTree( '[[[.,.], .], [.,.]]' ) + sage: BT.hook_number() + 1 + sage: BT = BinaryTree( '[[[[., [., .]], .], [[., .], [[[., .], [., .]], [., .]]]], [., [[[., .], [[[., .], [., .]], .]], .]]]' ) + sage: BT.hook_number() + 6 + """ + if self.is_empty(): + return 0 + return 1 + sum(t.hook_number() for t in self.comb(0) + self.comb(1)) + def q_hook_length_fraction(self, q=None, q_factor=False): r""" Compute the ``q``-hook length fraction of the binary tree ``self``, From 4ed1b4d305503b52840badb59fc6cc25ffd0dbaa Mon Sep 17 00:00:00 2001 From: Adrien Boussicault Date: Thu, 22 Oct 2015 15:22:49 +0200 Subject: [PATCH 011/163] Update documentation of hook_number() and comb() --- src/sage/combinat/binary_tree.py | 50 ++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/src/sage/combinat/binary_tree.py b/src/sage/combinat/binary_tree.py index a455d47f2ce..8c888a03497 100644 --- a/src/sage/combinat/binary_tree.py +++ b/src/sage/combinat/binary_tree.py @@ -13,6 +13,7 @@ AUTHORS: - Florent Hivert (2010-2011): initial implementation. +- Adrien Boussicault (2015): Hook statistics. REFERENCES: @@ -2035,7 +2036,7 @@ def single_edge_cut_shapes(self): resu += [(L + 1, L + 2, R)] return resu - def comb(self, side=0): + def comb(self, side='left'): r''' Return the comb of a tree. @@ -2044,40 +2045,47 @@ def comb(self, side=0): the root. The left (resp. right) comb is the list of right (resp. left) subtree of each of these vertices. - INPUT:: - - ``side`` -- set to 0 to obtain a left comb, and to 1 to obain - a right comb. + INPUT: + + - ``side`` -- (default: 'left') set to 'left' to obtain a left comb, and to 'right' to + obtain a right comb. - OUTPUT:: - A list of binary trees. + OUTPUT: + + A list of binary trees. EXAMPLES:: sage: BT = BinaryTree( '.' ) - sage: [BT.comb(0), BT.comb(1)] + sage: [BT.comb('left'), BT.comb('right')] [[], []] sage: BT = BinaryTree( '[.,.]' ) - sage: [BT.comb(0), BT.comb(1)] + sage: [BT.comb('left'), BT.comb('right')] [[], []] sage: BT = BinaryTree( '[[[.,.], .], [.,.]]' ) - sage: BT.comb(0) + sage: BT.comb('left') [., .] - sage: BT.comb(1) + sage: BT.comb('right') [.] sage: BT = BinaryTree( '[[[[., [., .]], .], [[., .], [[[., .], [., .]], [., .]]]], [., [[[., .], [[[., .], [., .]], .]], .]]]' ) - sage: BT.comb(0) + sage: BT.comb('left') [[[., .], [[[., .], [., .]], [., .]]], ., [., .]] - sage: BT.comb(1) + sage: BT.comb('right') [., [[., .], [[[., .], [., .]], .]]] ''' - if self.is_empty(): - return [] - tree = self[side] - res = [] - while not tree.is_empty(): - res.append(tree[1-side]) - tree = tree[side] - return res + def _comb(side): + if self.is_empty(): + return [] + tree = self[side] + res = [] + while not tree.is_empty(): + res.append(tree[1-side]) + tree = tree[side] + return res + if side == 'left': + return _comb(0) + elif side == 'right': + return _comb(1) def hook_number(self): """ @@ -2109,7 +2117,7 @@ def hook_number(self): """ if self.is_empty(): return 0 - return 1 + sum(t.hook_number() for t in self.comb(0) + self.comb(1)) + return 1 + sum(t.hook_number() for t in self.comb('left') + self.comb('right')) def q_hook_length_fraction(self, q=None, q_factor=False): r""" From 13522dd011463d9847458ff908a3504a234faf5d Mon Sep 17 00:00:00 2001 From: Adrien Boussicault Date: Fri, 23 Oct 2015 08:58:44 +0200 Subject: [PATCH 012/163] Modify ''' in the documentation --- src/sage/combinat/binary_tree.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/binary_tree.py b/src/sage/combinat/binary_tree.py index 8c888a03497..2e249aa9e05 100644 --- a/src/sage/combinat/binary_tree.py +++ b/src/sage/combinat/binary_tree.py @@ -2037,7 +2037,7 @@ def single_edge_cut_shapes(self): return resu def comb(self, side='left'): - r''' + r""" Return the comb of a tree. There are two combs in a binary tree : a left comb and a right comb. @@ -2072,7 +2072,7 @@ def comb(self, side='left'): [[[., .], [[[., .], [., .]], [., .]]], ., [., .]] sage: BT.comb('right') [., [[., .], [[[., .], [., .]], .]]] - ''' + """ def _comb(side): if self.is_empty(): return [] @@ -2088,7 +2088,7 @@ def _comb(side): return _comb(1) def hook_number(self): - """ + r""" Return the number of hooks. The hook of a vertex v is the union of {v}, its leftmost and From 238b35298dae93ef5e2fb7365c65b9c9b25662a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9r=C3=A9nice=20Delcroix-Oger?= Date: Wed, 28 Oct 2015 20:11:02 +0100 Subject: [PATCH 013/163] Add the twisting statistic --- src/sage/combinat/binary_tree.py | 47 ++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/sage/combinat/binary_tree.py b/src/sage/combinat/binary_tree.py index 2e249aa9e05..ac879483f2b 100644 --- a/src/sage/combinat/binary_tree.py +++ b/src/sage/combinat/binary_tree.py @@ -2119,6 +2119,53 @@ def hook_number(self): return 0 return 1 + sum(t.hook_number() for t in self.comb('left') + self.comb('right')) + def twisting_number(self): + r""" + Return a 2-tuple where the first element of the tuple is the number + of straight left branches in the binary tree and the second one is + the number of straight right branches in the binary tree. + + OUTPUT : + + A list of size 2 of non negative integers. + + EXAMPLES:: + sage: BT = BinaryTree( '.' ) + sage: BT.twisting_number() + [0, 0] + sage: BT = BinaryTree( '[.,.]' ) + sage: BT.twisting_number() + [0, 0] + sage: BT = BinaryTree( '[[[.,.], .], [.,.]]' ) + sage: BT.twisting_number() + [1, 1] + sage: BT = BinaryTree( '[[[[., [., .]], .], [[., .], [[[., .], [., .]], [., .]]]], [., [[[., .], [[[., .], [., .]], .]], .]]]' ) + sage: BT.twisting_number() + [5, 6] + sage: BT = BinaryTree( '[.,[[[.,.],.],.]]' ) + sage: BT.twisting_number() + [1, 1] + """ + tn=[0,0] + if self.node_number()<=1: + return tn + L=self.comb('left') + if len(L)>0: + tn[0]=tn[0]+1 + for h in L: + tw=BinaryTree([None,h]).twisting_number() + tn[0]=tn[0]+tw[0] + tn[1]=tn[1]+tw[1] + R=self.comb('right') + if len(R)>0: + tn[1]=tn[1]+1 + for l in R: + tw=BinaryTree([l,None]).twisting_number() + tn[0]=tn[0]+tw[0] + tn[1]=tn[1]+tw[1] + return tn + + def q_hook_length_fraction(self, q=None, q_factor=False): r""" Compute the ``q``-hook length fraction of the binary tree ``self``, From f89291574aa5366124358688579110bf5601d265 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Fri, 20 Nov 2015 14:38:14 +0200 Subject: [PATCH 014/163] Documentation of wiener_index() changed. --- src/sage/graphs/generic_graph.py | 40 ++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index f6000bd0f10..3425389db24 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -15632,7 +15632,9 @@ def shortest_path_all_pairs(self, by_weight=False, algorithm=None, def wiener_index(self, by_weight=False, algorithm=None, weight_function=None, check_weight=True): r""" - Returns the Wiener index of the graph. + Return the Wiener index of the graph. + + The graph is expected to not have cycles of negative weight. The Wiener index of a graph `G` is `W(G) = \frac 1 2 \sum_{u,v\in G} d(u,v)` @@ -15649,28 +15651,32 @@ def wiener_index(self, by_weight=False, algorithm=None, - ``by_weight`` (boolean) - if ``True``, the edges in the graph are weighted; if ``False``, all edges have weight 1. - - ``algorithm`` (string) - one of the following algorithms: + - ``algorithm`` (string) - the algorithm to use: - - ``'BFS'`` - the computation is done through a BFS centered on each - vertex successively. Works only if ``by_weight==False``. + - For ``by_weight==False`` only: - - ``'Floyd-Warshall-Cython'`` - the Cython implementation of - the Floyd-Warshall algorithm. Works only if ``by_weight==False``. + - ``'BFS'`` - the computation is done through a BFS centered on + each vertex successively. - - ``'Floyd-Warshall-Python'`` - the Python implementation of - the Floyd-Warshall algorithm. Works also with weighted graphs, even - with negative weights (but no negative cycle is allowed). + - ``'Floyd-Warshall-Cython'`` - the Cython implementation of + the Floyd-Warshall algorithm. Usually slower than ``'BFS'``. - - ``'Dijkstra_NetworkX'``: the Dijkstra algorithm, implemented in - NetworkX. It works with weighted graphs, but no negative weight is - allowed. + - For graphs without negative weights: - - ``'Dijkstra_Boost'``: the Dijkstra algorithm, implemented in Boost - (works only with positive weights). + - ``'Dijkstra_Boost'``: the Dijkstra algorithm, implemented in + Boost. - - ``'Johnson_Boost'``: the Johnson algorithm, implemented in - Boost (works also with negative weights, if there is no negative - cycle). + - ``'Dijkstra_NetworkX'``: the Dijkstra algorithm, implemented in + NetworkX. Usually slower than ``'Dijkstra_Boost'``. + + - For graphs with negative weights: + + - ``'Johnson_Boost'``: the Johnson algorithm, implemented in + Boost. + + - ``'Floyd-Warshall-Python'`` - the Python implementation of + the Floyd-Warshall algorithm. Usually slower than + ``'Johnson_Boost'``. - ``None`` (default): Sage chooses the best algorithm: ``'BFS'`` for unweighted graphs, ``'Dijkstra_Boost'`` if all weights are From 4f69f29063be549fe6144b236fc9e284e98e34fe Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 27 Nov 2015 13:15:31 +0100 Subject: [PATCH 015/163] trac #19630: Graph.gomory_hu's doc should link toward edge_cut --- src/sage/graphs/graph.py | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index c28f2039c1d..1c9dd9721aa 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -6462,7 +6462,7 @@ def is_prime(self): return D[0] == "Prime" and len(D[1]) == self.order() @rename_keyword(deprecation=19550, method='algorithm') - def _gomory_hu_tree(self, vertices, algorithm="FF"): + def _gomory_hu_tree(self, vertices, algorithm=None): r""" Return a Gomory-Hu tree associated to self. @@ -6479,15 +6479,9 @@ def _gomory_hu_tree(self, vertices, algorithm="FF"): fakes one introduced during the computations. This variable is useful for the algorithm and for recursion purposes. - - ``algorithm`` -- There are currently two different - implementations of this method : - - * If ``algorithm = "FF"`` (default), a Python - implementation of the Ford-Fulkerson algorithm is - used. - - * If ``algorithm = "LP"``, the flow problem is solved using - Linear Programming. + - ``algorithm`` -- select the algorithm used by the :meth:`edge_cut` + method. Refer to its documentation for allowed values and default + behaviour. EXAMPLE: @@ -6559,7 +6553,7 @@ def _gomory_hu_tree(self, vertices, algorithm="FF"): @doc_index("Connectivity, orientations, trees") @rename_keyword(deprecation=19550, method='algorithm') - def gomory_hu_tree(self, algorithm="FF"): + def gomory_hu_tree(self, algorithm=None): r""" Returns a Gomory-Hu tree of self. @@ -6577,15 +6571,9 @@ def gomory_hu_tree(self, algorithm="FF"): INPUT: - - ``algorithm`` -- There are currently two different - implementations of this method : - - * If ``algorithm = "FF"`` (default), a Python - implementation of the Ford-Fulkerson algorithm is - used. - - * If ``algorithm = "LP"``, the flow problems are solved - using Linear Programming. + - ``algorithm`` -- select the algorithm used by the :meth:`edge_cut` + method. Refer to its documentation for allowed values and default + behaviour. OUTPUT: From 84ab655f8438b505837df8f1179cafabba5e85f9 Mon Sep 17 00:00:00 2001 From: Miguel Marco Date: Tue, 26 Jan 2016 15:33:38 +0100 Subject: [PATCH 016/163] compute tightly the inverse of complex interval. --- src/sage/rings/complex_interval.pyx | 186 +++++++++++++++++++++++++--- 1 file changed, 171 insertions(+), 15 deletions(-) diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx index c9ea4321ff1..e853e3acfb4 100644 --- a/src/sage/rings/complex_interval.pyx +++ b/src/sage/rings/complex_interval.pyx @@ -55,6 +55,8 @@ cimport real_mpfi cimport real_mpfr from sage.libs.pari.gen cimport gen as pari_gen +from sage.libs.mpfr cimport MPFR_RNDU, MPFR_RNDD + cdef double LOG_TEN_TWO_PLUS_EPSILON = 3.321928094887363 # a small overestimate of log(10,2) @@ -1131,25 +1133,179 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: a = ~(5+I) # indirect doctest sage: a * (5+I) 1.000000000000000? + 0.?e-16*I - """ - cdef ComplexIntervalFieldElement x - x = self._new() - cdef mpfi_t t0, t1 - mpfi_init2(t0, self._prec) - mpfi_init2(t1, self._prec) - - mpfi_sqr(t0, self.__re) - mpfi_sqr(t1, self.__im) - mpfi_add(t0, t0, t1) # now t0 is the norm - mpfi_div(x.__re, self.__re, t0) # x.__re = self.__re/norm + REFERENCES: - mpfi_neg(t1, self.__im) - mpfi_div(x.__im, t1, t0) # x.__im = -self.__im/norm + .. [RL] J. Rokne, P. Lancaster. Complex interval arithmetic. + Communications of the ACM 14. 1971. + """ + + cdef ComplexIntervalFieldElement x + x = self._new() - mpfi_clear(t0) - mpfi_clear(t1) + cdef mpfr_t a, b, c, d + mpfr_init2(a, self._prec) + mpfr_init2(b, self._prec) + mpfr_init2(c, self._prec) + mpfr_init2(d, self._prec) + + cdef mpfr_t rmin, rmax, imin, imax + mpfr_init2(rmin, self._prec) + mpfr_init2(rmax, self._prec) + mpfr_init2(imin, self._prec) + mpfr_init2(imax, self._prec) + + cdef mpfr_t r + mpfr_init2(r, self._prec) + + mpfi_get_left(a, self.__re) + mpfi_get_right(b, self.__re) + mpfi_get_left(c, self.__im) + mpfi_get_right(d, self.__im) + + cdef mpfr_t a2, b2, d2, c2 + mpfr_init2(a2, self._prec) + mpfr_init2(b2, self._prec) + mpfr_init2(c2, self._prec) + mpfr_init2(d2, self._prec) + + cdef mpfr_t div1, div2, aux, aux2 + mpfr_init2(div1, self._prec) + mpfr_init2(div2, self._prec) + mpfr_init2(aux, self._prec) + mpfr_init2(aux2, self._prec) + + if mpfr_sgn(a) >= 0 and mpfr_sgn(c)>=0: #input interval lies in first quadrant + # left endpoint + mpfr_mul(a2, a, a, MPFR_RNDU) + mpfr_mul(b2, b, b, MPFR_RNDU) + mpfr_mul(d2, d, d, MPFR_RNDU) + mpfr_add(div1, a2, d2, MPFR_RNDU) + mpfr_add(div2, b2, d2, MPFR_RNDU) + mpfr_div(rmin, a, div1, MPFR_RNDD) + mpfr_div(aux, b, div2, MPFR_RNDD) + mpfr_min(rmin, rmin, aux, MPFR_RNDD) + #higher endpoint + mpfr_mul(c2, c, c, MPFR_RNDU) + mpfr_add(div1, b2, c2, MPFR_RNDU) + mpfr_div(imax, c, div1, MPFR_RNDU) + mpfr_set_si(aux, 0, MPFR_RNDD) + mpfr_sub(imax, aux, imax, MPFR_RNDU) + mpfr_div(aux2, d, div2, MPFR_RNDU) + mpfr_sub(aux2, aux, aux2, MPFR_RNDU) + mpfr_max(imax, aux2, imax, MPFR_RNDU) + # lower endpoint, it is the lowest point of the circle or one of + if mpfr_cmp(d, a) >=0 and mpfr_cmp(c, a) <= 0: + mpfr_add(imin, a, a, MPFR_RNDD) + mpfr_set_si(aux, -1, MPFR_RNDD) + mpfr_div(imin, aux, imin, MPFR_RNDD) + elif mpfr_cmp(c, a) > 0: + mpfr_mul(c2, c, c, MPFR_RNDD) + mpfr_mul(a2, a, a, MPFR_RNDD) + mpfr_add(div1, a2, c2, MPFR_RNDD) + mpfr_div(imin, c, div1, MPFR_RNDU) + mpfr_set_si(aux, 0, MPFR_RNDD) + mpfr_sub(imin, aux, imin, MPFR_RNDD) + else: + mpfr_mul(d2, d, d, MPFR_RNDD) + mpfr_mul(a2, a, a, MPFR_RNDD) + mpfr_add(div1, a2, d2, MPFR_RNDD) + mpfr_div(imin, d, div1, MPFR_RNDU) + mpfr_set_si(aux, 0, MPFR_RNDD) + mpfr_sub(imin, aux, imin, MPFR_RNDD) + #right endpoint + if mpfr_cmp(c, a) >=0 and mpfr_cmp(b, c) >= 0: + mpfr_add(rmax, c, c, MPFR_RNDD) + mpfr_set_si(aux, 1, MPFR_RNDU) + mpfr_div(rmax, aux, rmax, MPFR_RNDU) + elif mpfr_cmp(a,c) > 0: + mpfr_mul(a2, a, a, MPFR_RNDD) + mpfr_mul(c2, c, c, MPFR_RNDD) + mpfr_add(div1, a2, c2, MPFR_RNDD) + mpfr_div(rmax, a, div1, MPFR_RNDU) + else: + mpfr_mul(b2, b, b, MPFR_RNDD) + mpfr_mul(c2, c, c, MPFR_RNDD) + mpfr_add(div1, b2, c2, MPFR_RNDD) + mpfr_div(rmax, b, div1, MPFR_RNDU) + elif mpfr_sgn(c) > 0 and mpfr_sgn(b) > 0: #between first and second quadrant + # left endpoint + mpfr_abs(aux, a, MPFR_RNDU) + if mpfr_cmp(aux, c) >= 0: + mpfr_set_str(aux, '-0.5', 10, MPFR_RNDD) + mpfr_div(rmin, aux, c, MPFR_RNDD) + else: + mpfr_mul(a2, a, a, MPFR_RNDD) + mpfr_mul(c2, c, c, MPFR_RNDD) + mpfr_add(div1, a2, c2, MPFR_RNDD) + mpfr_div(rmin, a, div1, MPFR_RNDU) + # lower endpoint + mpfr_set_si(aux2, -1, MPFR_RNDD) + mpfr_div(imin, aux2, c, MPFR_RNDD) + #right endpoint + if mpfr_cmp(b, c) >=0: + mpfr_set_str(aux2, '0.5', 10, MPFR_RNDU) + mpfr_div(rmax, aux2, c, MPFR_RNDU) + else: + mpfr_mul(b2, b, b, MPFR_RNDD) + mpfr_mul(c2, c, c, MPFR_RNDD) + mpfr_add(div1, b2, c2, MPFR_RNDD) + mpfr_div(rmax, b, div1, MPFR_RNDU) + # upper endpoint + mpfr_mul(a2, a, a, MPFR_RNDU) + mpfr_mul(b2, b, b, MPFR_RNDU) + mpfr_mul(c2, c, c, MPFR_RNDU) + mpfr_mul(d2, d, d, MPFR_RNDU) + mpfr_add(div1, a2, c2, MPFR_RNDU) + mpfr_div(imax, c, div1, MPFR_RNDD) + mpfr_add(div1, b2, c2, MPFR_RNDU) + mpfr_div(aux, c, div1, MPFR_RNDD) + if mpfr_cmp(imax, aux) > 0: + mpfr_set(imax, aux, MPFR_RNDD) + mpfr_add(div1, a2, d2, MPFR_RNDU) + mpfr_div(aux, d, div1, MPFR_RNDD) + if mpfr_cmp(imax, aux) > 0: + mpfr_set(imax, aux, MPFR_RNDD) + mpfr_add(div1, b2, d2, MPFR_RNDU) + mpfr_div(aux, d, div1, MPFR_RNDD) + if mpfr_cmp(imax, aux) > 0: + mpfr_set(imax, aux, MPFR_RNDD) + mpfr_set_zero(aux, -1) + mpfr_sub(imax, aux, imax, MPFR_RNDU) + elif mpfr_sgn(b) <= 0 and mpfr_sgn(d) >=0: #second quadrant or between second and thirthd + I = self.parent().gen(0) + return -I*(-I*self).__invert__() + elif mpfr_sgn(a) <= 0 and mpfr_sgn(d) <= 0: # thirthd quadrant or between thirthd and fourth + return -(-self).__invert__() + elif mpfr_sgn(a) >=0: #fourth or between fourth and first + I = self.parent().gen(0) + return I*(I*self).__invert__() + + + mpfi_set_fr(x.__re, rmin) + mpfi_put_fr(x.__re, rmax) + + mpfi_set_fr(x.__im, imin) + mpfi_put_fr(x.__im, imax) + + mpfr_clear(a) + mpfr_clear(b) + mpfr_clear(c) + mpfr_clear(d) + mpfr_clear(imin) + mpfr_clear(imax) + mpfr_clear(rmin) + mpfr_clear(rmax) + mpfr_clear(r) + mpfr_clear(a2) + mpfr_clear(b2) + mpfr_clear(c2) + mpfr_clear(d2) + mpfr_clear(div1) + mpfr_clear(div2) + mpfr_clear(aux) + mpfr_clear(aux2) return x From a7a64ace373da32989e3361a34e434abeb303689 Mon Sep 17 00:00:00 2001 From: Miguel Marco Date: Tue, 26 Jan 2016 15:41:16 +0100 Subject: [PATCH 017/163] adapted division to new inverse --- src/sage/rings/complex_interval.pyx | 55 ++++++++++++----------------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx index e853e3acfb4..5d75bfaa6ba 100644 --- a/src/sage/rings/complex_interval.pyx +++ b/src/sage/rings/complex_interval.pyx @@ -780,38 +780,22 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: CIF(2,-3)._div_(CIF(1,-2)) 1.600000000000000? + 0.200000000000000?*I - """ - cdef ComplexIntervalFieldElement x - x = self._new() - cdef mpfi_t a, b, t0, t1, right_nm - mpfi_init2(t0, self._prec) - mpfi_init2(t1, self._prec) - mpfi_init2(a, self._prec) - mpfi_init2(b, self._prec) - mpfi_init2(right_nm, self._prec) - - mpfi_sqr(t0, (right).__re) - mpfi_sqr(t1, (right).__im) - mpfi_add(right_nm, t0, t1) - - mpfi_div(a, (right).__re, right_nm) - mpfi_div(b, (right).__im, right_nm) - - ## Do this: x.__re = a * self.__re + b * self.__im - mpfi_mul(t0, a, self.__re) - mpfi_mul(t1, b, self.__im) - mpfi_add(x.__re, t0, t1) - - ## Do this: x.__im = a * self.__im - b * self.__re - mpfi_mul(t0, a, self.__im) - mpfi_mul(t1, b, self.__re) - mpfi_sub(x.__im, t0, t1) - mpfi_clear(t0) - mpfi_clear(t1) - mpfi_clear(a) - mpfi_clear(b) - mpfi_clear(right_nm) - return x + sage: a = CIF((1, 2), (3, 4)) + sage: b = CIF(-1, (2, 3)) + sage: c = a/b + sage: c.endpoints() + (0.500000000000000 - 1.60000000000000*I, + 1.50000000000000 - 0.600000000000000*I, + 0.500000000000000 - 0.600000000000000*I, + 1.50000000000000 - 1.60000000000000*I) + sage: c = b/a + sage: c.endpoints() + (0.246153846153846 + 0.317647058823529*I, + 0.841176470588236 + 0.761538461538462*I, + 0.246153846153846 + 0.761538461538462*I, + 0.841176470588236 + 0.317647058823529*I) + """ + return self * right.__invert__() def __rdiv__(self, left): """ @@ -1133,6 +1117,13 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: a = ~(5+I) # indirect doctest sage: a * (5+I) 1.000000000000000? + 0.?e-16*I + sage: a = CIF((1, 2), (3, 4)) + sage: c = a.__invert__() + sage: c.endpoints() + (0.0588235294117647 - 0.300000000000000*I, + 0.153846153846154 - 0.200000000000000*I, + 0.0588235294117647 - 0.200000000000000*I, + 0.153846153846154 - 0.300000000000000*I) REFERENCES: From 5b7082d02101925b23c6704dde4e43da9a22addb Mon Sep 17 00:00:00 2001 From: Miguel Marco Date: Tue, 26 Jan 2016 16:46:37 +0100 Subject: [PATCH 018/163] fixed doctests --- src/sage/matrix/matrix0.pyx | 2 +- src/sage/matrix/matrix2.pyx | 44 ++++++++++++++--------------- src/sage/rings/complex_interval.pyx | 8 +++--- src/sage/rings/qqbar.py | 4 +-- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index fc5a11452db..797de1bd6bc 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -1769,7 +1769,7 @@ cdef class Matrix(sage.structure.element.Matrix): sage: K = (A-e).kernel() sage: P = K.basis_matrix() sage: P.str() - '[ 1.000000000000000? + 0.?e-17*I -2.116651487479748? + 0.0255565807096352?*I -0.2585224251020429? + 0.288602340904754?*I -0.4847545623533090? - 1.871890760086142?*I]' + '[ 1.000000000000000? + 0.?e-17*I -2.116651487479748? + 0.0255565807096352?*I -0.2585224251020429? + 0.2886023409047535?*I -0.4847545623533090? - 1.871890760086142?*I]' Use single-row delimiters where appropriate:: diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index f676203cd51..47345677059 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -8860,23 +8860,23 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" ... [-1, 1, -6, -6, 5]]) sage: Q, R = A.QR() sage: Q - [ -0.4588314677411235? -0.1260506983326509? 0.3812120831224489? -0.394573711338418? -0.687440062597?] - [ -0.4588314677411235? 0.4726901187474409? -0.05198346588033394? 0.717294125164660? -0.220962877263?] - [ 0.2294157338705618? 0.6617661662464172? 0.6619227988762521? -0.180872093737548? 0.1964114464561?] - [ 0.6882472016116853? 0.1890760474989764? -0.2044682991293135? 0.096630296654307? -0.662888631790?] + [ -0.4588314677411235? -0.1260506983326509? 0.3812120831224489? -0.394573711338418? -0.6874400625964?] + [ -0.4588314677411235? 0.4726901187474409? -0.05198346588033394? 0.7172941251646595? -0.2209628772631?] + [ 0.2294157338705618? 0.6617661662464172? 0.6619227988762521? -0.1808720937375480? 0.1964114464561?] + [ 0.6882472016116853? 0.1890760474989764? -0.2044682991293135? 0.0966302966543065? -0.6628886317894?] [ -0.2294157338705618? 0.5357154679137663? -0.609939332995919? -0.536422031427112? 0.0245514308070?] sage: R [ 4.358898943540674? -0.4588314677411235? 13.07669683062202? 6.194224814505168? 2.982404540317303?] [ 0 1.670171752907625? 0.5987408170800917? -1.292019657909672? 6.207996892883057?] - [ 0 0 5.444401659866974? 5.468660610611130? -0.682716185228386?] - [ 0 0 0 1.027626039419836? -3.61930014968662?] - [ 0 0 0 0 0.02455143080702?] + [ 0 0 5.444401659866974? 5.468660610611130? -0.6827161852283857?] + [ 0 0 0 1.027626039419836? -3.619300149686620?] + [ 0 0 0 0 0.024551430807012?] sage: Q.conjugate_transpose()*Q - [1.000000000000000? 0.?e-18 0.?e-17 0.?e-15 0.?e-12] - [ 0.?e-18 1.000000000000000? 0.?e-16 0.?e-15 0.?e-12] - [ 0.?e-17 0.?e-16 1.000000000000000? 0.?e-15 0.?e-12] - [ 0.?e-15 0.?e-15 0.?e-15 1.000000000000000? 0.?e-12] - [ 0.?e-12 0.?e-12 0.?e-12 0.?e-12 1.000000000000?] + [1.000000000000000? 0.?e-18 0.?e-17 0.?e-16 0.?e-13] + [ 0.?e-18 1.000000000000000? 0.?e-17 0.?e-16 0.?e-13] + [ 0.?e-17 0.?e-17 1.000000000000000? 0.?e-16 0.?e-13] + [ 0.?e-16 0.?e-16 0.?e-16 1.000000000000000? 0.?e-13] + [ 0.?e-13 0.?e-13 0.?e-13 0.?e-13 1.0000000000000?] sage: Q*R == A True @@ -8891,24 +8891,24 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" sage: Q, R = A.QR() sage: Q [ -0.7302967433402215? 0.2070566455055649? + 0.5383472783144687?*I 0.2463049809998642? - 0.0764456358723292?*I 0.2381617683194332? - 0.1036596032779695?*I] - [ 0.0912870929175277? -0.2070566455055649? - 0.3778783780476559?*I 0.3786559533863032? - 0.1952221495524667?*I 0.701244450214469? - 0.364371165098660?*I] - [ 0.6390096504226938? + 0.0912870929175277?*I 0.1708217325420910? + 0.6677576817554466?*I -0.03411475806452072? + 0.04090198741767143?*I 0.3140171085506763? - 0.0825191718705412?*I] + [ 0.0912870929175277? -0.2070566455055649? - 0.3778783780476559?*I 0.3786559533863033? - 0.1952221495524667?*I 0.701244450214469? - 0.3643711650986595?*I] + [ 0.6390096504226938? + 0.0912870929175277?*I 0.1708217325420910? + 0.6677576817554466?*I -0.03411475806452072? + 0.04090198741767143?*I 0.3140171085506764? - 0.0825191718705412?*I] [ 0.1825741858350554? + 0.0912870929175277?*I -0.03623491296347385? + 0.0724698259269477?*I 0.8632284069415110? + 0.06322839976356195?*I -0.4499694867611521? - 0.0116119181208918?*I] sage: R [ 10.95445115010333? 0.?e-18 - 1.917028951268082?*I 5.385938482134133? - 2.190890230020665?*I -0.2738612787525831? - 2.190890230020665?*I] - [ 0 4.829596256417300? + 0.?e-17*I -0.869637911123373? - 5.864879483945125?*I 0.993871898426712? - 0.3054085521207082?*I] + [ 0 4.829596256417300? + 0.?e-18*I -0.869637911123373? - 5.864879483945125?*I 0.993871898426712? - 0.3054085521207082?*I] [ 0 0 12.00160760935814? + 0.?e-16*I -0.2709533402297273? + 0.4420629644486323?*I] [ 0 0 0 1.942963944258992? + 0.?e-16*I] sage: Q.conjugate_transpose()*Q [1.000000000000000? + 0.?e-19*I 0.?e-18 + 0.?e-17*I 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I] [ 0.?e-18 + 0.?e-17*I 1.000000000000000? + 0.?e-17*I 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I] - [ 0.?e-17 + 0.?e-17*I 0.?e-17 + 0.?e-17*I 1.000000000000000? + 0.?e-16*I 0.?e-16 + 0.?e-16*I] - [ 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 1.000000000000000? + 0.?e-15*I] + [ 0.?e-17 + 0.?e-17*I 0.?e-17 + 0.?e-17*I 1.000000000000000? + 0.?e-17*I 0.?e-16 + 0.?e-16*I] + [ 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 1.000000000000000? + 0.?e-16*I] sage: Q*R - A [ 0.?e-17 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] - [ 0.?e-18 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-15 + 0.?e-15*I] + [ 0.?e-18 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] [0.?e-17 + 0.?e-18*I 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] - [0.?e-18 + 0.?e-18*I 0.?e-18 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-15 + 0.?e-16*I] + [0.?e-18 + 0.?e-18*I 0.?e-18 + 0.?e-18*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] A rank-deficient rectangular matrix, with both values of the ``full`` keyword. :: @@ -10311,9 +10311,9 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" True sage: _, T = A.is_similar(B, transformation=True) sage: T - [ 1.0000000000000? + 0.?e-13*I 0.?e-13 + 0.?e-13*I 0.?e-13 + 0.?e-13*I] - [-0.6666666666667? + 0.?e-13*I 0.16666666666667? + 0.?e-14*I -0.8333333333334? + 0.?e-13*I] - [ 0.6666666666667? + 0.?e-13*I 0.?e-13 + 0.?e-13*I -0.333333333334? + 0.?e-13*I] + [ 1.00000000000000? + 0.?e-14*I 0.?e-14 + 0.?e-14*I 0.?e-14 + 0.?e-14*I] + [-0.66666666666667? + 0.?e-15*I 0.166666666666667? + 0.?e-15*I -0.83333333333334? + 0.?e-14*I] + [ 0.66666666666667? + 0.?e-14*I 0.?e-14 + 0.?e-14*I -0.33333333333333? + 0.?e-14*I] sage: T.change_ring(QQ) [ 1 0 0] [-2/3 1/6 -5/6] diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx index 5d75bfaa6ba..1a07e49e778 100644 --- a/src/sage/rings/complex_interval.pyx +++ b/src/sage/rings/complex_interval.pyx @@ -804,7 +804,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): EXAMPLES:: sage: CIF(2,-3).__rdiv__(CIF(1,-2)) - 0.6153846153846154? - 0.0769230769230769?*I + 0.6153846153846154? - 0.0769230769230770?*I """ return ComplexIntervalFieldElement(self._parent, left)/self @@ -1116,7 +1116,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: I = CIF.0 sage: a = ~(5+I) # indirect doctest sage: a * (5+I) - 1.000000000000000? + 0.?e-16*I + 1.000000000000000? + -1.?e-16*I sage: a = CIF((1, 2), (3, 4)) sage: c = a.__invert__() sage: c.endpoints() @@ -1943,7 +1943,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: CIF(1,1).tan() 0.27175258531952? + 1.08392332733870?*I sage: CIF(2).tan() - -2.18503986326152? + -2.185039863261519? sage: CIF(0,2).tan() 0.964027580075817?*I """ @@ -2032,7 +2032,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: CIF(2).tanh() 0.964027580075817? sage: CIF(0,2).tanh() - -2.18503986326152?*I + -2.185039863261519?*I """ return self.sinh() / self.cosh() diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 35693e39238..e4e68141441 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -4069,8 +4069,8 @@ def __cmp__(self, other): [-0.0221204634374361? - 1.090991904211621?*I, -0.0221204634374361? + 1.090991904211621?*I, -0.8088604911480535?*I, - 0.?e-182 - 0.7598602580415435?*I, - 0.?e-249 + 0.7598602580415435?*I, + 0.?e-215 - 0.7598602580415435?*I, + 0.?e-229 + 0.7598602580415435?*I, 0.8088604911480535?*I, 0.0221204634374361? - 1.090991904211621?*I, 0.0221204634374361? + 1.090991904211621?*I] From f5ed6dc5dac4a5c93465b2fcd179975deddd0583 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 5 Mar 2016 07:34:43 -0600 Subject: [PATCH 019/163] Implemented a non-recursive iterator for integer vectors. --- src/sage/combinat/integer_vector.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/integer_vector.py b/src/sage/combinat/integer_vector.py index fc52464cf88..07d50e8df38 100644 --- a/src/sage/combinat/integer_vector.py +++ b/src/sage/combinat/integer_vector.py @@ -779,10 +779,20 @@ def __iter__(self): yield [self.n] return - for nbar in range(self.n+1): - n = self.n-nbar - for rest in IntegerVectors_nk(nbar , self.k-1): - yield [n] + rest + s = self.n + 1 # Current sum + cur = [s] + while cur: + cur[-1] -= 1 + s -= 1 + if s == self.n: + yield cur + [Integer(0)] * (self.k - len(cur)) + elif cur[-1] < 0: + s -= cur.pop() + elif len(cur) == self.k - 1: + yield cur + [Integer(self.n - s)] + else: + cur.append(self.n - s + 1) + s += self.n - s + 1 def __repr__(self): """ From 1df837d59345c9a06e113657c6fc35ef8561a69c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 5 Mar 2016 08:31:03 -0600 Subject: [PATCH 020/163] Implemented a non-recursive iterator for (weighted) integer vectors. --- src/sage/combinat/integer_vector.py | 16 ++--- src/sage/combinat/integer_vector_weighted.py | 67 +++++++++++++------- 2 files changed, 52 insertions(+), 31 deletions(-) diff --git a/src/sage/combinat/integer_vector.py b/src/sage/combinat/integer_vector.py index 07d50e8df38..1df0bb12512 100644 --- a/src/sage/combinat/integer_vector.py +++ b/src/sage/combinat/integer_vector.py @@ -779,20 +779,20 @@ def __iter__(self): yield [self.n] return - s = self.n + 1 # Current sum - cur = [s] + rem = -1 # Amount remaining + cur = [self.n+1] while cur: cur[-1] -= 1 - s -= 1 - if s == self.n: + rem += 1 + if rem == 0: yield cur + [Integer(0)] * (self.k - len(cur)) elif cur[-1] < 0: - s -= cur.pop() + rem += cur.pop() elif len(cur) == self.k - 1: - yield cur + [Integer(self.n - s)] + yield cur + [Integer(rem)] else: - cur.append(self.n - s + 1) - s += self.n - s + 1 + cur.append(rem + 1) + rem = -1 def __repr__(self): """ diff --git a/src/sage/combinat/integer_vector_weighted.py b/src/sage/combinat/integer_vector_weighted.py index e9c5b56cf4f..2081dd32902 100644 --- a/src/sage/combinat/integer_vector_weighted.py +++ b/src/sage/combinat/integer_vector_weighted.py @@ -245,27 +245,6 @@ def __contains__(self, x): return True - def _recfun(self, n, l): - """ - EXAMPLES:: - - sage: w = WeightedIntegerVectors(3, [2,1,1]) - sage: list(w._recfun(3, [1,1,2])) - [[0, 1, 1], [1, 0, 1], [0, 3, 0], [1, 2, 0], [2, 1, 0], [3, 0, 0]] - """ - w = l[-1] - l = l[:-1] - if l == []: - d = int(n) // int(w) - if n % w == 0: - yield [d] - # Otherwise: bad branch - return - - for d in range(int(n)//int(w), -1, -1): - for x in self._recfun(n-d*w, l): - yield x + [d] - def __iter__(self): """ TESTS:: @@ -288,13 +267,55 @@ def __iter__(self): sage: all( [ i.cardinality() == len(i.list()) for i in ivw] ) True """ - if len(self._weights) == 0: + if not self._weights: if self._n == 0: yield [] return perm = Word(self._weights).standard_permutation() l = [x for x in sorted(self._weights)] - for x in self._recfun(self._n, l): + for x in iterator_fast(self._n, l): yield perm.action(x) #_left_to_right_multiply_on_right(Permutation(x)) + +def iterator_fast(n, l): + """ + Iterate over all ``l`` weighted integer vectors with total weight ``n``. + + EXAMPLES:: + + sage: from sage.combinat.integer_vector_weighted import iterator_fast + sage: list(iterator_fast(3, [1,1,2])) + [[0, 1, 1], [1, 0, 1], [0, 3, 0], [1, 2, 0], [2, 1, 0], [3, 0, 0]] + """ + if n < 0: + return + + if not l: + if n == 0: + yield [] + return + if len(l) == 1: + if n % l[-1] == 0: + yield [n] + return + + k = -1 + cur = [n // l[k] + 1] + rem = n - cur[0] * l[k] # Amount remaining + while cur: + cur[0] -= 1 + rem += l[k] + if rem == 0: + yield [Integer(0)] * (len(l) - len(cur)) + cur + elif cur[0] < 0 or rem < 0: + rem += cur.pop(0) * l[k] + k += 1 + elif len(l) == len(cur) + 1: + if rem % l[0] == 0: + yield [rem // l[0]] + cur + else: + k -= 1 + cur.insert(0, rem // l[k] + 1) + rem -= cur[0] * l[k] + From 237c17e487e9e5a92f404de4c67fc9b0419d43c0 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sun, 6 Mar 2016 17:01:32 +0100 Subject: [PATCH 021/163] mixed order comparison --- src/sage/symbolic/comparison.pxd | 2 +- src/sage/symbolic/comparison.pyx | 189 ++++++++++++++++++++++++++++--- src/sage/symbolic/expression.pyx | 26 +---- 3 files changed, 180 insertions(+), 37 deletions(-) diff --git a/src/sage/symbolic/comparison.pxd b/src/sage/symbolic/comparison.pxd index 36a57d8635b..d8e919f191f 100644 --- a/src/sage/symbolic/comparison.pxd +++ b/src/sage/symbolic/comparison.pxd @@ -6,6 +6,6 @@ cpdef int print_order(lhs, rhs) except -2 cdef int print_order_c(Expression lhs, Expression rhs) cpdef print_sorted(expression_list) - + # cpdef int math_order_c(Expression lhs, Expression rhs) except -2 diff --git a/src/sage/symbolic/comparison.pyx b/src/sage/symbolic/comparison.pyx index 3f3d0782bd4..08b0a47d01b 100644 --- a/src/sage/symbolic/comparison.pyx +++ b/src/sage/symbolic/comparison.pyx @@ -55,12 +55,10 @@ cpdef int print_order(lhs, rhs) except -2: sage: print_order(1, sqrt(2)) 1 - Known bug, see :trac:`12967` :: + Check that :trac:`12967` is fixed:: sage: cmp(SR(oo), sqrt(2)) - Traceback (most recent call last): - ... - RuntimeError: comparing typeid's + 1 """ if not is_Expression(lhs): lhs = SR(lhs) @@ -79,7 +77,7 @@ class _print_key(object): - ``ex`` -- symbolic expression or something that can be converted into one. - + EXAMPLES:: sage: from sage.symbolic.comparison import _print_key @@ -123,7 +121,7 @@ cpdef print_sorted(expressions): - ``expressions`` -- a list/tuple/iterable of symbolic expressions, or something that can be converted to one. - + OUTPUT: The list sorted by :meth:`print_order`. @@ -147,7 +145,7 @@ class _math_key(object): - ``ex`` -- symbolic expression or something that can be converted into one. - + EXAMPLES:: sage: from sage.symbolic.comparison import _math_key @@ -177,12 +175,10 @@ class _math_key(object): sage: _math_key(1) < _math_key(sqrt(2)) True - Known bug, see :trac:`12967` :: - + Check that :trac:`12967` is fixed:: + sage: _math_key(1) < _math_key(oo) - Traceback (most recent call last): - ... - ValueError: cannot compare 1 and +Infinity + True """ less_than = bool(self.ex < other.ex) greater_than = bool(self.ex > other.ex) @@ -206,7 +202,7 @@ cpdef math_sorted(expressions): - ``expressions`` -- a list/tuple/iterable of symbolic expressions, or something that can be converted to one. - + OUTPUT: The list sorted by ascending (real) value. If an entry does not @@ -221,3 +217,170 @@ cpdef math_sorted(expressions): """ return sorted(expressions, key=_math_key) + +cpdef int mixed_order(lhs, rhs) except -2: + """ + Comparison in the mixed order + + INPUT: + + - ``lhs``, ``rhs`` -- two symbolic expressions or something that + can be converted to one. + + OUTPUT: + + Either `-1`, `0`, or `+1` indicating the comparison. An exception + is raised if the arguments cannot be converted into the symbolic + ring. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import mixed_order + sage: mixed_order(1, oo) + -1 + sage: mixed_order(e, oo) + -1 + sage: mixed_order(pi, oo) + -1 + sage: mixed_order(1, sqrt(2)) + -1 + sage: mixed_order(x + x^2, x*(x+1)) + -1 + + Check that :trac:`12967` is fixed:: + + sage: cmp(SR(oo), sqrt(2)) + 1 + """ + if not is_Expression(lhs): + lhs = SR(lhs) + if not is_Expression(rhs): + rhs = SR(rhs) + less_than = _mixed_key(lhs) < _mixed_key(rhs) + if less_than: + return -1 + greater_than = _mixed_key(lhs) > _mixed_key(rhs) + if greater_than: + return 1 + else: + return 0 + + +class _mixed_key(object): + + def __init__(self, ex): + """ + Sort key to sort in mixed order. + + Mixed order is print order if variables are present, + mathematical/numeric if not. This should enable quick + and correct results. + + INPUT: + + - ``ex`` -- symbolic expression or something that can be + converted into one. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import _mixed_key + sage: _mixed_key(1) + + """ + self.ex = ex if is_Expression(ex) else SR(ex) + + def __lt__(self, other): + """ + Implement "less than" to make the key comparable. + + INPUT: + + - ``other`` -- another :class:`_mixed_key` instance. + + OUTPUT: + + Boolean. A ``ValueError`` is raised if we do not know how to + perform the comparison. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import _mixed_key + sage: _mixed_key(1) < _mixed_key(2) + True + sage: _mixed_key(1) < _mixed_key(sqrt(2)) + True + + Check that :trac:`12967` is fixed:: + + sage: _mixed_key(1) < _mixed_key(oo) + True + """ + from sage.rings.real_mpfi import RIF + if len(self.ex.variables() + other.ex.variables()) > 0: + return _print_key(self.ex) < _print_key(other.ex) + + rel = self.ex < other.ex + if (self.ex.is_infinity() or other.ex.is_infinity()): + pynac_result = decide_relational((rel)._gobj) + if pynac_result == relational_undecidable: + raise ValueError('cannot compare {0} and {1}'.format(self.ex, other.ex)) + return pynac_result == relational_true + + det_ex = self.ex - other.ex + if not has_symbol_or_function((rel)._gobj): + while hasattr(det_ex, 'pyobject') and isinstance(det_ex, Expression): + try: + det_ex = det_ex.pyobject() + except TypeError: + break + if not isinstance(det_ex, Expression): + return det_ex < 0 + from sage.rings.qqbar import QQbar + try: + from sage.rings.qqbar import QQbar + num = QQbar(det_ex) + except (TypeError, AttributeError,ValueError,NotImplementedError): + try: + num = det_ex.expand().n(RIF.prec()+5) + except (TypeError, AttributeError): + raise ValueError('cannot compare {0} and {1}'.format(self.ex, other.ex)) + else: + return num < 0 + else: + return num < 0 + + # here we have expressions containing functions + try: + num = det_ex.expand().n(RIF.prec()+5) + except (TypeError, AttributeError): + raise ValueError('cannot compare {0} and {1}'.format(self.ex, other.ex)) + else: + return num < 0 + + + +cpdef mixed_sorted(expressions): + """ + Sort a list of symbolic numbers in the "Mixed" order + + INPUT: + + - ``expressions`` -- a list/tuple/iterable of symbolic + expressions, or something that can be converted to one. + + OUTPUT: + + In the list the numeric values are sorted by ascending (real) value, + and the expressions with variables according to print order. + If an entry does not + define a real value (or plus/minus infinity), or if the comparison + is not known, a ``ValueError`` is raised. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import mixed_sorted + sage: mixed_sorted([SR(1), SR(e), SR(pi), sqrt(2), x, sqrt(x), sin(1/x)]) + [sin(1/x), 1, sqrt(2), e, sqrt(x), x, pi] + """ + return sorted(expressions, key=_mixed_key) + diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index f2ac0283a71..534f07eca50 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -140,6 +140,7 @@ import ring import sage.rings.integer import sage.rings.rational from sage.structure.element cimport ModuleElement, RingElement, Element +from sage.symbolic.comparison import mixed_order from sage.symbolic.getitem cimport OperandsWrapper from sage.symbolic.series cimport SymbolicSeries from sage.symbolic.complexity_measures import string_length @@ -3459,28 +3460,7 @@ cdef class Expression(CommutativeRingElement): sage: t.subs(x=I*x).subs(x=0).is_positive() False """ - return (left)._cmp(right) - - cdef int _cmp_c_impl(left, Element right) except -2: - """ - Compare ``left`` and ``right``. - - INPUT: - - - ``right`` -- A :class:`Expression` instance. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: a = sqrt(3) - sage: b = x^2+1 - sage: a.__cmp__(b) # indirect doctest - -1 - """ - return print_order_compare(left._gobj, (right)._gobj) + return mixed_order(left, right) cpdef int _cmp_add(Expression left, Expression right) except -2: """ @@ -3508,7 +3488,7 @@ cdef class Expression(CommutativeRingElement): TypeError: Argument 'right' has incorrect type (expected sage.symbolic.expression.Expression, got sage.rings.integer.Integer) """ - return sage.symbolic.comparison.print_order_c(left, right) + return mixed_order(left, right) cpdef int _cmp_mul(Expression left, Expression right) except -2: """ From c92ffba371de028843bcbb08a7813fda95b9448a Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sun, 6 Mar 2016 17:27:32 +0100 Subject: [PATCH 022/163] fix wrong doctest --- src/sage/symbolic/random_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/symbolic/random_tests.py b/src/sage/symbolic/random_tests.py index fa7650d07f3..c37c6c3e558 100644 --- a/src/sage/symbolic/random_tests.py +++ b/src/sage/symbolic/random_tests.py @@ -338,7 +338,7 @@ def assert_strict_weak_order(a,b,c, cmp_func): ....: cmp[i,j] = x[i].__cmp__(x[j]) sage: cmp [ 0 -1 -1] - [ 1 0 1] + [ 1 0 -1] [ 1 -1 0] """ from sage.matrix.constructor import matrix From 06ea5a9957bcfd03fa1476f7651b2d9400e9e36b Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Mon, 7 Mar 2016 09:17:08 +0100 Subject: [PATCH 023/163] fix doctest --- src/sage/symbolic/random_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/symbolic/random_tests.py b/src/sage/symbolic/random_tests.py index c37c6c3e558..42726363a88 100644 --- a/src/sage/symbolic/random_tests.py +++ b/src/sage/symbolic/random_tests.py @@ -339,7 +339,7 @@ def assert_strict_weak_order(a,b,c, cmp_func): sage: cmp [ 0 -1 -1] [ 1 0 -1] - [ 1 -1 0] + [ 1 1 0] """ from sage.matrix.constructor import matrix from sage.combinat.permutation import Permutations From c78d15119bf930af733b831bc83b54c40c56f800 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Thu, 10 Mar 2016 14:55:52 +0100 Subject: [PATCH 024/163] add Constant.__lt__ --- src/sage/symbolic/constants.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/sage/symbolic/constants.py b/src/sage/symbolic/constants.py index 20b93ff9788..729cc4ebbc4 100644 --- a/src/sage/symbolic/constants.py +++ b/src/sage/symbolic/constants.py @@ -357,6 +357,19 @@ def domain(self): """ return self._domain + def __lt__(self, other): + """ + Perform float comparison with constant. + + EXAMPLES:: + + sage: cmp(pi, 0) + 1 + sage: cmp(pi, SR(0) + 1 + """ + return self.__float__() < other + def expression(self): """ Returns an expression for this constant. From 1381a89d5e960afb76815f85e771f71f99a3d1c1 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Thu, 10 Mar 2016 15:05:10 +0100 Subject: [PATCH 025/163] 16397: add tests --- src/sage/symbolic/expression.pyx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 534f07eca50..3f0a4e1e911 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -3459,6 +3459,23 @@ cdef class Expression(CommutativeRingElement): I*x - 1/2 sage: t.subs(x=I*x).subs(x=0).is_positive() False + + Check if :trac:`16397` is fixed: + + sage: cmp(1, sqrt(2)) + -1 + sage: cmp(SR(1), sqrt(2)) + -1 + sage: cmp(log(8), 3*log(2)) + 0 + sage: RLF(1) < RLF(sqrt(2)) + True + sage: RealSet((0, pi),[pi, pi],(pi,4)) + (0, 4) + sage: RealSet((0, pi),[0, pi],(pi,4)) + [0, 4) + sage: RealSet((0, pi),[0, 3.5],(pi,4)) + [0, 4) """ return mixed_order(left, right) From 60370a98cc7b77a1c0884066dcc57d193dcf9a6c Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Fri, 11 Mar 2016 08:55:06 +0100 Subject: [PATCH 026/163] 16397: fix typo --- src/sage/symbolic/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/symbolic/constants.py b/src/sage/symbolic/constants.py index 729cc4ebbc4..91d3f57af05 100644 --- a/src/sage/symbolic/constants.py +++ b/src/sage/symbolic/constants.py @@ -365,7 +365,7 @@ def __lt__(self, other): sage: cmp(pi, 0) 1 - sage: cmp(pi, SR(0) + sage: cmp(pi, SR(0)) 1 """ return self.__float__() < other From f78854cf315994d26d33b0bcc93c7e64bb0bd8a8 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Fri, 11 Mar 2016 16:18:11 +0100 Subject: [PATCH 027/163] fix depecrated import of python.pxi --- src/sage/symbolic/comparison.pyx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/symbolic/comparison.pyx b/src/sage/symbolic/comparison.pyx index 08b0a47d01b..082dd39e039 100644 --- a/src/sage/symbolic/comparison.pyx +++ b/src/sage/symbolic/comparison.pyx @@ -12,8 +12,7 @@ There are two useful ways to compare symbolic expressions: symbolic variables). Can be very slow as it potentially calls Maxima to prove the inequality. """ - -include "sage/ext/python.pxi" +from cpython cimport * from sage.symbolic.ring import SR from sage.symbolic.expression cimport is_Expression From eab9ec2ce8ce805441543ffcbebcdf5fb527a2cb Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Fri, 11 Mar 2016 16:34:43 +0100 Subject: [PATCH 028/163] 16397: fix doctest --- src/sage/symbolic/expression.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 1a342ee3674..6fbe1b6bb1c 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -3507,7 +3507,7 @@ cdef class Expression(CommutativeRingElement): TypeError: Argument 'right' has incorrect type (expected sage.symbolic.expression.Expression, got sage.rings.integer.Integer) """ - return mixed_order(left, right) + return print_order_compare(left._gobj, right._gobj) cpdef int _cmp_mul(Expression left, Expression right) except -2: """ From 4b427302c9f1da0c1e8e16f7f258656b34e63c22 Mon Sep 17 00:00:00 2001 From: Ivan Andrus Date: Wed, 13 Jan 2016 22:59:38 -0700 Subject: [PATCH 029/163] Fix warnings --- src/mac-app/AppController.m | 2 +- src/mac-app/MyDocument.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mac-app/AppController.m b/src/mac-app/AppController.m index 1335a160972..9520fbd6b0f 100644 --- a/src/mac-app/AppController.m +++ b/src/mac-app/AppController.m @@ -378,7 +378,7 @@ -(IBAction)revealInFinder:(id)sender{ [sageBinary stringByDeletingLastPathComponent]]]; } else { [[NSWorkspace sharedWorkspace] selectFile:[sageBinary stringByDeletingLastPathComponent] - inFileViewerRootedAtPath:nil]; + inFileViewerRootedAtPath:@""]; } } diff --git a/src/mac-app/MyDocument.m b/src/mac-app/MyDocument.m index ca38b29ab3f..9f2362e5767 100644 --- a/src/mac-app/MyDocument.m +++ b/src/mac-app/MyDocument.m @@ -152,7 +152,7 @@ - (void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)fr - (void)webView:(WebView *)wv runOpenPanelForFileButtonWithResultListener:(id )listener { NSOpenPanel *openPanel = [NSOpenPanel openPanel]; [openPanel beginSheetForDirectory:nil - file:nil + file:@"" modalForWindow:[[self webView] window] modalDelegate:self didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) From 598cc87c92add5c3f95e1109a9b3277eb8519dbc Mon Sep 17 00:00:00 2001 From: Ivan Andrus Date: Mon, 29 Feb 2016 21:07:57 -0700 Subject: [PATCH 030/163] Update AppleScript for iTerm v3 --- src/mac-app/Defaults.plist | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/mac-app/Defaults.plist b/src/mac-app/Defaults.plist index 15381d6e0f7..f62aad90feb 100644 --- a/src/mac-app/Defaults.plist +++ b/src/mac-app/Defaults.plist @@ -50,7 +50,7 @@ end tell set the_script to "%@; exit" tell application "System Events" - set is_running to ("iTerm" is in name of every application process) + set is_running to ("iTerm2" is in name of every application process) end tell tell application "iTerm" @@ -70,7 +70,7 @@ end tell set the_script to "%@" tell application "System Events" - set is_running to ("iTerm" is in name of every application process) + set is_running to ("iTerm2" is in name of every application process) end tell tell application "iTerm" @@ -90,6 +90,38 @@ end tell do shell script "xterm -e '%@' &" xterm - don't exit do shell script "xterm -hold -e '%@' &" + iTerm v3 + set the_script to "%@; exit" + +tell application "System Events" + set is_running to ("iTerm2" is in name of every application process) +end tell + +tell application "iTerm" + if is_running then + set w to (create window with default profile) + end if + tell the (current session of window 1) + write text the_script + end tell + activate +end tell + iTerm v3 - don't exit + set the_script to "%@" + +tell application "System Events" + set is_running to ("iTerm2" is in name of every application process) +end tell + +tell application "iTerm" + if is_running then + set w to (create window with default profile) + end if + tell the (current session of window 1) + write text the_script + end tell + activate +end tell alsoShowMenuExtra From 3857cb6dd347aa9653977a136c4c1c3225d8fafd Mon Sep 17 00:00:00 2001 From: Ivan Andrus Date: Wed, 13 Jan 2016 22:59:59 -0700 Subject: [PATCH 031/163] Automatically offer to upgrade --- src/mac-app/AppController.h | 2 + src/mac-app/AppController.m | 42 ++++ src/mac-app/Defaults.plist | 6 + .../English.lproj/MainMenu.nib/designable.nib | 201 +++++++++++++++++- .../MainMenu.nib/keyedobjects.nib | Bin 55885 -> 62800 bytes 5 files changed, 242 insertions(+), 9 deletions(-) diff --git a/src/mac-app/AppController.h b/src/mac-app/AppController.h index f6b2c1a2c27..b6bd8cefac9 100644 --- a/src/mac-app/AppController.h +++ b/src/mac-app/AppController.h @@ -62,6 +62,8 @@ -(IBAction)showPreferences:(id)sender; -(void)ensureReadWrite; +-(void)offerNotebookUpgrade; +-(IBAction)upgradeNotebook:(id)sender; -(void)setupPaths; // Quit diff --git a/src/mac-app/AppController.m b/src/mac-app/AppController.m index 9520fbd6b0f..7d555fa2e91 100644 --- a/src/mac-app/AppController.m +++ b/src/mac-app/AppController.m @@ -34,6 +34,7 @@ - (void) awakeFromNib{ // Find sageBinary etc. [self setupPaths]; [self ensureReadWrite]; + [self offerNotebookUpgrade]; // Initialize the StatusItem if desired. // If we are on Tiger, then showing in the dock doesn't work @@ -372,6 +373,47 @@ -(void)ensureReadWrite { } } +-(void)offerNotebookUpgrade { + NSFileManager *filemgr = [NSFileManager defaultManager]; + NSLog(@"Checking if sagenb exists %d.", [defaults boolForKey:@"askToUpgradeNB"]); + if ( ! [filemgr fileExistsAtPath:@"~/.sage/sage_notebook.sagenb/users.pickle"] + && [defaults boolForKey:@"askToUpgradeNB"]) { + + // TODO: variable to + NSAlert *alert = [NSAlert alertWithMessageText:@"Sage Notebook Upgrade" + defaultButton:@"Upgrade" + alternateButton:@"Ask me Later" + otherButton:@"Don't ask again" + informativeTextWithFormat:@"You appear to have data in the old notebook format.\n" + "Sage has changed to use Jupyter notebooks by default.\n" + "Unfortunately, they are not completely compatible.\n" + "We can attempt to upgrade, .\n" + ]; + + [alert setAlertStyle:NSWarningAlertStyle]; + NSInteger resp = [alert runModal]; + if (resp == NSAlertDefaultReturn) { // Upgrade + [self upgradeNotebook:self]; + } else if ( resp == NSAlertAlternateReturn) { // Ask Me Later + // nothing + NSLog(@"Ask to upgrade later."); + } else { // Don't ask again + NSLog(@"Don't ask to upgrade again."); + [defaults setBool:NO forKey:@"askToUpgradeNB"]; + [defaults setObject:@"sagenb" forKey:@"preferredNotebookType"]; + NSLog(@"synchronizing defaults: %@",defaults); + } + } +} + +-(IBAction)upgradeNotebook:(id)sender{ + NSLog(@"Upgrade Notebook."); + // TODO: the variable will be set in the upgrade function + [self sageTerminalRun:@"notebook=export" withArguments:nil]; + [defaults setBool:NO forKey:@"askToUpgradeNB"]; + [defaults setObject:@"jupyter" forKey:@"preferredNotebookType"]; +} + -(IBAction)revealInFinder:(id)sender{ if ( [[sender title] isEqualToString:@"Reveal in Shell"] ) { [self terminalRun:[NSString stringWithFormat:@"cd '%@' && $SHELL", diff --git a/src/mac-app/Defaults.plist b/src/mac-app/Defaults.plist index f62aad90feb..033784c7e2d 100644 --- a/src/mac-app/Defaults.plist +++ b/src/mac-app/Defaults.plist @@ -156,5 +156,11 @@ end tell autoStartServer + askToUpgradeNB + + preferSageNB + + defaultJupyterPath + ~/Documents/Sage/ diff --git a/src/mac-app/English.lproj/MainMenu.nib/designable.nib b/src/mac-app/English.lproj/MainMenu.nib/designable.nib index 2e2957f963e..fc57c0902ed 100644 --- a/src/mac-app/English.lproj/MainMenu.nib/designable.nib +++ b/src/mac-app/English.lproj/MainMenu.nib/designable.nib @@ -1,8 +1,8 @@ - + - + @@ -181,8 +181,7 @@ - - + @@ -336,18 +335,55 @@ - + + + + NSNegateBoolean + + - + + + + NSNegateBoolean + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -556,6 +592,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -567,7 +634,8 @@ - + + @@ -683,7 +751,7 @@ - + + + + + + + + + + + + + + + + + + + + + + + ns placeholder + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -945,6 +1089,7 @@ Tip: Dragging a file to the Sage Executable text box will paste the path. + @@ -1199,6 +1344,37 @@ Tip: Dragging a file to the Sage Executable text box will paste the path. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1293,6 +1469,7 @@ Tip: Dragging a file to the Sage Executable text box will paste the path. + @@ -1369,5 +1546,11 @@ The command will be run as + + + + + + diff --git a/src/mac-app/English.lproj/MainMenu.nib/keyedobjects.nib b/src/mac-app/English.lproj/MainMenu.nib/keyedobjects.nib index f7b68125f1b2f506e78681987fa201f570a4e2cc..1539ef124368b0a305c1f841ee8cdcfa6037464d 100644 GIT binary patch literal 62800 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 ZNX?R(l$xCB-x3o3Gx64c{{PgJe*kE~jfnsN literal 55885 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 From d547cce0bb35f972da19b8afe9cfea565b3288c6 Mon Sep 17 00:00:00 2001 From: Ivan Andrus Date: Thu, 14 Jan 2016 22:55:50 -0700 Subject: [PATCH 032/163] Add support for opening Jupyter notebooks --- src/mac-app/AppController.h | 3 + src/mac-app/AppController.m | 118 +++++++++++++++++- .../English.lproj/MainMenu.nib/designable.nib | 52 ++++++-- .../MainMenu.nib/keyedobjects.nib | Bin 62800 -> 64347 bytes src/mac-app/start-sage.sh | 24 ++-- 5 files changed, 176 insertions(+), 21 deletions(-) diff --git a/src/mac-app/AppController.h b/src/mac-app/AppController.h index b6bd8cefac9..7b7451fca12 100644 --- a/src/mac-app/AppController.h +++ b/src/mac-app/AppController.h @@ -32,6 +32,7 @@ NSTask *theTask; NSTask *launchTask; NSPipe *taskPipe; + NSTask *jupyterTask; int port; BOOL myIsInDock, haveStatusItem, useSystemBrowser, neverOpenedFileBrowser; @@ -39,6 +40,8 @@ } // Server control +-(IBAction)startJupyter:(id)sender; +-(IBAction)stopJupyter:(id)sender; -(IBAction)startServer:(id)sender; -(IBAction)stopServer:(id)sender; -(BOOL)serverIsRunning:(BOOL)wait; diff --git a/src/mac-app/AppController.m b/src/mac-app/AppController.m index 7d555fa2e91..0891b67d94c 100644 --- a/src/mac-app/AppController.m +++ b/src/mac-app/AppController.m @@ -88,15 +88,88 @@ - (void) dealloc { [sageBinary release]; [logPath release]; [theTask release]; + [launchTask release]; + [jupyterTask release]; [taskPipe release]; [URLQueue release]; [super dealloc]; } +-(IBAction)startJupyter:(id)sender{ + + if ( jupyterTask != nil ) {// TODO: open a new browser window + // [self browseRemoteURL:[[NSBundle mainBundle] pathForResource:@"loading-page" ofType:@"html"]]; + + return; + } + + NSLog(@"Starting Jupyter server"); + if (haveStatusItem) [statusItem setImage:statusImageGreen]; + + // Add SAGE_BROWSER to environment to point back to this application + if ( !useSystemBrowser ) { + NSString *browserPath = [[NSBundle mainBundle] pathForResource:@"open-location" ofType:@"sh"]; + setenv("SAGE_BROWSER", [browserPath UTF8String], 1); // this overwrites, should it? + } + + // Get any default options they might have for this session + [defaults synchronize]; + NSString *jupyterPath = [defaults objectForKey:@"defaultJupyterPath"]; + NSString *defArgs = [[defaults dictionaryForKey:@"DefaultArguments"] + objectForKey:@"jupyter"]; + + // Escape the arguments + NSMutableString *escSageBin = [NSMutableString stringWithString:sageBinary]; + [escSageBin replaceOccurrencesOfString:@"'" + withString:@"'\\''" + options:0 + range:NSMakeRange(0, [escSageBin length])]; + + NSMutableString * escLogPath = [NSMutableString stringWithString:logPath]; + [escLogPath replaceOccurrencesOfString:@"'" + withString:@"'\\''" + options:0 + range:NSMakeRange(0, [escLogPath length])]; + + // Compile the command. + // We have to run it through a shell so that the default arguments are parsed properly + NSString *command = [NSString stringWithFormat: + @"'%@' --notebook=jupyter %@ >> '%@' 2>&1", + escSageBin, + // default args are ready to be + (defArgs == nil) ? @"" : defArgs, + logPath]; + NSLog(@"Command: %@",command); + + // Create a task that starts the server + jupyterTask = [[[NSTask alloc] init] retain]; + [jupyterTask setLaunchPath:@"/bin/bash"]; + [jupyterTask setArguments:[NSArray arrayWithObjects: @"-c", command, nil]]; + [jupyterTask setCurrentDirectoryPath:jupyterPath]; + [jupyterTask launch]; + + if (haveStatusItem) [statusItem setImage:statusImageBlue]; +} + +-(IBAction)stopJupyter:(id)sender{ + + if (jupyterTask == nil ) { + return; + } + if (haveStatusItem) [statusItem setImage:statusImageRed]; + + [jupyterTask terminate]; + [jupyterTask terminate]; // We have to send it twice to actually stop -- actually do I? + [jupyterTask release]; + jupyterTask = nil; + if (haveStatusItem) [statusItem setImage:statusImageGrey]; +} + + -(IBAction)startServer:(id)sender{ // TODO: Check to see if it's running before attempting to start - NSLog(@"Starting server"); + NSLog(@"Starting SageNB server"); if (haveStatusItem) [statusItem setImage:statusImageGreen]; NSString *scriptPath = [[NSBundle mainBundle] pathForResource:@"start-sage" ofType:@"sh"]; @@ -115,6 +188,7 @@ -(IBAction)startServer:(id)sender{ launchTask = [[NSTask launchedTaskWithLaunchPath:scriptPath arguments:[NSArray arrayWithObjects:sageBinary, logPath, + @"sagenb", defArgs, // May be nil, but that's okay nil]] retain]; @@ -156,6 +230,7 @@ -(void)serverStartedWithPort:(int)p{ } - (void)taskTerminated:(NSNotification *)aNotification { + NSLog(@"Task stopped\n"); NSTask *theObject = [aNotification object]; if (theObject == theTask) { @@ -184,11 +259,11 @@ - (void)taskTerminated:(NSNotification *)aNotification { } else if (theObject == launchTask ) { const int status = [theObject terminationStatus]; - if (status != 0) { + if (status == 0) { + if (haveStatusItem) [statusItem setImage:statusImageGrey]; + } else { + if (haveStatusItem) [statusItem setImage:statusImageRed]; // We failed, so tell the user - if (haveStatusItem) { - [statusItem setImage:statusImageGrey]; - } port = 0; NSAlert *alert = [NSAlert alertWithMessageText:@"Sage Server failed to start" defaultButton:@"View Log" @@ -208,12 +283,42 @@ - (void)taskTerminated:(NSNotification *)aNotification { // Reset for next time. [launchTask release]; launchTask = nil; - + } else if (theObject == jupyterTask ) { + NSLog(@"jupyterTask stopped\n"); + const int status = [theObject terminationStatus]; + if (status == 0) { + if (haveStatusItem) [statusItem setImage:statusImageGrey]; + } else { + if (haveStatusItem) [statusItem setImage:statusImageRed]; + // We failed, so tell the user + NSAlert *alert = [NSAlert alertWithMessageText:@"Jupyter Server failed to start" + defaultButton:@"View Log" + alternateButton:@"Cancel" + otherButton:nil + informativeTextWithFormat:@"For some reason the Jupyter server failed to start. " + "Please check the log for clues, and have that information handy when asking for help."]; + [alert setAlertStyle:NSWarningAlertStyle]; + NSInteger resp = [alert runModal]; + if (resp == NSAlertDefaultReturn) { + // View Log + [self viewSageLog:self]; + } else { + // Cancel + } + } + // Reset for next time. + [jupyterTask release]; + jupyterTask = nil; + } else { // NSLog(@"Got called for a different task."); } } +// TODO: Figure out how to create new worksheet from Jupyter +// TODO: Test Jupyter vs. SageNB +// TODO: Test upgrading... + -(IBAction)stopServer:(id)sender{ if (haveStatusItem) [statusItem setImage:statusImageRed]; @@ -244,6 +349,7 @@ -(IBAction)stopServer:(id)sender{ -(IBAction)stopServerAndQuit:(id)sender{ [self stopServer:self]; + [self stopJupyter:self]; // Tell the application to quit [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0]; diff --git a/src/mac-app/English.lproj/MainMenu.nib/designable.nib b/src/mac-app/English.lproj/MainMenu.nib/designable.nib index fc57c0902ed..d7b3fa56a91 100644 --- a/src/mac-app/English.lproj/MainMenu.nib/designable.nib +++ b/src/mac-app/English.lproj/MainMenu.nib/designable.nib @@ -1,8 +1,8 @@ - + - + @@ -360,13 +360,13 @@ - + - + @@ -1226,16 +1226,52 @@ Tip: Dragging a file to the Sage Executable text box will paste the path. - + - + + + + NSNegateBoolean + + - + - + + + + NSNegateBoolean + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mac-app/English.lproj/MainMenu.nib/keyedobjects.nib b/src/mac-app/English.lproj/MainMenu.nib/keyedobjects.nib index 1539ef124368b0a305c1f841ee8cdcfa6037464d..1e0d517cc3f5e688fae532d71d1dc9a95cf008d6 100644 GIT binary patch literal 64347 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 literal 62800 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 ZNX?R(l$xCB-x3o3Gx64c{{PgJe*kE~jfnsN diff --git a/src/mac-app/start-sage.sh b/src/mac-app/start-sage.sh index ce1e6e0fba7..6c60965755b 100755 --- a/src/mac-app/start-sage.sh +++ b/src/mac-app/start-sage.sh @@ -1,19 +1,19 @@ #!/bin/bash # start-sage.sh -# Fluidium # # Created by Ivan Andrus on 16/1/10. # Copyright 2010 Ivan Andrus. All rights reserved. # Ensure we have enough arguments -if [ $# -lt 2 ]; then +if [ $# -lt 3 ]; then echo "usage: $0 SAGE_EXECUTABLE LOG [ARGS_FOR_NOTEBOOK]" exit 1; fi SAGE_EXECUTABLE="$1" SAGE_LOG="$2" +NB_TYPE="$3" # Read environment variables cd $(dirname $SAGE_EXECUTABLE) @@ -24,16 +24,23 @@ source local/bin/sage-env # So always run first the respective script handling this # This should also catch Intel vs. PPC or 32Bit vs. 64Bit conflicts echo Checking install location >> "$SAGE_LOG" -# TODO: If relocate-once.py is present run it with some sort of progress +# TODO: If relocate-once.py is present run it with some sort of progress # display, e.g. in a terminal +if [ "$NB_TYPE" = jupyter ]; then + +./sage --notebook=jupyter $4 >> "$SAGE_LOG" 2>> "$SAGE_LOG" & + + +else + echo Checking existence of SageNB directory >> "$SAGE_LOG" if [ -e $DOT_SAGE/sage_notebook.sagenb/users.pickle ]; then echo Starting Notebook >> "$SAGE_LOG" - # $3 is not quoted because it comes as one argument from the app, + # $4 is not quoted because it comes as one argument from the app, # so we need the shell to parse it here. - ./sage --notebook $3 >> "$SAGE_LOG" 2>> "$SAGE_LOG" + ./sage --notebook=sagenb $4 >> "$SAGE_LOG" 2>> "$SAGE_LOG" else false fi @@ -52,9 +59,12 @@ if [ $? != 0 ]; then sage-native-execute osascript \ -e 'tell app "Terminal"' \ -e ' activate' \ - -e " do script \"'$SAGE_EXECUTABLE' --notebook\"" \ + -e " do script \"'$SAGE_EXECUTABLE' --notebook=sagenb\"" \ -e 'end' - # We don't include $3 here since this should only happen the first time + # We don't include $4 here since this should only happen the first time # they run it, and this way we don't have to worry about quoting it. fi + +fi + exit $? From f799a2ced4993165ee89b3b1d73a616ee5f6a1a6 Mon Sep 17 00:00:00 2001 From: Ivan Andrus Date: Mon, 7 Mar 2016 22:44:43 -0700 Subject: [PATCH 033/163] Remove bdist menus (#20164) Since we won't be able to merge/rebase on top of #20164, we have to duplicate the changes here. --- .../English.lproj/MainMenu.nib/designable.nib | 64 +----------------- .../MainMenu.nib/keyedobjects.nib | Bin 64347 -> 62174 bytes 2 files changed, 1 insertion(+), 63 deletions(-) diff --git a/src/mac-app/English.lproj/MainMenu.nib/designable.nib b/src/mac-app/English.lproj/MainMenu.nib/designable.nib index d7b3fa56a91..176198c9bcb 100644 --- a/src/mac-app/English.lproj/MainMenu.nib/designable.nib +++ b/src/mac-app/English.lproj/MainMenu.nib/designable.nib @@ -592,37 +592,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1254,7 +1223,7 @@ Tip: Dragging a file to the Sage Executable text box will paste the path. - + @@ -1380,37 +1349,6 @@ Tip: Dragging a file to the Sage Executable text box will paste the path. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/mac-app/English.lproj/MainMenu.nib/keyedobjects.nib b/src/mac-app/English.lproj/MainMenu.nib/keyedobjects.nib index 1e0d517cc3f5e688fae532d71d1dc9a95cf008d6..722933608d5c8813d29d74709b9db45e3e4384a1 100644 GIT binary patch literal 62174 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 From e7f3e22bb28711e9971b4d0e2e3c22aa5644f440 Mon Sep 17 00:00:00 2001 From: Ivan Andrus Date: Sun, 21 Feb 2016 22:17:39 -0700 Subject: [PATCH 034/163] Grab the Jupyter URL so we can open it later --- src/mac-app/AppController.h | 2 ++ src/mac-app/AppController.m | 57 +++++++++++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/src/mac-app/AppController.h b/src/mac-app/AppController.h index 7b7451fca12..6e1bd5599e5 100644 --- a/src/mac-app/AppController.h +++ b/src/mac-app/AppController.h @@ -25,6 +25,7 @@ NSString *sageBinary; NSString *logPath; + NSString *jupyterURL; NSMutableArray *URLQueue; NSUserDefaults *defaults; @@ -42,6 +43,7 @@ // Server control -(IBAction)startJupyter:(id)sender; -(IBAction)stopJupyter:(id)sender; +-(void)receivedData:(NSNotification *)notif; -(IBAction)startServer:(id)sender; -(IBAction)stopServer:(id)sender; -(BOOL)serverIsRunning:(BOOL)wait; diff --git a/src/mac-app/AppController.m b/src/mac-app/AppController.m index 0891b67d94c..fd6d5ab69c5 100644 --- a/src/mac-app/AppController.m +++ b/src/mac-app/AppController.m @@ -60,6 +60,7 @@ - (void) awakeFromNib{ } // indicate that we haven't started the server yet + jupyterURL = nil; port = 0; neverOpenedFileBrowser = YES; URLQueue = [[NSMutableArray arrayWithCapacity:3] retain]; @@ -92,14 +93,17 @@ - (void) dealloc { [jupyterTask release]; [taskPipe release]; [URLQueue release]; + [jupyterURL release]; [super dealloc]; } -(IBAction)startJupyter:(id)sender{ - if ( jupyterTask != nil ) {// TODO: open a new browser window - // [self browseRemoteURL:[[NSBundle mainBundle] pathForResource:@"loading-page" ofType:@"html"]]; - + if ( jupyterTask != nil ) { + if ( jupyterURL != nil ) { + NSLog(@"Going to browse to %@",jupyterURL); + [self browseRemoteURL:jupyterURL]; + } return; } @@ -134,7 +138,9 @@ -(IBAction)startJupyter:(id)sender{ // Compile the command. // We have to run it through a shell so that the default arguments are parsed properly NSString *command = [NSString stringWithFormat: - @"'%@' --notebook=jupyter %@ >> '%@' 2>&1", + @"'%@' --notebook=jupyter %@ 2>&1 | tee -a '%@' |" + " grep --line-buffered -i 'ipython notebook is running at' |" + " grep --line-buffered -o http://.*", escSageBin, // default args are ready to be (defArgs == nil) ? @"" : defArgs, @@ -146,11 +152,34 @@ -(IBAction)startJupyter:(id)sender{ [jupyterTask setLaunchPath:@"/bin/bash"]; [jupyterTask setArguments:[NSArray arrayWithObjects: @"-c", command, nil]]; [jupyterTask setCurrentDirectoryPath:jupyterPath]; - [jupyterTask launch]; + + // set up std out to + NSPipe *outputPipe = [NSPipe pipe]; + [jupyterTask setStandardOutput:outputPipe]; + NSFileHandle *fh = [outputPipe fileHandleForReading]; + [fh waitForDataInBackgroundAndNotify]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receivedData:) name:NSFileHandleDataAvailableNotification object:fh]; + + [jupyterTask launch]; + if (haveStatusItem) [statusItem setImage:statusImageBlue]; } + +- (void)receivedData:(NSNotification *)notif { + NSFileHandle *fh = [notif object]; + NSData *data = [fh availableData]; + if (data.length > 0) { + NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + jupyterURL = [[str stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]] + retain]; + [str release]; + } +} + + -(IBAction)stopJupyter:(id)sender{ if (jupyterTask == nil ) { @@ -166,7 +195,6 @@ -(IBAction)stopJupyter:(id)sender{ } - -(IBAction)startServer:(id)sender{ // TODO: Check to see if it's running before attempting to start NSLog(@"Starting SageNB server"); @@ -315,8 +343,6 @@ - (void)taskTerminated:(NSNotification *)aNotification { } } -// TODO: Figure out how to create new worksheet from Jupyter -// TODO: Test Jupyter vs. SageNB // TODO: Test upgrading... -(IBAction)stopServer:(id)sender{ @@ -485,7 +511,6 @@ -(void)offerNotebookUpgrade { if ( ! [filemgr fileExistsAtPath:@"~/.sage/sage_notebook.sagenb/users.pickle"] && [defaults boolForKey:@"askToUpgradeNB"]) { - // TODO: variable to NSAlert *alert = [NSAlert alertWithMessageText:@"Sage Notebook Upgrade" defaultButton:@"Upgrade" alternateButton:@"Ask me Later" @@ -514,7 +539,6 @@ -(void)offerNotebookUpgrade { -(IBAction)upgradeNotebook:(id)sender{ NSLog(@"Upgrade Notebook."); - // TODO: the variable will be set in the upgrade function [self sageTerminalRun:@"notebook=export" withArguments:nil]; [defaults setBool:NO forKey:@"askToUpgradeNB"]; [defaults setObject:@"jupyter" forKey:@"preferredNotebookType"]; @@ -531,11 +555,20 @@ -(IBAction)revealInFinder:(id)sender{ } -(IBAction)openNotebook:(id)sender{ - [self browseLocalSageURL:@""]; + if ( jupyterURL != nil ) { + [self browseRemoteURL:jupyterURL]; + } else { + [self browseLocalSageURL:@""]; + } } -(IBAction)newWorksheet:(id)sender{ - [self browseLocalSageURL:@"new_worksheet"]; + if ( jupyterURL != nil ) { + // AFAICT you can't create a new worksheet via curl + [self browseRemoteURL:jupyterURL]; + } else { + [self browseLocalSageURL:@"new_worksheet"]; + } } -(IBAction)showPreferences:(id)sender{ From 7a9c3c056bf71cf6c43d80dc6168af168fcd42e8 Mon Sep 17 00:00:00 2001 From: Ivan Andrus Date: Mon, 29 Feb 2016 22:52:05 -0700 Subject: [PATCH 035/163] Remove trailing whitespace that creeped in --- src/mac-app/AppController.m | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/mac-app/AppController.m b/src/mac-app/AppController.m index fd6d5ab69c5..204645825e1 100644 --- a/src/mac-app/AppController.m +++ b/src/mac-app/AppController.m @@ -152,18 +152,18 @@ -(IBAction)startJupyter:(id)sender{ [jupyterTask setLaunchPath:@"/bin/bash"]; [jupyterTask setArguments:[NSArray arrayWithObjects: @"-c", command, nil]]; [jupyterTask setCurrentDirectoryPath:jupyterPath]; - + // set up std out to NSPipe *outputPipe = [NSPipe pipe]; [jupyterTask setStandardOutput:outputPipe]; NSFileHandle *fh = [outputPipe fileHandleForReading]; [fh waitForDataInBackgroundAndNotify]; - + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receivedData:) name:NSFileHandleDataAvailableNotification object:fh]; [jupyterTask launch]; - + if (haveStatusItem) [statusItem setImage:statusImageBlue]; } @@ -181,7 +181,7 @@ - (void)receivedData:(NSNotification *)notif { -(IBAction)stopJupyter:(id)sender{ - + if (jupyterTask == nil ) { return; } @@ -208,7 +208,7 @@ -(IBAction)startServer:(id)sender{ } // Create a task to start the server - + // Get any default options they might have for this session [defaults synchronize]; NSString *defArgs = [[defaults dictionaryForKey:@"DefaultArguments"] @@ -285,7 +285,7 @@ - (void)taskTerminated:(NSNotification *)aNotification { [taskPipe release]; taskPipe = nil; } else if (theObject == launchTask ) { - + const int status = [theObject terminationStatus]; if (status == 0) { if (haveStatusItem) [statusItem setImage:statusImageGrey]; @@ -510,7 +510,7 @@ -(void)offerNotebookUpgrade { NSLog(@"Checking if sagenb exists %d.", [defaults boolForKey:@"askToUpgradeNB"]); if ( ! [filemgr fileExistsAtPath:@"~/.sage/sage_notebook.sagenb/users.pickle"] && [defaults boolForKey:@"askToUpgradeNB"]) { - + NSAlert *alert = [NSAlert alertWithMessageText:@"Sage Notebook Upgrade" defaultButton:@"Upgrade" alternateButton:@"Ask me Later" @@ -520,7 +520,7 @@ -(void)offerNotebookUpgrade { "Unfortunately, they are not completely compatible.\n" "We can attempt to upgrade, .\n" ]; - + [alert setAlertStyle:NSWarningAlertStyle]; NSInteger resp = [alert runModal]; if (resp == NSAlertDefaultReturn) { // Upgrade From 07f12cdfd14f3a7af5445aab7419c0ddaea1074d Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Fri, 18 Mar 2016 09:04:27 +0100 Subject: [PATCH 036/163] 16397: restore transitivity --- src/sage/symbolic/comparison.pyx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/sage/symbolic/comparison.pyx b/src/sage/symbolic/comparison.pyx index 082dd39e039..de129a18a1b 100644 --- a/src/sage/symbolic/comparison.pyx +++ b/src/sage/symbolic/comparison.pyx @@ -315,9 +315,18 @@ class _mixed_key(object): True """ from sage.rings.real_mpfi import RIF - if len(self.ex.variables() + other.ex.variables()) > 0: - return _print_key(self.ex) < _print_key(other.ex) + selfv = len(self.ex.variables()) + otherv = len(other.ex.variables()) + if selfv: + if otherv: + return _print_key(self.ex) < _print_key(other.ex) + else: + return False + else: + if otherv: + return True + # no variables involved from here on rel = self.ex < other.ex if (self.ex.is_infinity() or other.ex.is_infinity()): pynac_result = decide_relational((rel)._gobj) @@ -379,7 +388,7 @@ cpdef mixed_sorted(expressions): sage: from sage.symbolic.comparison import mixed_sorted sage: mixed_sorted([SR(1), SR(e), SR(pi), sqrt(2), x, sqrt(x), sin(1/x)]) - [sin(1/x), 1, sqrt(2), e, sqrt(x), x, pi] + [1, sqrt(2), e, pi, sin(1/x), sqrt(x), x] """ return sorted(expressions, key=_mixed_key) From 9f6b4e88268b081ca534671af96a2c182ef466d7 Mon Sep 17 00:00:00 2001 From: Chris Wuthrich Date: Thu, 24 Mar 2016 11:20:18 +0000 Subject: [PATCH 037/163] trac 20254: changing supersingular p-adic L-series to use Eisenstein p-adics rather than quotient rings --- .../schemes/elliptic_curves/padic_lseries.py | 116 +++++++++++------- 1 file changed, 73 insertions(+), 43 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/padic_lseries.py b/src/sage/schemes/elliptic_curves/padic_lseries.py index ec26a0b5f7a..ea929ea3b85 100644 --- a/src/sage/schemes/elliptic_curves/padic_lseries.py +++ b/src/sage/schemes/elliptic_curves/padic_lseries.py @@ -341,7 +341,7 @@ def modular_symbol(self, r, sign=+1, quadratic_twist= +1): raise NotImplementedError("Quadratic twists for negative modular symbols are not yet implemented.") if D > 0: m = self._modular_symbol - s = +1 + s = ZZ(+1) else: try: m = self._negative_modular_symbol @@ -349,7 +349,7 @@ def modular_symbol(self, r, sign=+1, quadratic_twist= +1): if not hasattr(self, '_modular_symbol_negative'): self.__add_negative_space() m = self._negative_modular_symbol - s = -1 + s = ZZ(-1) # without the ZZ here the u is treated as a 'int' and dividing by D gives 0 # this only happens when it is called from __init__ (?) return s * sum([kronecker_symbol(D,u) * m(r+ZZ(u)/D) for u in range(1,abs(D))]) @@ -422,7 +422,7 @@ def measure(self, a, n, prec, quadratic_twist=+1, sign = +1): raise NotImplementedError("Quadratic twists not implemented for sign -1") if quadratic_twist < 0: - s = -1 + s = ZZ(-1) try: p, alpha, z, w, f = self.__measure_data[(n,prec,s)] @@ -450,13 +450,16 @@ def measure(self, a, n, prec, quadratic_twist=+1, sign = +1): return z * f(a/(p*w)) - (z/alpha) * f(a/w) else: D = quadratic_twist - chip = kronecker_symbol(D,p) + if self.is_ordinary(): + chip = kronecker_symbol(D,p) + else: + chip = 1 # alpha is +- sqrt(-p) anyway if self._E.conductor() % p == 0: mu = chip**n * z * sum([kronecker_symbol(D,u) * f(a/(p*w)+ZZ(u)/D) for u in range(1,abs(D))]) else: mu = chip**n * sum([kronecker_symbol(D,u) *(z * f(a/(p*w)+ZZ(u)/D) - chip *(z/alpha)* f(a/w+ZZ(u)/D)) for u in range(1,abs(D))]) return s*mu - + def alpha(self, prec=20): r""" Return a `p`-adic root `\alpha` of the polynomial `x^2 - a_p x @@ -519,8 +522,9 @@ def alpha(self, prec=20): return K(a) raise RunTimeError("bug in p-adic L-function alpha") else: # supersingular case - f = f.change_ring(Qp(p, prec, print_mode='series')) - a = f.root_field('alpha', check_irreducible=False).gen() + f = f.change_ring(K) + A = K.extension(f, names="alpha") + a = A.gen() self._alpha[prec] = a return a @@ -592,13 +596,6 @@ def order_of_vanishing(self): return v n += 1 - -# def _c_bounds(self, n): -# raise NotImplementedError - -# def _prec_bounds(self, n,prec): -# raise NotImplementedError - def teichmuller(self, prec): r""" Return Teichmuller lifts to the given precision. @@ -702,10 +699,12 @@ def _set_series_in_cache(self, n, prec, D, eta, f): def _quotient_of_periods_to_twist(self,D): r""" - For a fundamental discriminant `D` of a quadratic number field this computes the constant `\eta` such that - `\sqrt{D}\cdot\Omega_{E_D}^{+} =\eta\cdot \Omega_E^{sign(D)}`. As in [MTT]_ page 40. - This is either 1 or 2 unless the condition on the twist is not satisfied, e.g. if we are 'twisting back' - to a semi-stable curve. + For a fundamental discriminant `D` of a quadratic number field this + computes the constant `\eta` such that + `\sqrt{\vert D\vert }\cdot\Omega_{E_D}^{+} =\eta\cdot \Omega_E^{sign(D)}`. + As in [MTT]_ page 40. This is either 1 or 2 unless the condition + on the twist is not satisfied, e.g. if we are 'twisting back' to a + semi-stable curve. REFERENCES: @@ -747,13 +746,12 @@ def _quotient_of_periods_to_twist(self,D): # Note that the number of real components does not change by twisting. if D == 1: return 1 + Et = self._E.quadratic_twist(D) if D > 1: - Et = self._E.quadratic_twist(D) - qt = Et.period_lattice().basis()[0]/self._E.period_lattice().basis()[0] + qt = self._E.period_lattice().basis()[0]/Et.period_lattice().basis()[0] qt *= sqrt(qt.parent()(D)) else: - Et = self._E.quadratic_twist(D) - qt = Et.period_lattice().basis()[0]/self._E.period_lattice().basis()[1].imag() + qt = self._E.period_lattice().basis()[0]/Et.period_lattice().basis()[1].imag() qt *= sqrt(qt.parent()(-D)) verbose('the real approximation is %s'%qt) # we know from MTT that the result has a denominator 1 @@ -1066,7 +1064,7 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): Here the normalization of the `p`-adic L-series is chosen such that `L_p(E,1) = (1-1/\alpha)^2 L(E,1)/\Omega_E` - where `\alpha` is the unit root of the characteristic + where `\alpha` is a root of the characteristic polynomial of Frobenius on `T_pE` and `\Omega_E` is the Neron period of `E`. @@ -1082,6 +1080,12 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): Teichmueller character on the group of roots of unity in `\ZZ_p^\times`) + OUTPUT: + + a power series with coefficients in a quadratic ramified extension of + the `p`-adic numbers generated by a root `alpha` of the characteristic + polynomial of Frobenius on `T_pE`. + ALIAS: power_series is identical to series. EXAMPLES: @@ -1131,7 +1135,7 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): p = self._p eta = ZZ(eta) % (p-1) if p == 2 and self._normalize : - print 'Warning : for p == 2 the normalization might not be correct !' + print 'Warning : for p = 2 the normalization might not be correct !' if prec == 1: if eta == 0: @@ -1142,26 +1146,23 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): K = alpha.parent() R = PowerSeriesRing(K,'T',1) L = self.modular_symbol(0, sign=+1, quadratic_twist= D) - if self._E.has_nonsplit_multiplicative_reduction(p): - L *= 2 - if self._E.has_split_multiplicative_reduction(p): - L *= 0 - else: - chip = kronecker_symbol(D,p) - L *= (1-chip/self.alpha())**2 + chip = kronecker_symbol(D,p) + L *= (1-chip/self.alpha())**2 L /= self._quotient_of_periods_to_twist(D)*self._E.real_components() L = R(L, 1) return L else: # here we need some sums anyway bounds = self._prec_bounds(n,prec) - padic_prec = 20 + alphaadic_prec = 20 else: prec = min(p**(n-1), prec) bounds = self._prec_bounds(n,prec) - padic_prec = max(sum(bounds[1:],[])) + 5 + #padic_prec = max(sum(bounds[1:],[])) + 5 + alphaadic_prec = max(bounds[1:]) + 5 - verbose("using p-adic precision of %s"%padic_prec) + padic_prec = alphaadic_prec//2+1 + verbose("using alpha-adic precision of %s"%padic_prec) ans = self._get_series_from_cache(n, prec, quadratic_twist,eta) if not ans is None: verbose("found series in cache") @@ -1194,11 +1195,15 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): # Now create series but with each coefficient truncated # so it is proven correct: + # the coefficients are now treated as alpha-adic numbers (trac 20254) L = R(L,prec) aj = L.list() if len(aj) > 0: - bj = [aj[0][0].add_bigoh(padic_prec-2) + alpha * aj[0][1].add_bigoh(padic_prec-2)] - bj += [aj[j][0].add_bigoh(bounds[j][0]) + alpha * aj[j][1].add_bigoh(bounds[j][1]) for j in range(1,len(aj))] + bj = [aj[0].add_bigoh(2*(padic_prec-2))] + j = 1 + while j < len(aj): + bj.append( aj[j].add_bigoh(bounds[j]) ) + j += 1 L = R(bj, prec) L /= self._quotient_of_periods_to_twist(D)*self._E.real_components() self._set_series_in_cache(n, prec, quadratic_twist, eta, L) @@ -1236,7 +1241,7 @@ def _prec_bounds(self, n,prec): r""" A helper function not designed for direct use. - It returns the `p`-adic precisions of the approximation + It returns the `\alpha`-adic precisions of the approximation to the `p`-adic L-function. EXAMPLES:: @@ -1250,12 +1255,29 @@ def _prec_bounds(self, n,prec): sage: Lp._prec_bounds(10,5) [[+Infinity, +Infinity], [3, 2], [3, 2], [3, 2], [3, 2]] """ - p = self._p + #p = self._p e = self._e_bounds(n-1,prec) - c0 = ZZ(n+2)/2 - c1 = ZZ(n+3)/2 - return [[infinity,infinity]] + [[(e[j] - c0).floor(), (e[j] - c1).floor()] for j in range(1,len(e))] + c0 = ZZ(n+2) + return [infinity] + [ 2* e[j] - c0 for j in range(1,len(e))] + #return [[infinity,infinity]] + [[(e[j] - c0).floor(), (e[j] - c1).floor()] for j in range(1,len(e))] + + def _poly(self, a): + """ + Given an element a in Qp[alpha] this return the list + containing the two coordinates in Qp. The last digits may be wrong. + + this should be implemented in elements of Eisenstein rings at some point + + """ + v = a._ntl_rep_abs() + k = v[1] + v = v[0] + K = a.base_ring() + pi = K.uniformiser() + v0 = K(v[0]._sage_()) * pi**k + v1 = K(v[1]._sage_()) * pi**k + return [ v0, v1 ] def Dp_valued_series(self, n=3, quadratic_twist = +1, prec=5): r""" @@ -1298,8 +1320,16 @@ def Dp_valued_series(self, n=3, quadratic_twist = +1, prec=5): # now split up the series in two lps = G + H * alpha R = lps.base_ring().base_ring() # Qp QpT , T = PowerSeriesRing(R,'T',prec).objgen() - G = QpT([lps[n][0] for n in range(0,lps.prec())], prec) - H = QpT([lps[n][1] for n in range(0,lps.prec())], prec) + Gli = [] + Hli = [] + for n in range(0,lps.prec()): + v = self._poly(lps[n]) + Gli.append( v[0] ) + Hli.append( v[1] ) + G = QpT( Gli, prec ) + H = QpT( Hli, prec ) + #G = QpT([lps[n][0] for n in range(0,lps.prec())], prec) + #H = QpT([lps[n][1] for n in range(0,lps.prec())], prec) # now compute phi phi = matrix.matrix([[0,-1/p],[1,E.ap(p)/p]]) From 7d1232cb9d9c714e5db7640ad226fa2b96b24df8 Mon Sep 17 00:00:00 2001 From: Chris Wuthrich Date: Thu, 24 Mar 2016 13:23:42 +0000 Subject: [PATCH 038/163] trac 20254 : correcting the normalisation of negative modular symbols --- .../elliptic_curves/ell_modular_symbols.py | 32 ++++++++++++++----- .../elliptic_curves/ell_rational_field.py | 5 +++ .../schemes/elliptic_curves/padic_lseries.py | 2 ++ 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py index a2c85e94832..3311c68c02e 100644 --- a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py +++ b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py @@ -274,8 +274,8 @@ def _find_scaling_L_ratio(self): ... for D in [5,17,12,8]: ... ED = E.quadratic_twist(D) ... md = sum([kronecker(D,u)*m(ZZ(u)/D) for u in range(D)]) - ... etaa = lp._quotient_of_periods_to_twist(D) - ... assert ED.lseries().L_ratio()*ED.real_components()*etaa == md + ... etaD = D/ lp._quotient_of_periods_to_twist(D) + ... print ED.lseries().L_ratio()*ED.real_components() * etaD == md """ E = self._E @@ -361,13 +361,15 @@ def __lalg__(self,D): E = self._E ED = E.quadratic_twist(D) lv = ED.lseries().L_ratio() # this is L(ED,1) divided by the Neron period omD of ED - lv *= ED.real_components() + lv *= ED.real_components() # now it is by the least positive period omD = ED.period_lattice().basis()[0] if D > 0 : om = E.period_lattice().basis()[0] q = sqrt(D)*omD/om * 8 else : om = E.period_lattice().basis()[1].imag() + if E.real_components() == 1: + om *= 2 q = sqrt(-D)*omD/om*8 # see padic_lseries.pAdicLeries._quotient_of_periods_to_twist @@ -409,7 +411,14 @@ def __scale_by_periods_only__(self): print "Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by -1, 2 or -2." cr0 = Integer(crla[0]).str() + crla[1] + '1' E0 = EllipticCurve(cr0) - q = E0.period_lattice().basis()[0]/self._E.period_lattice().basis()[0] + if self._sign == 1: + q = E0.period_lattice().basis()[0]/self._E.period_lattice().basis()[0] + else: + q = E0.period_lattice().basis()[1].imag()/self._E.period_lattice().basis()[0].imag() + if E0.real_components() == 1: + q *= 2 + if E.real_components() == 1: + q /= 2 q = QQ(int(round(q*200)))/200 verbose('scale modular symbols by %s'%q) self._scaling = q @@ -589,7 +598,7 @@ def __init__(self, E, sign, normalize="L_ratio"): 1 sage: M=sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,-1) sage: M(1/3) - 1 + 1/2 This is a rank 1 case with vanishing positive twists. The modular symbol is adjusted by -2:: @@ -597,9 +606,9 @@ def __init__(self, E, sign, normalize="L_ratio"): sage: E=EllipticCurve('121b1') sage: M=sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,-1,normalize='L_ratio') sage: M(1/3) - 2 + 1 sage: M._scaling - -2 + -1 sage: M = EllipticCurve('121d1').modular_symbol(use_eclib=False) sage: M(0) @@ -679,7 +688,14 @@ def _find_scaling_period(self): else : cr0 = Integer(crla[0]).str() + crla[1] + '1' E0 = EllipticCurve(cr0) - q = E0.period_lattice().basis()[0]/E.period_lattice().basis()[0] + if self._sign == 1: + q = E0.period_lattice().basis()[0]/E.period_lattice().basis()[0] + else: + q = E0.period_lattice().basis()[1].imag()/E.period_lattice().basis()[1].imag() + if E0.real_components() == 1: + q *= 2 + if E.real_components() == 1: + q /= 2 q = QQ(int(round(q*200)))/200 verbose('scale modular symbols by %s'%q) self._scaling = q diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 47cf1c21383..fd2ac5b4811 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -1115,6 +1115,8 @@ def modular_symbol(self, sign=1, use_eclib = False, normalize = "L_ratio"): of ``lseries()``, where the value is also divided by the number of connected components of `E(\RR)`). In particular the modular symbol depends on `E` and not only the isogeny class of `E`. + For the negative part the corresponding period is purely imaginary of + smallest positive imaginary part. INPUT: @@ -1209,6 +1211,9 @@ def modular_symbol(self, sign=1, use_eclib = False, normalize = "L_ratio"): 1 sage: E.modular_symbol(use_eclib=False, normalize='period')(0) 1/25 + sage: E.modular_symbol(sign=-1, use_eclib=False, normalize='L_ratio')(1/3) + 1/2 + """ typ = (sign, normalize, use_eclib) diff --git a/src/sage/schemes/elliptic_curves/padic_lseries.py b/src/sage/schemes/elliptic_curves/padic_lseries.py index ea929ea3b85..fdf954bcd3e 100644 --- a/src/sage/schemes/elliptic_curves/padic_lseries.py +++ b/src/sage/schemes/elliptic_curves/padic_lseries.py @@ -752,6 +752,8 @@ def _quotient_of_periods_to_twist(self,D): qt *= sqrt(qt.parent()(D)) else: qt = self._E.period_lattice().basis()[0]/Et.period_lattice().basis()[1].imag() + if Et.real_components() == 1: + qt /= 2 qt *= sqrt(qt.parent()(-D)) verbose('the real approximation is %s'%qt) # we know from MTT that the result has a denominator 1 From 45297356bc88459869d18fc16f2acee7bff60131 Mon Sep 17 00:00:00 2001 From: Chris Wuthrich Date: Thu, 24 Mar 2016 16:31:18 +0000 Subject: [PATCH 039/163] trac 20254: some doctests adjustments --- .../schemes/elliptic_curves/padic_lseries.py | 69 ++++++++++--------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/padic_lseries.py b/src/sage/schemes/elliptic_curves/padic_lseries.py index fdf954bcd3e..cb7213a43bd 100644 --- a/src/sage/schemes/elliptic_curves/padic_lseries.py +++ b/src/sage/schemes/elliptic_curves/padic_lseries.py @@ -122,7 +122,8 @@ class pAdicLseries(SageObject): sage: L.series(3, prec=10) O(5^5) + O(5^2)*T + (4 + 4*5 + O(5^2))*T^2 + (2 + 4*5 + O(5^2))*T^3 + (3 + O(5^2))*T^4 + (1 + O(5))*T^5 + O(5)*T^6 + (4 + O(5))*T^7 + (2 + O(5))*T^8 + O(5)*T^9 + O(T^10) sage: L.series(2,quadratic_twist=-3) - 2 + 4*5 + 4*5^2 + O(5^4) + O(5)*T + (1 + O(5))*T^2 + (4 + O(5))*T^3 + O(5)*T^4 + O(T^5) + 4 + 4*5 + 4*5^2 + 5^3 + O(5^4) + O(5)*T + (2 + O(5))*T^2 + (3 + O(5))*T^3 + O(5)*T^4 + O(T^5) + A prime p such that E[p] is reducible:: @@ -140,12 +141,12 @@ class pAdicLseries(SageObject): sage: E=EllipticCurve('11a1') sage: lp=E.padic_lseries(7) sage: lp.series(4,eta=1) - 6 + 2*7^3 + 5*7^4 + O(7^6) + (4*7 + 2*7^2 + O(7^3))*T + (2 + 3*7^2 + O(7^3))*T^2 + (1 + 2*7 + 2*7^2 + O(7^3))*T^3 + (1 + 3*7^2 + O(7^3))*T^4 + O(T^5) + 3 + 7^3 + 6*7^4 + 3*7^5 + O(7^6) + (2*7 + 7^2 + O(7^3))*T + (1 + 5*7^2 + O(7^3))*T^2 + (4 + 4*7 + 4*7^2 + O(7^3))*T^3 + (4 + 3*7 + 7^2 + O(7^3))*T^4 + O(T^5) sage: lp.series(4,eta=2) 5 + 6*7 + 4*7^2 + 2*7^3 + 3*7^4 + 2*7^5 + O(7^6) + (6 + 4*7 + 7^2 + O(7^3))*T + (3 + 2*7^2 + O(7^3))*T^2 + (1 + 4*7 + 7^2 + O(7^3))*T^3 + (6 + 6*7 + 6*7^2 + O(7^3))*T^4 + O(T^5) sage: lp.series(4,eta=3) - O(7^6) + (3 + 2*7 + 5*7^2 + O(7^3))*T + (5 + 4*7 + 5*7^2 + O(7^3))*T^2 + (3*7 + 7^2 + O(7^3))*T^3 + (2*7 + 7^2 + O(7^3))*T^4 + O(T^5) - + O(7^6) + (5 + 4*7 + 2*7^2 + O(7^3))*T + (6 + 5*7 + 2*7^2 + O(7^3))*T^2 + (5*7 + O(7^3))*T^3 + (7 + 4*7^2 + O(7^3))*T^4 + O(T^5) + (Note that the last series vanishes at `T = 0`, which is consistent with :: sage: E.quadratic_twist(-7).rank() @@ -208,7 +209,7 @@ def __add_negative_space(self): sage: E = EllipticCurve('11a1') sage: lp = E.padic_lseries(5) sage: lp.modular_symbol(1/7,sign=-1) #indirect doctest - -1 + -1/2 """ if self._use_eclib: @@ -314,13 +315,14 @@ def modular_symbol(self, r, sign=+1, quadratic_twist= +1): sage: [lp.modular_symbol(r) for r in [0,1/5,oo,1/11]] [1/5, 6/5, 0, 0] sage: [lp.modular_symbol(r,sign=-1) for r in [0,1/3,oo,1/7]] - [0, 1, 0, -1] + [0, 1/2, 0, -1/2] sage: [lp.modular_symbol(r,quadratic_twist=-20) for r in [0,1/5,oo,1/11]] - [2, 2, 0, 1] + [1, 1, 0, 1/2] - sage: lpt = E.quadratic_twist(-3).padic_lseries(5) - sage: et = E.padic_lseries(5)._quotient_of_periods_to_twist(-3) - sage: lpt.modular_symbol(0) == lp.modular_symbol(0,quadratic_twist=-3)/et + sage: Et = E.quadratic_twist(-3) + sage: lpt = Et.padic_lseries(5) + sage: eta = lpt._quotient_of_periods_to_twist(-3) + sage: lpt.modular_symbol(0) == lp.modular_symbol(0,quadratic_twist=-3) * eta True """ @@ -411,7 +413,7 @@ def measure(self, a, n, prec, quadratic_twist=+1, sign = +1): sage: E = EllipticCurve('11a1') sage: a = E.quadratic_twist(-3).padic_lseries(5).measure(1,2,prec=15) sage: b = E.padic_lseries(5).measure(1,2, quadratic_twist=-3,prec=15) - sage: a == b/E.padic_lseries(5)._quotient_of_periods_to_twist(-3) + sage: a == b * E.padic_lseries(5)._quotient_of_periods_to_twist(-3) True """ @@ -486,9 +488,9 @@ def alpha(self, prec=20): sage: L = E.padic_lseries(3) sage: alpha = L.alpha(10); alpha - (1 + O(3^10))*alpha + alpha + O(alpha^21) sage: alpha^2 - E.ap(3)*alpha + 3 - (O(3^10))*alpha^2 + (O(3^11))*alpha + (O(3^11)) + O(alpha^22) A reducible prime:: @@ -719,25 +721,25 @@ def _quotient_of_periods_to_twist(self,D): sage: E = EllipticCurve('37b1') sage: lp = E.padic_lseries(3) sage: lp._quotient_of_periods_to_twist(-20) - 1 + 20 sage: lp._quotient_of_periods_to_twist(-4) - 1 + 4 sage: lp._quotient_of_periods_to_twist(-3) - 1 + 3 sage: lp._quotient_of_periods_to_twist(-8) - 2 + 4 sage: lp._quotient_of_periods_to_twist(8) - 2 + 4 sage: lp._quotient_of_periods_to_twist(5) - 1 + 5 sage: lp._quotient_of_periods_to_twist(12) - 1 + 12 sage: E = EllipticCurve('11a1') sage: Et = E.quadratic_twist(-3) sage: lpt = Et.padic_lseries(5) sage: lpt._quotient_of_periods_to_twist(-3) - 6 + 1 """ from sage.functions.all import sqrt @@ -840,7 +842,7 @@ def series(self, n=2, quadratic_twist=+1, prec=5, eta=0): sage: E = EllipticCurve('43a1') sage: lp = E.padic_lseries(3) sage: lp.series(2,quadratic_twist=-19) - 2 + 2*3 + 2*3^2 + O(3^4) + (1 + O(3))*T + (1 + O(3))*T^2 + O(T^3) + 2 + 2*3 + 3^2 + 3^3 + O(3^4) + (1 + O(3))*T + (1 + O(3))*T^2 + O(T^3) sage: E.quadratic_twist(-19).label() # optional -- database_cremona_ellcurve '15523a1' @@ -851,9 +853,9 @@ def series(self, n=2, quadratic_twist=+1, prec=5, eta=0): sage: L = EllipticCurve('110a1').padic_lseries(5) sage: for j in [0..3]: print L.series(4, eta=j) O(5^6) + (2 + 2*5 + 2*5^2 + O(5^3))*T + (5 + 5^2 + O(5^3))*T^2 + (4 + 4*5 + 2*5^2 + O(5^3))*T^3 + (1 + 5 + 3*5^2 + O(5^3))*T^4 + O(T^5) - 3 + 2*5 + 2*5^3 + 3*5^4 + O(5^6) + (2 + 5 + 4*5^2 + O(5^3))*T + (1 + 4*5 + 2*5^2 + O(5^3))*T^2 + (1 + 5 + 5^2 + O(5^3))*T^3 + (2 + 4*5 + 4*5^2 + O(5^3))*T^4 + O(T^5) + 4 + 3*5 + 2*5^2 + 3*5^3 + 5^4 + O(5^6) + (1 + 3*5 + 4*5^2 + O(5^3))*T + (3 + 4*5 + 3*5^2 + O(5^3))*T^2 + (3 + 3*5^2 + O(5^3))*T^3 + (1 + 2*5 + 2*5^2 + O(5^3))*T^4 + O(T^5) 2 + O(5^6) + (1 + 5 + O(5^3))*T + (2 + 4*5 + 3*5^2 + O(5^3))*T^2 + (4 + 5 + 2*5^2 + O(5^3))*T^3 + (4 + O(5^3))*T^4 + O(T^5) - 1 + 3*5 + 4*5^2 + 2*5^3 + 5^4 + 4*5^5 + O(5^6) + (2 + 4*5 + 3*5^2 + O(5^3))*T + (2 + 3*5 + 5^2 + O(5^3))*T^2 + (1 + O(5^3))*T^3 + (2*5 + 2*5^2 + O(5^3))*T^4 + O(T^5) + 3 + 5 + 2*5^2 + 5^3 + 3*5^4 + 4*5^5 + O(5^6) + (1 + 2*5 + 4*5^2 + O(5^3))*T + (1 + 4*5 + O(5^3))*T^2 + (3 + 2*5 + 2*5^2 + O(5^3))*T^3 + (5 + 5^2 + O(5^3))*T^4 + O(T^5) """ n = ZZ(n) if n < 1: @@ -952,7 +954,8 @@ def series(self, n=2, quadratic_twist=+1, prec=5, eta=0): L = R(L,res_series_prec) aj = L.list() if len(aj) > 0: - aj = [aj[0].add_bigoh(padic_prec-2)] + [aj[j].add_bigoh(bounds[j]) for j in range(1,len(aj))] + aj = [aj[0].add_bigoh(padic_prec-2)] + \ + [aj[j].add_bigoh(bounds[j]) for j in range(1,len(aj))] L = R(aj,res_series_prec ) L /= self._quotient_of_periods_to_twist(D)*self._E.real_components() @@ -1099,18 +1102,16 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): sage: L.series(2) O(T^3) sage: L.series(4) # takes a long time (several seconds) - (O(3))*alpha + (O(3^2)) + ((O(3^-1))*alpha + (2*3^-1 + O(3^0)))*T + ((O(3^-1))*alpha + (2*3^-1 + O(3^0)))*T^2 + O(T^5) + O(alpha) + (alpha^-2 + O(alpha^0))*T + (alpha^-2 + O(alpha^0))*T^2 + O(T^5) sage: L.alpha(2).parent() - Univariate Quotient Polynomial Ring in alpha over 3-adic Field with capped - relative precision 2 with modulus (1 + O(3^2))*x^2 + (3 + O(3^3))*x + (3 + O(3^3)) + Eisenstein Extension of 3-adic Field with capped relative precision 2 in alpha defined by (1 + O(3^2))*x^2 + (3 + O(3^3))*x + (3 + O(3^3)) An example where we only compute the leading term (:trac:`15737`):: sage: E = EllipticCurve("17a1") sage: L = E.padic_lseries(3) sage: L.series(4,prec=1) - (O(3^18))*alpha^2 + (2*3^-1 + 1 + 3 + 3^2 + 3^3 + ... + 3^18 + O(3^19))*alpha + (2*3^-1 + 1 + 3 + 3^2 + 3^3 + 3^4 + ... + 3^18 + O(3^19)) + O(T) - + alpha^-2 + alpha^-1 + 2 + 2*alpha + ... + O(alpha^38) + O(T) """ n = ZZ(n) if n < 1: @@ -1251,11 +1252,11 @@ def _prec_bounds(self, n,prec): sage: E = EllipticCurve('11a1') sage: Lp = E.padic_lseries(19) sage: Lp._prec_bounds(3,5) - [[+Infinity, +Infinity], [-1, -1], [-1, -1], [-1, -1], [-1, -1]] + [+Infinity, -1, -1, -1, -1] sage: Lp._prec_bounds(2,5) - [[+Infinity, +Infinity], [-1, -2], [-1, -2], [-1, -2], [-1, -2]] + [+Infinity, -2, -2, -2, -2] sage: Lp._prec_bounds(10,5) - [[+Infinity, +Infinity], [3, 2], [3, 2], [3, 2], [3, 2]] + [+Infinity, 6, 6, 6, 6] """ #p = self._p e = self._e_bounds(n-1,prec) @@ -1313,7 +1314,7 @@ def Dp_valued_series(self, n=3, quadratic_twist = +1, prec=5): sage: E = EllipticCurve('14a') sage: L = E.padic_lseries(5) sage: L.Dp_valued_series(4) # long time (9s on sage.math, 2011) - (1 + 4*5 + 4*5^3 + O(5^4) + (4 + O(5))*T + (1 + O(5))*T^2 + (4 + O(5))*T^3 + (2 + O(5))*T^4 + O(T^5), O(5^4) + O(5)*T + O(5)*T^2 + O(5)*T^3 + (2 + O(5))*T^4 + O(T^5)) + (1 + 4*5 + O(5^2) + (4 + O(5))*T + (1 + O(5))*T^2 + (4 + O(5))*T^3 + (2 + O(5))*T^4 + O(T^5), 5^2 + O(5^3) + O(5^2)*T + (4*5 + O(5^2))*T^2 + (2*5 + O(5^2))*T^3 + (2 + 2*5 + O(5^2))*T^4 + O(T^5)) """ E = self._E p = self._p From 3709eaeb4f08c07f94ab02b0670c1074e6fcbd03 Mon Sep 17 00:00:00 2001 From: Martin Albrecht Date: Fri, 25 Mar 2016 16:17:53 +0000 Subject: [PATCH 040/163] libfplll-20160325 --- build/pkgs/libfplll/checksums.ini | 8 ++++---- build/pkgs/libfplll/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/pkgs/libfplll/checksums.ini b/build/pkgs/libfplll/checksums.ini index 0796175109d..6528a6604d2 100644 --- a/build/pkgs/libfplll/checksums.ini +++ b/build/pkgs/libfplll/checksums.ini @@ -1,4 +1,4 @@ -tarball=libfplll-VERSION.tar.bz2 -sha1=0810bb8283671b7bb84392494cc84a20162dd40f -md5=281bbe2b95572401b6cd5264b5694863 -cksum=3698765896 +tarball=libfplll-VERSION.tar.gz +sha1=5154d73807ef4d5e935bc9ffcc366497034dfb03 +md5=fb3f0e8be532723dbee97d84628f41de +cksum=2223093635 diff --git a/build/pkgs/libfplll/package-version.txt b/build/pkgs/libfplll/package-version.txt index d6344119dc3..3f0c8d2e5b4 100644 --- a/build/pkgs/libfplll/package-version.txt +++ b/build/pkgs/libfplll/package-version.txt @@ -1 +1 @@ -20160107 +20160325 From 6677b7b4a3b71e6ac2918c3aaff7726d4f98815d Mon Sep 17 00:00:00 2001 From: Chris Wuthrich Date: Sat, 26 Mar 2016 18:19:45 +0000 Subject: [PATCH 041/163] trac 20254: further small adjustments to twists in padic lseries --- .../schemes/elliptic_curves/padic_lseries.py | 44 ++++++++++--------- src/sage/schemes/elliptic_curves/sha_tate.py | 16 ++++--- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/padic_lseries.py b/src/sage/schemes/elliptic_curves/padic_lseries.py index cb7213a43bd..64e8ef5ca5a 100644 --- a/src/sage/schemes/elliptic_curves/padic_lseries.py +++ b/src/sage/schemes/elliptic_curves/padic_lseries.py @@ -215,10 +215,8 @@ def __add_negative_space(self): if self._use_eclib: verbose('Currently there is no negative modular symbols in eclib, so we have to fall back on the implementation of modular symbols in sage') # once there is a eclib implementation of -1, this should be changed. - self._negative_modular_symbol = self._E.modular_symbol(sign=-1, use_eclib = False, normalize=self._normalize) - else: - self._negative_modular_symbol = self._E.modular_symbol(sign=-1, use_eclib = False, normalize=self._normalize) - + self._negative_modular_symbol = self._E.modular_symbol(sign=-1, use_eclib = False, normalize=self._normalize) + def __cmp__(self,other): r""" Compare self and other. @@ -298,7 +296,7 @@ def modular_symbol(self, r, sign=+1, quadratic_twist= +1): Note also that this function does not check if the condition on the quadratic_twist=D is satisfied. So the result will only be correct if for each prime `\ell` dividing `D`, we have - `ord_{\ell(N)}<= ord_{\ell}(D)`, where `N` is the conductor of the curve. + `ord_{\ell}(N)<= ord_{\ell}(D)`, where `N` is the conductor of the curve. INPUT: @@ -343,7 +341,9 @@ def modular_symbol(self, r, sign=+1, quadratic_twist= +1): raise NotImplementedError("Quadratic twists for negative modular symbols are not yet implemented.") if D > 0: m = self._modular_symbol - s = ZZ(+1) + return sum([ kronecker_symbol(D,u) * m(r+ZZ(u)/D) \ + for u in range(1,D) ] ) + else: try: m = self._negative_modular_symbol @@ -351,10 +351,12 @@ def modular_symbol(self, r, sign=+1, quadratic_twist= +1): if not hasattr(self, '_modular_symbol_negative'): self.__add_negative_space() m = self._negative_modular_symbol - s = ZZ(-1) + return -sum([ kronecker_symbol(D,u) * m(r+ZZ(u)/D) \ + for u in range(1,-D) ] ) + # without the ZZ here the u is treated as a 'int' and dividing by D gives 0 # this only happens when it is called from __init__ (?) - return s * sum([kronecker_symbol(D,u) * m(r+ZZ(u)/D) for u in range(1,abs(D))]) + #return s * sum([kronecker_symbol(D,u) * m(r+ZZ(u)/D) for u in range(1,abs(D))]) def measure(self, a, n, prec, quadratic_twist=+1, sign = +1): @@ -449,7 +451,7 @@ def measure(self, a, n, prec, quadratic_twist=+1, sign = +1): if quadratic_twist == 1: if self._E.conductor() % p == 0: return z * f(a/(p*w)) - return z * f(a/(p*w)) - (z/alpha) * f(a/w) + return z * ( f(a/(p*w)) - f(a/w) / alpha) else: D = quadratic_twist if self.is_ordinary(): @@ -457,9 +459,9 @@ def measure(self, a, n, prec, quadratic_twist=+1, sign = +1): else: chip = 1 # alpha is +- sqrt(-p) anyway if self._E.conductor() % p == 0: - mu = chip**n * z * sum([kronecker_symbol(D,u) * f(a/(p*w)+ZZ(u)/D) for u in range(1,abs(D))]) + mu = chip**n * z * sum([kronecker_symbol(D,u) * f(a/(p*w)+ZZ(u)/D) for u in range(1,D.abs())]) else: - mu = chip**n * sum([kronecker_symbol(D,u) *(z * f(a/(p*w)+ZZ(u)/D) - chip *(z/alpha)* f(a/w+ZZ(u)/D)) for u in range(1,abs(D))]) + mu = chip**n * z * sum([kronecker_symbol(D,u) *( f(a/(p*w)+ZZ(u)/D) - chip /alpha * f(a/w+ZZ(u)/D) ) for u in range(1,D.abs())]) return s*mu def alpha(self, prec=20): @@ -750,12 +752,12 @@ def _quotient_of_periods_to_twist(self,D): return 1 Et = self._E.quadratic_twist(D) if D > 1: - qt = self._E.period_lattice().basis()[0]/Et.period_lattice().basis()[0] + qt = Et.period_lattice().basis()[0]/self._E.period_lattice().basis()[0] qt *= sqrt(qt.parent()(D)) else: - qt = self._E.period_lattice().basis()[0]/Et.period_lattice().basis()[1].imag() + qt = Et.period_lattice().basis()[1].imag()/self._E.period_lattice().basis()[0] if Et.real_components() == 1: - qt /= 2 + qt *= 2 qt *= sqrt(qt.parent()(-D)) verbose('the real approximation is %s'%qt) # we know from MTT that the result has a denominator 1 @@ -894,7 +896,7 @@ def series(self, n=2, quadratic_twist=+1, prec=5, eta=0): # set prec arbitrary to 20. K = Qp(p, 20, print_mode='series') R = PowerSeriesRing(K,'T',1) - L = self.modular_symbol(0, sign=+1, quadratic_twist= D) + L = self.modular_symbol(0, sign=+1, quadratic_twist=D) chip = kronecker_symbol(D,p) if self._E.conductor() % p == 0: L *= 1 - chip/self.alpha() @@ -930,6 +932,7 @@ def series(self, n=2, quadratic_twist=+1, prec=5, eta=0): gamma_power = K(1) teich = self.teichmuller(padic_prec) p_power = p**(n-1) + si = 1-2*(eta % 2) verbose("Now iterating over %s summands"%((p-1)*p_power)) verbose_level = get_verbose() @@ -941,7 +944,7 @@ def series(self, n=2, quadratic_twist=+1, prec=5, eta=0): count_verb += 3 for a in range(1,p): b = teich[a] * gamma_power - s += teich[a]**eta * self.measure(b, n, padic_prec,quadratic_twist=D, sign = 1-2*(eta % 2)).lift() + s += teich[a]**eta * self.measure(b, n, padic_prec, quadratic_twist=D, sign=si).lift() L += s * one_plus_T_factor one_plus_T_factor *= 1+T gamma_power *= gamma @@ -1148,9 +1151,9 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): alpha = self.alpha(prec=20) K = alpha.parent() R = PowerSeriesRing(K,'T',1) - L = self.modular_symbol(0, sign=+1, quadratic_twist= D) - chip = kronecker_symbol(D,p) - L *= (1-chip/self.alpha())**2 + L = self.modular_symbol(0, sign=+1, quadratic_twist=D) + #chip = kronecker_symbol(D,p) + L *= (1-1/self.alpha())**2 L /= self._quotient_of_periods_to_twist(D)*self._E.real_components() L = R(L, 1) return L @@ -1180,6 +1183,7 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): one_plus_T_factor = R(1) gamma_power = 1 teich = self.teichmuller(padic_prec) + si = 1-2*(eta % 2) verbose("Now iterating over %s summands"%((p-1)*p**(n-1))) verbose_level = get_verbose() @@ -1191,7 +1195,7 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): count_verb += 3 for a in range(1,p): b = teich[a] * gamma_power - s += teich[a]**eta * self.measure(b, n, padic_prec,quadratic_twist=D, sign=1-2*(eta % 2)) + s += teich[a]**eta * self.measure(b, n, padic_prec, quadratic_twist=D, sign=si) L += s * one_plus_T_factor one_plus_T_factor *= 1+T gamma_power *= gamma diff --git a/src/sage/schemes/elliptic_curves/sha_tate.py b/src/sage/schemes/elliptic_curves/sha_tate.py index e06375103c4..d3086ddb0be 100644 --- a/src/sage/schemes/elliptic_curves/sha_tate.py +++ b/src/sage/schemes/elliptic_curves/sha_tate.py @@ -89,6 +89,7 @@ from sage.misc.all import verbose import sage.arith.all as arith from sage.rings.padics.factory import Qp +from sage.modules.free_module_element import vector factor = arith.factor valuation = arith.valuation @@ -542,22 +543,27 @@ def an_padic(self, p, prec=0, use_twists=True): E = self.Emin tam = E.tamagawa_product() tors = E.torsion_order()**2 - reg = E.padic_regulator(p) r = E.rank() - + if r > 0 : + reg = E.padic_regulator(p) + else: + if E.is_supersingular(p): + reg = vector([ Qp(p,20)(1), 0 ]) + else: + reg = Qp(p,20)(1) if use_twists and p > 2: Et, D = E.minimal_quadratic_twist() # trac 6455 : we have to assure that the twist back is allowed D = ZZ(D) if D % p == 0: - D = D/p + D = ZZ(D/p) for ell in D.prime_divisors(): if ell % 2 == 1: if Et.conductor() % ell**2 == 0: - D = D/ell + D = ZZ(D/ell) ve = valuation(D,2) - de = (D/2**ve).abs() + de = ZZ( (D/2**ve).abs() ) if de % 4 == 3: de = -de Et = E.quadratic_twist(de) From 1a7a261042df5cf6e0442612527fa7c6664928af Mon Sep 17 00:00:00 2001 From: Chris Wuthrich Date: Sun, 27 Mar 2016 02:52:18 +0100 Subject: [PATCH 042/163] trac 20254: correcting small errors --- .../elliptic_curves/ell_modular_symbols.py | 10 +++---- .../schemes/elliptic_curves/padic_lseries.py | 28 ++++++++++--------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py index 3311c68c02e..6b9061619e6 100644 --- a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py +++ b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py @@ -274,8 +274,8 @@ def _find_scaling_L_ratio(self): ... for D in [5,17,12,8]: ... ED = E.quadratic_twist(D) ... md = sum([kronecker(D,u)*m(ZZ(u)/D) for u in range(D)]) - ... etaD = D/ lp._quotient_of_periods_to_twist(D) - ... print ED.lseries().L_ratio()*ED.real_components() * etaD == md + ... etaD = lp._quotient_of_periods_to_twist(D) + ... ED.lseries().L_ratio()*ED.real_components() * etaD == md """ E = self._E @@ -414,12 +414,12 @@ def __scale_by_periods_only__(self): if self._sign == 1: q = E0.period_lattice().basis()[0]/self._E.period_lattice().basis()[0] else: - q = E0.period_lattice().basis()[1].imag()/self._E.period_lattice().basis()[0].imag() + q = E0.period_lattice().basis()[1].imag()/self._E.period_lattice().basis()[1].imag() if E0.real_components() == 1: q *= 2 - if E.real_components() == 1: + if self._E.real_components() == 1: q /= 2 - q = QQ(int(round(q*200)))/200 + q = ZZ(int(round(q*200)))/200 verbose('scale modular symbols by %s'%q) self._scaling = q diff --git a/src/sage/schemes/elliptic_curves/padic_lseries.py b/src/sage/schemes/elliptic_curves/padic_lseries.py index 64e8ef5ca5a..b19d5b7ca0b 100644 --- a/src/sage/schemes/elliptic_curves/padic_lseries.py +++ b/src/sage/schemes/elliptic_curves/padic_lseries.py @@ -122,7 +122,7 @@ class pAdicLseries(SageObject): sage: L.series(3, prec=10) O(5^5) + O(5^2)*T + (4 + 4*5 + O(5^2))*T^2 + (2 + 4*5 + O(5^2))*T^3 + (3 + O(5^2))*T^4 + (1 + O(5))*T^5 + O(5)*T^6 + (4 + O(5))*T^7 + (2 + O(5))*T^8 + O(5)*T^9 + O(T^10) sage: L.series(2,quadratic_twist=-3) - 4 + 4*5 + 4*5^2 + 5^3 + O(5^4) + O(5)*T + (2 + O(5))*T^2 + (3 + O(5))*T^3 + O(5)*T^4 + O(T^5) + 2 + 4*5 + 4*5^2 + O(5^4) + O(5)*T + (1 + O(5))*T^2 + (4 + O(5))*T^3 + O(5)*T^4 + O(T^5) @@ -723,25 +723,25 @@ def _quotient_of_periods_to_twist(self,D): sage: E = EllipticCurve('37b1') sage: lp = E.padic_lseries(3) sage: lp._quotient_of_periods_to_twist(-20) - 20 + 1 sage: lp._quotient_of_periods_to_twist(-4) - 4 + 1 sage: lp._quotient_of_periods_to_twist(-3) - 3 + 1 sage: lp._quotient_of_periods_to_twist(-8) - 4 + 2 sage: lp._quotient_of_periods_to_twist(8) - 4 + 2 sage: lp._quotient_of_periods_to_twist(5) - 5 + 1 sage: lp._quotient_of_periods_to_twist(12) - 12 + 1 sage: E = EllipticCurve('11a1') sage: Et = E.quadratic_twist(-3) sage: lpt = Et.padic_lseries(5) sage: lpt._quotient_of_periods_to_twist(-3) - 1 + 3 """ from sage.functions.all import sqrt @@ -844,7 +844,7 @@ def series(self, n=2, quadratic_twist=+1, prec=5, eta=0): sage: E = EllipticCurve('43a1') sage: lp = E.padic_lseries(3) sage: lp.series(2,quadratic_twist=-19) - 2 + 2*3 + 3^2 + 3^3 + O(3^4) + (1 + O(3))*T + (1 + O(3))*T^2 + O(T^3) + 2 + 2*3 + 2*3^2 + O(3^4) + (1 + O(3))*T + (1 + O(3))*T^2 + O(T^3) sage: E.quadratic_twist(-19).label() # optional -- database_cremona_ellcurve '15523a1' @@ -1277,13 +1277,15 @@ def _poly(self, a): this should be implemented in elements of Eisenstein rings at some point """ - v = a._ntl_rep_abs() - k = v[1] - v = v[0] + if a.is_zero(): + return [0,0] + v, k = a._ntl_rep_abs() K = a.base_ring() pi = K.uniformiser() v0 = K(v[0]._sage_()) * pi**k v1 = K(v[1]._sage_()) * pi**k + alpha = a.parent().gen() + assert v0 + v1*alpha == a return [ v0, v1 ] def Dp_valued_series(self, n=3, quadratic_twist = +1, prec=5): From a265fc0f458017a32e6a923b836901a8baaaf3c0 Mon Sep 17 00:00:00 2001 From: Chris Wuthrich Date: Sun, 27 Mar 2016 13:54:40 +0100 Subject: [PATCH 043/163] trac 20254: final doctests adjustments --- src/sage/schemes/elliptic_curves/ell_modular_symbols.py | 2 +- src/sage/schemes/elliptic_curves/padic_lseries.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py index 6b9061619e6..9f2001c4a22 100644 --- a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py +++ b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py @@ -275,7 +275,7 @@ def _find_scaling_L_ratio(self): ... ED = E.quadratic_twist(D) ... md = sum([kronecker(D,u)*m(ZZ(u)/D) for u in range(D)]) ... etaD = lp._quotient_of_periods_to_twist(D) - ... ED.lseries().L_ratio()*ED.real_components() * etaD == md + ... assert ED.lseries().L_ratio()*ED.real_components() * etaD == md """ E = self._E diff --git a/src/sage/schemes/elliptic_curves/padic_lseries.py b/src/sage/schemes/elliptic_curves/padic_lseries.py index b19d5b7ca0b..96c83d35ffd 100644 --- a/src/sage/schemes/elliptic_curves/padic_lseries.py +++ b/src/sage/schemes/elliptic_curves/padic_lseries.py @@ -317,10 +317,11 @@ def modular_symbol(self, r, sign=+1, quadratic_twist= +1): sage: [lp.modular_symbol(r,quadratic_twist=-20) for r in [0,1/5,oo,1/11]] [1, 1, 0, 1/2] - sage: Et = E.quadratic_twist(-3) + sage: E = EllipticCurve('20a1') + sage: Et = E.quadratic_twist(-4) sage: lpt = Et.padic_lseries(5) - sage: eta = lpt._quotient_of_periods_to_twist(-3) - sage: lpt.modular_symbol(0) == lp.modular_symbol(0,quadratic_twist=-3) * eta + sage: eta = lpt._quotient_of_periods_to_twist(-4) + sage: lpt.modular_symbol(0) == lp.modular_symbol(0,quadratic_twist=-4) / eta True """ From 56af15e5fa396822ea9975220aa64fcde79b1f90 Mon Sep 17 00:00:00 2001 From: Ivan Andrus Date: Mon, 28 Mar 2016 21:56:12 -0600 Subject: [PATCH 044/163] Remove dependency on export notebook --- .../English.lproj/MainMenu.nib/designable.nib | 39 ++++++------------ .../MainMenu.nib/keyedobjects.nib | Bin 62174 -> 61899 bytes 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/src/mac-app/English.lproj/MainMenu.nib/designable.nib b/src/mac-app/English.lproj/MainMenu.nib/designable.nib index 176198c9bcb..ee3be91782f 100644 --- a/src/mac-app/English.lproj/MainMenu.nib/designable.nib +++ b/src/mac-app/English.lproj/MainMenu.nib/designable.nib @@ -1,8 +1,8 @@ - + - + @@ -992,16 +992,23 @@ Tip: Dragging a file to the Sage Executable text box will paste the path. - + - + + Modern Sage has 3 notebook interfaces. All three offer slightly different user experiences and there are some incompatibilities between them (e.g. interacts). + +1. SageNB. These are the old notebooks of which you may have many. Development of this interface has stalled. + +2. Jupyter. This is the successor of IPython and is intended to be the successor of SageNB on the desktop. + +3. SageMathCloud. This is (currently) a cloud-only offering, and hence not available on the desktop. - + @@ -1020,7 +1027,7 @@ Tip: Dragging a file to the Sage Executable text box will paste the path. - + @@ -1028,26 +1035,6 @@ Tip: Dragging a file to the Sage Executable text box will paste the path. - - - - - - - - - - diff --git a/src/mac-app/English.lproj/MainMenu.nib/keyedobjects.nib b/src/mac-app/English.lproj/MainMenu.nib/keyedobjects.nib index 722933608d5c8813d29d74709b9db45e3e4384a1..6bc5b3c19b21550fcc4a2536ad80ddf57163a587 100644 GIT binary patch literal 61899 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 V!S>lvmw&ms{?~uL$^T=={sxCzQ$GLz literal 62174 zcmeFacYIVu_dh;!%kJLoNl^A)0axirOF~za&^v?#f>f7eNdieWW)rID9UF?h_XZ+% zMG<>LDOLnQ!G;CFUO-Vq<@cVsyPHk2NqC;;``7RFVUw`C_spC#XJ*bhbLPyMIhAFl z(dwQ(cMwV>A`^wE#Ae)Ute)C#LAWYf8mXAtE;46cxUf2UX1h>TVM!^z&Tm&8sZ1yI z;=3P9vyCKn;vgrIwxk{DLb{VI@;6neojRzCx~Yez(c|d}^dx#R?N5i(5p*0KPbbhx zbP6q?(`bm!p%Ge1&!#nW0X?5yN>|Wp=(Y3~dMmw$uA~pp2kBb+1bvo1M_;8I=-c!i zx{ZEBzo1{zZ|QgRS9*XRrhiFPk|a%XN?s{VY9+On+DYxDu2MItkJMK>LmDKFkVZ-q zq={02G*y}{oh6k@QK?#5B%LQ+EG?I=l&+F)lx~t%N~@$tq(`MEr01kpr47=X(p%DY z>3!)V>0{|DX|ME?^s{tOIxG{J$_`nR)8urymE2lxEBBI5lLyMD%fsa1@_2cITp&-C z&yqv(Jh@7a%IC-z$`{F3$XCiY$~Vb($#={5%MZwFE zO1V<0R4X;gVr7Z4OgUeMSql2Tfql@DdM^8tlqmQGn<21)W z$6&_@#}r3_W2)m!$1F$K5ph&H7C9C>mN+hOTFF9UwyzY3z@up*|V~1mxV~=C6<7>x0$9ImO9KSmbIsS0`>G(%eG);4A zF3qpC)K1V^X|1(3+R0jHt&4Vw)>Z4N_16Yyr)sBZgS3&_C~dShMw_5b(F(Mw+L_u+ zEv(JeinS80T&vb8>HJp{`M` z(XI)uiLS}6DX!_R8Lp6Pj;q*J;wpDlxT3CV*J9Ta*Lg0}b&>00*JZBDUDvp-b=~B; z*>#8OUe{{ZeXfUGPq?0TJ>z=O^}1`L>rK}-*LK(Yu3fG@uFqXxxc0ezbp7P|+4Y<2 zi0g0HKW@pbxt(s8+wX4a4!Cu98+Utm2X{wzS9i9%m%F!nfIG)M*ge8M!9CGE**)7` zvg<-X7Th&hsqyT;jRZbEW4h&-I=gJhyso^W5#Z$8(=&jpt#{Bc3Nb zPkNs5JnMPM^Rnl4&qmLip0_;jc|P>)@_g#~+OyB|z2|`EkmnE2pPqlbidXg8ye@Bs zw}tmOui-tx8}xSccJg-f_V)Ji_Vu3T9pWA89p)YF9pjzo&Gk<47I+}X_lWON-&4Nz zzNdZ9`CjsE^lkEO_HFTP^}XlY=G*1l?fc00rSB`>Uf)l?pMAebEqw=+e!ky)M|^+# z{_!h*)o=5A{XW0nf4sl7zm5Mye+Pd@e$H7ozos2Yccy2gw@UAj-Z8yvdXMy;>HX6Orw>UV zmp(pyM*7_J;`I6HW$88P=cZqherfux>8sKoPG6t?bo$HbZ=}DOz9aq9^!@4Ir2mxu zXZqpve={5zT1Lx^U`FSR?2LgK!!sskOwE{+QIb)W5zSbVaY4p~8CPc9m2rQ@+KeYM zp2}FC@l1<7$Ga}#T{rX-^j3Ony^VgN9@J0LPuAP&?ez9~2fd@-N$;$8(NEF4>fQA2dJny) zo~dW)*?KR%x86tZtM}9U>jU&t_0#l$`sw-^`XD_=AFL12hw8)h;ra-Dq&`X?t&h>i z>f`kB`UHKVo~uvN^YnauvOYyG(5LEW>eKY;`V4)hK1-jipQVTNIeMX9q=)snda+)j zm+JHM`FfdNu2<+0y;470uhOG>wO*qy&==~9^u_uTeW`wqzDz$?KTkJxrk}50pkJt8 zq+hHr*Duj8)mP}3>6hzQ=vV4j=~wI5=-2Ak>DTKw=r`&&={M`Q=(p;(>9^~5=y&RO z>38e*=qvSA`n~#U{XTt-e!u>J{-FMl{;>Xt{;2+#{Mq8tu(cb7_bTm2{ zosBNWDMnYLo6+6qVe~XIjVvSE=wBX-qSw8#9cV#w=sD zah4G><`{)Wkr6iL8pTG5QEJRH<{M>3xlv(6j7sBdqsoXH)kcl6z*uN3G8P+4jHSjo z#xmnv<2=JOm~p;wfpMX6k#VuH+_=QJ)L3C$W?XJuVO(ikWn67sV_a)oXIyXGVBBcj zWZZ1rV%%!nX54AqGr3(wOw*4x}ULG|j5N0#H+}x8dk46s?I?N6M|nt^{1?sqv<7C#L{GSJDk6b&o9* z?>22#g*`}5uy9;WbqMXw3s4mIHK`#B$U?G+ zEGA3HQgRMiM$RSY5tA@-KDmHgNG>85ljY&esP8S*T7jyz9ZATN@a$jjsv@+#RtUL&uQjbszqOtz3W$eZLX@-}&g zyi2x{_sBM~oxD$Wke%cM@*&wpc9W0D$K(_8Dfx`-A)k{k$d}|RvX^{K_L2SM8}cpr zj(kskAU~3y$j{^#@+&z&4wB!<@8l5qgZxPjlfO)d=`;Q2y^JnlbSb0D7(I_s#^?o% zE@$*oMlWOZ3P!JC^g2dwVDu(NZ)S8YqfavW6r)cw`YfZ*G5S2CuQIxc(YF}g#^?u( ze$43SjDF4NcZ~kb=x>Z3X7pbs$xN~{$;Bi;lUgvzVA6?9YR9BbOzOs@EGG41(rHY} zVbU-rjb_q#Cgm|{DwAd~=`1FNnN-T8aweV4q#7nIX3{byoyR1`qzjmI5tEiP=~5x`9bIG3gd2-NvLlm~nfwcr4>I`>lMgfbZ>A8YC`^G^ax%rk z6u&ua2l0o;YtYh$o=AgeL4GOFK;VuG zSJaHE4wpyGbTh*oK2b}DX*}`mXo@kCRN10>HapnXw*UU2~n+4{X<}`DzS#6$c-f7-t-eV5?5EKlcr_$5t zK+tnKJ%a{l4jl}thSFhBZ=4!1dt61hJW^3wI5tvvCWLs!sPa&87{VL6WZ2m?r3<*g zE?pEZ65pGx%(V3LjvhT~9*;I0XBsodhZhD5B31LFCE;-O`*b9bj-sRK7&U4^_>#I%*=#o$ZI7w3JP}{7O2JbWTPz+2pmI zAV7I7g4(=lbi7r~RC*?w6X*R(I+b*8oHeuQFy2Pdyf$dwgvxM5aD1dXJSP&F{}JaW z8pt_XNQ=PLF!;*3dWPWZJUWk-(Q-NrgE>D`RUEDcNAqgtl!GdKl_QJ@RNhKdf3kV9 z88lBa4N#sRt}4ffFAL^zYavoGD{7UpiVTPks45zzL9?yd&K#bikTskZk|Ph$MRYM; zLYLBW=rVdPJ&&4Zd$Xh2**wMUX7(^M&1|!GssS=DHb9mOE}SgL>|h%Cc^DE6c@~Gh zDhaw1Z;p?{2Rns|7KADa!$m3B9WQ!QQqeA^kyjk5Y#h&BNx)rAV^VPwuxpZFyPL+8 z^3bBv@=)V-Kb8dFlfy5p3RTQ+9RBGf_$<@Nozyt!%SoWUOk--aw4%7CEL4?xG;z*Y zqiHkUOyA(63FE{XF@4RxW*1h$hS_tE$14!VL%7=0NidGshf~s@pfmx_ytJw6Ey)0qBizVOhgw#ew%H0iR|X1*MgXt4ksk zjTiVisle%AadYs0B*70to1=xWz#FgrXcG8fRA0RiyBI}{!b|of_@Nk0(GnppB$wod zw2IlNIaO7m#jEh0hjfOx5+5xvpX86p(3O&pblwd?JAjJ@DV+|KGK9d*foMQiN-d=T z|E0qqZAV4(!j++_P<5mVf^}4MXlaz&!bO%;(9DteF^!20$&Sfx>108+C7?$0$(8|8 zS5+P4Q*QiV$d4-Q#x!2MOH%Q%ym)#ZHg4*c-c>8+r0$|62@xk{OTFkY*z5VB;;vgt4|REX(#Mdn1)m?6lC)zWw~h9xy)(tpl{ zaY@DV8{tCZ$R{V2o`MNIr4%}FY^1pH5;K!ZOf`)%EK{n)0wgT{ufT#NOrpUK$25xS z4wckWq_)Ew=|a)e8A684=B)Kk0v2et(u&}4taMWh&P3VE zlgfs8Szm40ypnKPLur&KdVNySLI{h&HKk=mf}PUM(k=8fOijxQ$^k!GO1DE_pF!J5 zcgH4WP8kL*EY9$3e_E~SGcHmzE)<<_&d}1apGnZBRkIqu&t-7h^@ zhx;XFiCN6KUyv`X+~PRrACw*k=ZnqKIOm@vM^;Pgq^G3y($ms2<~*~^tS~Fhs#M&+ zKgNAa*OUnE&o_-Q*1;H9q&haV8t~%&BwlO*3rnKYKn`Yn&bWPz4R;kqsCkq?~mWekyX;~(jn;&bCJ2kJSSB@ zd>iYBe??0dh<;eiTUse5dL`P)y$fh*L$=1s%C@AkOSw%{G$-{y{M#Z+=LY>`{yrPpDLiGSyDw&aftmsRe-rc7AJOHgT)8Viq!V@($ANi`_u+Zt2#G@|91I~q&ght%@|?hLh$!i6bHw`1dTRTc(r_AQen-!6;rODbbe`5WgeCJ zVkJ(r)4UxrV$`G|(`S#*tJeIUiL|FCX)nhPh8>R1vJ8(z&JKDy-SZg%;(N zF5#{ytC(=$Br4(d-fX|Y{UQ~^v9FLdP`TMJeNgE0cZDiBqWKfEkd98C}<-1l&@aB{?ZVY^tR@~Rh?g@x^MA3WA{ zfw{(ffYDJJei^M{bYc2giy%zzHwpS>>C)_ey}Jgp`(!Wc8eH0|S3kV(*LT^n=@YBM zF>w`z`@*Vur+iTUP5vFcKLpPHDIb>ql8*=v&iL@6YBaVo!i_w5T;TXv0Meo$ad;K> z4>)H~%>vO1lb8=OT5bN7M&Yl{Ep*k|HaLqAE7|5J^|;ibFoA zX!380Q*kM7#iMxTeTrX6Q_|7d`=pl2@w5HM0VN!Lh1KDr{LmcE72dB{%O!o{+~ah@ zxy1pn0uiJ^p$$j(fzrI1N{mT70>kqyQUzOwR)rQud0!M)MQSRFX8VuFYOt&ly3?m5mK8j?`2SEv>S4z`&JZ71o0F3G;D>`662Kh`Ck+KQM~2 zx(2iQ^x=ajt6%@F!OVWkmZiczNXbcH@4>N~@0f4J>v~)JOc|w&R>mk}m2t{=Wr8wM z$yFwSS$RsnGFh3T6ev?+xd)VVWjeSqQ<f>0Uv zLZW5ysWZ5yx;j!JK4Iz18yhqHW7F5#)r(xU;u)qvykf8pgNy4WFJdkL;IAu;>_R zK8r4X+I-S{!CaR{ul7sWFU%>6LYS6B76!S8aZaczSc(zfWrq@0<|@TtS&34r%v0v0 zcVm-pu4p+vbNj9Bpf13Ge`!h!^C@#Z_}8890fzGyM^Vq@QG8AaZ--K$L{Nx-Ua3^n zala~2;eLGvGdpi#X?0=Ac4YxDFH{zR^MVJ$RD8~Sz7~RDb5U8UoFjU`YPf*3HUL&C zOG#%7?YYW%;JyXH``}*XT*Xuv>5OkJ*C_usHh8Tf7bq73kyT{1a)EM@ajLv-(*BKC`bi;Qy1++X18Z=J@E1 zb@EH-HAzW2Ba|{p}&fA@7HmhRcd_ z%1VnXxU+++*ijYHaCLNw70_&cXMW=__X}b6ZL8J z7E4Zqu@MZ#1&vra1jCEMh1NEBmjSiHvzOH^y)mvD6Q0|Y?FpWB5~}4)GdS5%Aqs+Xst}AVLEo_#DL3DmW%;@O>EJ0o4Ms6M_aJC|g8=Z3BR*#-|`Y@xM{= zHv(Ah(d(RIj;d6Z5>4zPn)rdaGv36lN$l!fmtFsfU^zvZsru`+eFxh9q1Cf(>&(%G zCD`JMV4q7^hWqzv`S1qSTJJpnV*`U#3$85 z|54X<1^Mmjk-rV(e{7N8aaa)+5_4gB2MZ(R<)MnAXs}a7q&hecQ7J*}yVdYX9_ z98Ks2vc}FsDsoTZcTA^aGlQWBK|(F5=$s<1cl)J zixl%mu&XUj?KQtNzc9a=Vp$Y33N1THJbPaqreZTy9WLjqr=vhY35=I@H)Ol~51Dz)&Yz28M+=PtA{Yyig#k)x5YpVaaShw)zU0o!z@9Y=NwVZc+=94Q_R+ zdZyA(ogOo}PvO&EjIxoI&v7Db)+z*unTO2Z9OmyavwDZNJ3dr#mY|C=3Bz@~I!EcQ zc2m4kOSPD`QA^c%!dAS#lH|bR-)FAV%$^+xh)W21T*Uu~#Ys#hsJ)oUzD)awP)FFt>e zQ1|KB93bYOSZ)2msq(YwIYmW$R!+>xADNhFN{Mv*^{@R7dL{k`(|=LESuH4;FVUK>OJa8^B+bjqm#{HAE*QI zWHmXe-lwjCRC_>is}ITls*k`@=4%wbwzXEsn8US>4-=7C1UAr7tUCYA%`6xZu7GE~ z%$f>o)h95=la|se)wQH^15E0A^=V*wR@B9)%xJ#(5TjCVmT(+E>?+)GpuQ;orM`@H zE}RpJ`4^*kR%>1*M;=r+sIRH7s~go#>SlF|`iAV)PV7doX$`qh~NW zgwf%QPN=uYl@BSf2i14gt(bG$K+$?jAu(!a)aGEctsqQ>uEp@k?1kZhF~XUf)r-$! z=!j30(dvilF6f4dn0C7{@$x`ACZYNf91cvKr*6RH`%K+~!v|mRsVLkJBSYMvtmf-x z_;Mm;W#KBZCXEGpa(BcKcn`Rr0gNmQ^S(%Lftd@xjB2Jc>SWYmu4D9MOyauJZeM)b zF{<%-hjnO7m~#0gCg2`_EBSX?C?8V4SAS4{RC>t=LGMrM&ln58sK2TQ)Pss${Z0K{ zJ*4!eVf7F7PsOGlR{v6usDG>PDksBxmyQ4X*{Dsj$#^O1yEdE6Zgbc)n-lNdQcIiH z=Ck<$k&azl$gm)n^kVTVydlwHiz-7EJc1=Z5-EeSvZ7!?Url*M4tKeSqoqq=Xb1 z!R%tx@M9XCgpX|pSt=YKJ#7bNTOWj|+J2Tv#2fBd5EG#_$_LvH;-U-x81;#&yjsE1 zObDBv%P_*KP5Z9Bo%%GTP}hNRn0v;}P^LEwC5Yiny~ zYcJ>9I$*$b!mBgy4{(jESTJy8I0P3V|A6n|YmIXWvRn)f-sNDL@SVbG$f@840_P~!RT@3$TSB@$ZK0KTW`xS>R{EF=5EC$cT7Xt`g27$v}cg3x;m59Hn4WG z9&b%nUx{dPVy+6AY8z}DBCG(>^|ee{Z5tdnLmJqswqcMuQ2#bb-D4YL8=KxD$lKuL znjZrJ@`F)5-H+|+$--o*OV~s~SdO`u(Gx(}SI{jnDN@f0u;`m?n_|iQ_}E=(n@l>_ zt@2D>rENN|vW@u#qbHtO9G?+!M$NJq)xEc{2~vzIxPxqSY=y$NHY8F$Cz6;8_t}bU z;aZ!E2Lsl5o-9c%nu~3Gz6q$okwturt+tiM*;((2Rbsx455stg`)uX4R$kKgb(V6L zhOtF#m3+~YtYJDZ+8&FZgo5Ep9#&#jFQMf#v-lzkuJwlfx(G@S{R(Zp$9AqdFJ1BT zer?D5mFrS;Ye&8{6^|1fR*n!dh|>Y7=wi0>>(kYR(a!bhg5K+8tuciksDCzh*T&XH zwFakc1(b1;!D+kJGC0p7Fg7~XwKxfhkrOw6!KmK0>uon!#;?UF2@|dxqg{oen_v`{ zYFYikDPbx0?_*hM$84IZ8*MA&rkU_${3#5D?%W)k%zc`RQ}Jny?f&`}LMEd<>sScr zz`p3Ttp5GPA~I9>xR1p^*hJgdo~$(xAZ>ea6`z%);{PX&ZChh$Z2aHP_HtZf+txs1 zzh-G{yyqHwGc@)VK)lH{woo0JASM&7%oY*@iUY0gCH``yFo4lM;#qGPNGWD3tWMjz zTnB7r2uDt>18h&)-nZ?r?M&7IqV4?|?N>)$Lrga*r0dG(hVy0^n4BaJfuxQoOwoc= zBjFp{xAkYtKt@liKV$ez?kfgDW)^nI8k@~>Dk8A%iko*IiePDO>?X?NA9 zVi==Clc`A9(0#=cODwneN;riqQRl#%Pm(2^mLW+ZL5j?p*H8#XHD+ZYOcIg#g|tLM`D6 zvex-L2!CQETFRpsV1x{gRKW=0z+8#&hg?};gA@~EB%|Xk$;9YrMn{;x3)9Db3ORDO zISjV`(xu&CBxUsxtA<`ZAxW`vo3a$Ehn~yo4QFMe;C-<@+ulpPQZW8|M#nNbh7(&y zncDl+8V=*Q;V`X?pM4PKrO;LOfp+fiK)HME1Fhv@3V~}MWXA>@_cqiD++0Q{HW0X( znK5~rne`tF+_&uG>kHfoj85VLcUF9WV-ZwZ3GO$t(uM7l&}XqR7;Uhiv=`W?CM_tZ zFj^%Rly#Pq_L=fu_Sx2QvVboq&$jw2gg|Kf9DAX?$R4)OwHGrwmC+fDp2cVpqa}=% zBl0MDGlAGnCwO#9+ww+3uB_z-YRYO47(qtj#F4QmHJI2Pwv3Kz$tbm8i>jw4u<+t0OJ zO_ot&KhJK8&VQNHFCVeCL3Au45Z(YI2hK@x!XWN)VssXxGx^Yf(Ned4ePVu7zU$Hp z4u=N9SJ`MUv;+$V_m#G5!P~fC!T6p%+dnOz`$WTwD$7Ep6;bSglnDo9Q7PP+d}0Qx zG5w-EoGO0A0zeh;hL+$lcc9Eg#AvV_t43^t&E?yweDVgv)rH+tNOAi$_G`JYvO118 zoY6UqhJ>)HZCW;0=b8P5I*)tpQpzfOQCwEp@8XU&jGLIOVzkiw3B&i?9@v_*yv;p$ zR9TNyRIRe#Tc4`AjE3t`CH4|~397JB(x@c2hb@Myd+cjDMMZIjLuzp+M11PSBB+BC z;p-g`+b>Hp%HSWdud_b|f%9R3+amki z#K3r+(^(Ry69WTlcw=&9aTOMU@ga?_j!_)fLP^B-wr{p?u>@M}oQyD95ff;&U7Oio zNM*~0#9Qv^J7ycwUToi4U!;{YTFIwkL5$u;W!cpD0Jh}xC-zTkoox5oKe1%>wA$br zp+kha;9kjI-0#${FBS&ysK6_j{0~DR`D0JZL1_Qdj@291LzV+0KU|JERvlhx|B~;W z;~<>eKCDkHt zb)<`0%+-uuVtS|XeI1M4Nl;1$N5F!5iP05+;_(=xD)^Rt0?J{h)X|1RU1-8xb2&Hz zq;UsT5}2c{qn!X_^h!prjuYOI6Mhx%$`+$4Dr>6i^&-A5P4RV7y=Y2z3q6{0ZR)0g z$bh4l1(n9=^}H!KL{}576PbsG*c}59nTK|Ow>O&JM);?0ijOFag}NikF~rf9NB1#$ z3)*>1;eC#gJiN~_#xXWIyw5SoktcuUn9S&{BFc@?+Zes0PMn)qtKPi`Cs66_Ch_Bx zAP;xThQ{s{3wPs-PRI>x35d90!C-is1drK?~q+;)06t|Fm-AB*^-$Zilpc7h57AjE?Vst}4$ zflpBcCg8jT-nBg z%QXk*Q%kc0Q%;(eGY8SD!pKB8omlV-#vuNsq9{~VBpwS%UrUVUrd0ECV&3LT?!DA4 z*V3$x(^_c9@s_{CJC4zf3Wszt&cj3%f zABc~My1QxJdENN=2z6V5{kLeD$w%gRW2Kh-$eh+k>r02hEj}tbva|>x^JuvU@@I56 z!$D(yRD3}=H#(EYyu{m-Ks8W1oum4M(LIedL*wW225ZB#;i4JoBps__!P?Il{S-$l zCi25O$QDym*}Z%B+=;Uge$BtK;*=AZ#%kj@rY{)X+Zd);|K(}@#ug#u5ETi-Cc-!w{)sU{>&zy%SG zh6A9vd6>k>yvvCujm%PqU7OEwelNu0Pt7##Y^^G(X+JXh1Nc`bp+RoNOg{MHYnKH3 z7HNw)nqL?_*a(hu;&MwfIgVfBa_d0d`FNprk#;ekj~$tU3E64dkg_nsl(|U>kyOY( zAQ%&#fH-%Po4Z2Zs$I_M_?=teuo6;A(4(=AyjHu8Q?kVxEq^e2sELENxm|(29FbLs z^v*&9c%RgZYtV}i z@VbxkUX&p4nrzo&+T*p=OO#0j)r+Gv++NK+8>)bxr^ypXY0wt_DAAGYwWoneQkZ0G z42|}@m~nS&I6DgVrISE>#{JDC6(wV%u+oH<3lm+IU_`3R3rRM^_thF930NgbQ6khXEQPgKZ< z6|$F1;S25Ra6ysyFHvU~=PA6-&P?jsbe+yjXI7$4CY{10WGb0Fk%v>}E|?<1Dif9V zbN1(zc4rc{)KU(SSpPZCa1KhUv?r5#KzU9^Cb--slX1W(lBjaHa|Ew4n@N39WlTR? z=VW8q8FHMXtwG@&Cy#NC=hgPIhD9GH^-c^6XRdP+9~PNR$uftZ$q(ZP`B|u3_zhde zOauO5T~l7k?O$s^!Z(~d4~8CBDB-HaccUs|0a!Uz;ZTCF zQL9OVn{1M0jyRXd`Oc+D_=hrS2z04nA)IRqN+UJV+!66snNG%AHJnMKQnyMw7^`== z^AcWbY!Z)T(g+NKNns3vX_Kq)UxL^xEn>$sNo;Ho+#p})yfKN`aZD1KK91+{{0XK6 zVU3G~47uSr-M2Y!=X6hC(xlXMJ9fwDUg=x~1lDSWNx4iy=8pVnkw|&&z!|ZT#6Km- zf57=5uQ{Jd1)}Ce8YOE4J0GEm)7s9pob<_Yd5!!UF>Bhn&iND{zP*^zTckhAL+EsM zkW057Vlu+DQynQT#_=WofS>QEsA*bNP=}6bJT-|xm|KJG z7ihwhEMz>Wjc-d7itlme5vQc9Yxp)r0_$ey7LIi$lV&%AHQ%|l4%S&vzZTZ9r4{pm zv~@kC{Oh3*ZX%2F^GjzlsuQzjp59Xy-Diq)D_t)wZe@}bC>QiIMxMBfv&8)!j;dtJ=r26^*5(?YAOF+VT z^-4+uJ>mPH9Z&#^Rr5qq;9^n0AETix9-2`L-X7q20BVfs3J)Va;+n$1(Q}=YM-aHNb-KV*72k>+>0=Qr<-YNkhz`(UZLaf7kJ_D zZ4$4@zfp@9={K6JeG_UIy8Izg`yiE{^Vaxb#spTB$FPmmky;#Twem$UXJTYf$OPeaLsgmUkBGZ-acQw3^mP6S=TSXd4S`5 zTHt)P8Jzj9!*y^zBf4x-JW)j~uTFDF|8>~iLfJnrkiOUq(lKu6{3NEn0H!7<0%``G z+YNNwr7XQH(7oC;Iw{-Deaf|c_zGs7*qn?fEou&*0er1EzSji4jZNc|Z+Ew?gYR|V ztCPZ}sX^n0%yoC+$TkaPZ#07};?AstY)gX_Lrr3Xes%Zb*xnM@-WJ#zio8+H@otD^5HRW8xJ8gM*^T3<)DlvX`@|n}BcC8#XO?T1yXdy^L0OychWnO8 zz8lL)mr&8}Tr>vCoJre6W8k8FC1tWM_Y@kFl$1$Y^Qzn70@h}Ul14R^A%;0y=br-|nmv%DggT_mJ}k$Qc$1gAui#Dom`R_;GQ7F3M!2B+8uzvC>)hA7 zZ*bqpq|cbNhe@9^=?f-($)vARwe+G`OK-C1|3tJDE~17ljYHm*4B53Q$Ti83$Ya}h zozS1y7LzRO$Y9$zZGrPUNP=#BFpz;4X96 zJ$5cbu+D)A_%mckQ}7-a;Kj1@R{{SU;2TilNwdbZC${oDU@dhICOiaEr6+(+oX(UX zLSEG7>Eu2!e(cc)w-|CVVkcUBQM=0Uv<3kua)J*D0uHAnplytR4i?k?un71wL4c>T zrweycoX(UpI0dP);Lfmo>|$di2|o;SPbSBIMBwM!I}I5S$KS6G{=cCZ>ujJkvi9-} z1dcN}j-wohOq;}ktxi6KtTxKn-Z>@{+cO3@_%5d`3mj^bIIzi?ghSzDDn;hs>9spa zwG5-2Vbih3C=ht;z=HwC!`y1)3X`_&5P8(r69Qd4+z59IXn-#W;Tn|6q@_8crG{90 z+E^c8sl&Im6Kc^j4+zV6%iIE^H)YEztr23S%Ve@gOnm`o$RMw`q^ISxS0cR|~9h1}QX54IQ0=lmOKEAC9<-*4X zTnbH2SogJnzm~)A5%7pIXd3=Hz~9W_KNs+U=HPDt{GA;BO95{*2Y(~r@8$5=EC>Cq znuEU{@WTCz-FU$B4W}mgvmNk{^ZNG-_+WGJSRi@UbNFus{K?J1e+c*&IQ$O+zFl+h z?*o1VhyO;vcW4fNC*Z{*@jC(EsTp|B+kk%;^~+rZJbYCtDi5%Kjt-e6E$~7%_(hJPg?EBmhQ&LmJY;5wuxlNO`_Nj6yI_bJq3#Fl*IpNO;yh? zf_OPo2y_*`kXXM-XD5%pRh42$ZIYITzr& z0G=tpAw!x5KM&xa0UYWav_pn839h~+zvty{V>*HFv&rWQcpV1~!`1mQ{msL;!q&N- zT7l^O26(>XlH3%C&PTyrP3#gd@2}7v3BRb!go3J{EF6-n@lB{xfbmbX>`H;~LaSws zf_wi4_`jUMiv|30z&E;9l*GLtv2~n@z=at+k1+ROA(w7#qbc@}d@hhMSyXu`s!R>& z^8ql8Bfm^QUXc=c%rNxfdKtdJvsUWz<(xD1Z_j9AQ7&P$`miWBA&b!CR|&o}D(;k? z@_WAafQH45{=Np#(?Uf>`4OJ)DygOL4Etapn4?6c*YTF7T$1~+!u0VaxqO3wyeVZ% zV@q;hoh7+^BX8-kE@gdKm;3m-T*jIc?Ys?$8VooOMu}(%`+Qtp zdyj7|lb^rG_au{FVDgIy1V+X>Ua`pLzz>jwMea$6GMr}-w>u6-qOowuy0O;xjPF^V zpsQ5GLxrjquk<|=%O&l5-uFWM6ij`#uEN6?8=Qjq(CVdlAKB-KKDx`d!S|Z)b)HFp za~Ujtg~=P3{3;G|h*qZL#ANX(H6!Zbp{2!Lv7+{FdOEHEa?*E3yt2 z$3z_;*Eqf$8$1o4#fQ+>zHfc&R{8e%_WQnJ@;gj^m&sdK`M&dg@B4wt?=g8BledfE zWLW9yN-Js*v01iwa%B{dt+7b=a0qi{Vy490@e zk(Ug!FRIN#ZUYe#Wq~{D_h`2mzyI_dMvvyj*}a3wJO4{|*TdlJVPWu7zr=g=hByjH z!Cn6)iUvJOJ8>j_&3BjI<#(eYxR@3*;1kiIA2Ipk{}NHG7yW7e4BxtX0-#Z0Sq6(S7c4H3|> z2GxGoSmnL_ef)iEE8oxLZ<&mPfx)nKp+jO0)^hA|XE@J<9y$K(r~2=r1N8`QH#V5LKQryiNXUAY0&H$R(mI zupJe$>K`Wm`#;L6bNuHvkW~$-Xf3F?5L8_3Uk)m;;|b;}pyes1P^L)#BNdnXuWUd? zeWu?L>)jjtH-hOfB1G>hDpPEN>GedJSkG9}aze#eadktjC`<9MKDv`)= zmf%Rp&Jb>7;q#9la2&P>*N4HXNQjntkLt57j_O(ebC~K0RB24X$`gY6znJPGL52V2 zw7rj|(RTc%b@vqiYmTFi!~RX$3jY@W8~!)_Z)vw^>$TVYTeUUrA^z=Jn(G#=RCD`3 z)Xvcs`9H$h<8InC|7Y4jZLI$b?Vz)t)AWDs^l3jkySO?yTRW-$2mg=G(KsSB+!=LV z=?wdSb3Wp7`~Pr0;C#+`)VbNE`~Pw7cJ6R~?L3esX*;zqwQXrOPg_q9&%iXNr!^+) zK;IY`r9INpynnlM5#VIS7+sVci{p;CHz&u4q;>S9d0al9=WWmKv~FqLJwJN(dpmnu zc@BH+X}vv_-hQ5Wo~3C69J?LgJ8w%n-LW%mkn3T0AGh1}uj?1rmuVy1ozg~mzj5tw zZE_o~XI*R3CgR$$eYm~Ai7Q0fdzDjEic6P%!i53H zCw_Kmv5b{HvioIH=UeUQ2m zIgI8Y$5gr>`W7bNA=XAWFeQgO01>3e!|cT74-b3$q+xg%llCHP zr(yUKQ$XPXCc!?qjPLVeLsl8e6ut+@cTTZ^hTS>rU1ASW8G*P0zO{wm2Sg+&@SVNL zQ!wCImVzCl7nm|09#9_sh+tCbc^)>4XkBF@55H7$89b>76qX+1p`X(IJp5Y8Ggq4r zAf@|1NbUZZ`2_9>dj&UzK>?d@;I^=La9`MV+!*!&?hN|~H(Pv)TLiwt&0zm4Kj!|rWcVZO%bc)E~+CwA_g)<)>ckBSW6W{p z1T)vnGbft`=9%U+Tx=Y_p?I!YV%FZ6TWv0g-Pw9BE>3LfBGNm}yUcsIe&$graNfal zcaKO_rK=f?*Agq=FUy)m^L77 z5T7U*{utDJpl#!00llh>H-~);XH@N_r-if#?x`@p`qVwe3SUK9kfTU^5esRI$9wWw zw*|43h-kF1{*8;_l4N=viQZs>rkI^b;u@y}*O#AGdx5z_9IodX4B}UqbIh&yzO{8x z`F~rk(Uw~ndE_Wm)0^pSMKGszs2>hkE?w|*0&hCfmg1tm!Z0$BC9bDU@0{K?{S+SZ z+8R(yX(RZP-Yu54(}ycE6gahR@&a*c8# zehd%CLr3G4YjHvx#^a|Qp>lBmH#0W+DvB&lJcFysYYo79TDht56+80>WRKvrt{yU| z^2s>x%aP5jbJWqo`W88|rmmrxQuQMb8<%c;0|3%(pp*eA3mJ<;$JhoyOb)-b7Q1J$ zL&x!3l2gVyYX@m;YX}DIZs(qQ^uoTFgHyAdUO1=|E^CprG-}cOEv{>^OkozcMUII- zFq?O3pVY$#1IB{{798Icq>>2Z@IgTUil9~~&VshF zFo@)pDVbe6{9O;@w2)qxSZ-mj#$m$*C$565Z))*)>;`!w0|tj7>&=4I$pa?kENG?- zzY)CFJVj2}j3)`~(0pV-weE7o9rSP|SBcF($&>N)O3A}_%OFBBp0#`iqy>M)yaq5u z`OE)?^?&>?p2BNjy@b~w_TKsiPvJF4^%7nKu4C}h4sx$S@(bT^uK`}#LE$yn>7F9I z23%Oxd%33wuK_KAiM^!cdZ|#y|6js9~0l>4F|To2EtaeEVhP! zV_pMc#aOlsH|#90fv`5X`ylayM}4_UkNx1IaQQ80v%CgE;zHmjV95Wq`kM8Q^b;_*-s`C-@8a zi~9@ezj~W}lU^2F{lDB_V7<~z?k|YH(oF6z;IA~3`wJTXXVu6@cCEVrPJ?@e>j1$p zc>k~%_{wAE#s4{D|IZmK9g_a|U!Ad}7wJv*hLT~(yfuQ1B%{b^GKP#LJ<%tt!ka^%H~AeHIaqzZ|AtC1pj0aE=gLfYUZ zWGOj^EFEauvCnTtluU*OBYV4dh00 z6SOXOwp3VD@mAg__v$wsn?Y$jXC8{|#$7I~YzL*6A@ z$$MlQ*-qXkJ8((r2joMti|i&Jk&nqIAN7@(1~o943EZuK%BNJg(;IpPi1Yx%y{&<7)2L|8tK2 z|HwIRI!vGGH=zmjMwc^sDWjJ$dIh7`Fp9w78yH2<@6C+DAN zbQA0MzmGG?OyB@5GnLHL-IXeJr|$0V?k;y-ktB74Bn4sx8o}KT?(VJ!=Wy^}-kJLQvJRIK)V368$f#ibO=Dl0dyKb7Xfq)K(_((06B1tb7S9e~sYNIihm2S@{eGz3U% z0ckxT(E(`~kVXM%9FQgfX&R7b0f_}jn*oUnNb`WS07w!*+6_qi0qHOx9S5XSfOHm+ zE&$S7Kza{IUjXSlAW7p0(ky_qA-@EWmjd!KK$dows<%PG(ctm@)#gb0rCbwW&$!BkT(G`7m)dYybX{S09gdc2LSm9ARhpnwycobs0W7s# z(pK32dqYvr|GzgB^#b)G^%C_m^$PVW^&0g$^#=7O^%nIu^$zte^&a&;^#S!E^%3RakN>U-)3>PPA)>SyW~>c7;l)c>g8sNbnSs6VN{ zsK2Ry@I^R;OOO5HFpl69d@;TR{|jG=FTJPZ%VBk)K(3XjHP z@K`(!kH-`6L_7&k##8WAJPl9BGw@723(v-L@LW6(&&Lb!Lc9ns#!K*0ybLeLEAUFZ z3a`d%@LIeMug4qkM!X4c##``KybT9d=wwU$MFe#5}(4S@fmy;-+*t#nK%n)<8$~Xd^5fU=ipqNhx2g(K96t3 zx8d9I1zd=Wa4{~yci=nmUHEQ%555=QhwsM^;0N(T_+k7AeiT23AIDGNf8!_dQ}}88 z41N|rho8qU;1}^r_+|VGeigrlU&n9YH}PBeZTt>?7r%$!#~aan(!t32!A4g2qc1tU?PMFCBlesB7%q{qKIfBhKMENhRPM64m!66=Wd1dX5*3}ToVAx4QY zVw{*DCW$FxnwTMGi4DX?f=RFlHZezRA~q9S2oAv|cm$si5c9-VVjHoYSRjOih!7JJ zVh6F4*hTCn_5e5@zzG1B?v_Xba58{X0GtZoGytaqSgO}E0h|TkYyjr~SUNGB2jF}F zOB?!y04@S>F@Q?|TngYa0G9)}0>G63t^#m1fNKC;3*b5c*8{i#z>NTI0&p{cTL9b& z;5GmQ0Jj6U1HhdC?gDT(fO`Pk3*bHg_XBtUz=HrD0`M9DuLbZr0Ivry4Zw5&GXN|N z35)=E6u@Hu9tZFQfF}Vw1>k7_&j5H9z#9O(5x`6Uvj8k@%Fh9K6M#1Zcng3z0OkUi z2Vg#c1puB0@KykC1MqeLF92BD+!p~@3}6X>cK~=NfOi3SH-M#o)LsBfjjA+Jb^yQ! z0elF+hXH&9z()am48X?$d;-9K1NbC>PXYKefX@K5;}{1L#P0Q?!iUjY0sfWHFxKLCFN@OJ?J0Ps%${{rxD z0RI8VB7i^uApt~sT^$Ap0uX76W-&mP0OT)#ECt9ifGh`y3_xT7vH~C|Krn#F0Yn}k z3II_A2o)eWKnQ>+0Yn)fDgaRhh#ElD0ippAO@L?tL>nMF0MP}A9zgT~VgL|BfEWS9 z7$7D9F$IVjK+FMR0T4@otOSS^K&$~`0}xw)*a5^IAgci401!ujI03{NAT9uL1&A9! z+yUYN5Kn-30mK_1J^)z_5MO}!0mL650RRaENDx4R0TKd`P=JI1Bpe_S0Eq-h6hNW@ z5(AJ}fW!eL9v}$-Nd!m|K#~EH0+3XIqyZ!yAQ=G31V|P@vH_90I38>6+o&1QUj1$fYbq`9v}?>X#_|UK$-#40+3dKv;hPF zq#Ym~0ORA0Pt&83f1>K-K_cEkM=*WIaG=0HFhf0gz#Ui~wX5 zAY%X-2gn3KCIK=9kZFL-0Av;*8vwEqAWVR;0Kx{y96&Y!WHUgv0E7b&EV?1;{plYzN2!K!gAh0YnTC2|#uLWG6s&0c1Bo_5frrK=uJ-KR^xuvfKtTY70w{|CWeK4C1t?1aWf`C>2NW4Vkp+|$fPw-F1}JiXA`d7EfT9Q} zR6xOj^e{U`2~d;)MFmh)0Ywc^)B!~UP&5HW3sAHHMF&uH0Ywi`^Z~^HPz(XZ2vCdx z#RO1H0mTeZ%mKv$P%HsuC7@UViZ!6v0E#W3*a3^RXX|P`ol}8m&MU;x-D1j=W%BU);hN`0)s3xj~ zYNOJeJ*tQ5qXwuUYJ?i2Ca5$DkD8+vs3p1*wL+~?8}$G3>*y-f0d+*3P-oNyb^Tw! z9Q8mwQ7_aR^+8vozNj<_js~DXXfPUrhN5Br3uvQ}XcQWa#-Ooi92$@QFG-z*CZj26 zDw>9-qZz0)_>5+wIcP4LhvuUNXyN}7%4i8%ik6|}Xa!n{R-x7Z3lgJsXg%71Hlj^v zGunc-qHQQZ+tCiR6Yct6#24*F`_O)L03Ae!&^72PjsSZ(6i_{^gMb2y(rBhT}H2$6TOArM(?0^(R=89^a1)1eS|(npP*0CXXtbE1^N0^)q1{q?I5e6A!kO>BvVvrdInPZRz23cazN({2XAZrY= z!5~`P!k3cpTf4C=<9 z9t`Tmpgs)h$DjcW8pNO>3|fOhYcXg&2GKBxjzJ6z8pfaz3>w9tF$@~Vpa~3`#Gonc ze+TGhFlZKoHek?33}Rvs3xn7gG>1W(FlaLdZNVT825~Wnhe3P{5@66g25rTlZ5Xs2 zgBCDIh(RI@5@V1AgLYuhP7Kr? zUU};O5@P@F^DOp6`dE_|n|h1%m6^({6zR1i&(Mql>4cAWdR018nYqa5|K<;+vtQX+ z9u=8+`8Coes`4h~%_M8(Ez-EWzo&U+z4Sap@oMQ+uKzb<_WzsQOqPCC`hPzq9Xv~K zgBC4Wq_jv|+Htj&%3C+-2E3ax6d4&A6&XVrM;SkvESXxF^)dn(k<0;^Lo!EXj?0{s z=AF;WT$H&Yb6w_^%w3shG9P6=%ls<~%gV?SvRbm1vbM6Wvc9qrvXQdUvMI8KvK6v* zvOr2;1u12yp= zrPERxU6fMjx|BY5rPO&WrOitzW!_2Y@7+`EMwp7}hMKO7diYZ~Lm0ySL31*J1#B4BoX`a#rbH}`})tEmPgoR=eSTq)gC1NR9I+lgyVg*<+R)$q#HCR2? zgtcPrSQpld4Pa}q^%w&i#U`+6Yy-x^Heno$k8Q;kFfq0h+k@@L4q->J6WA&2EOr6A zj9tTSVt26n*dy#I_5yp2y~RFYpRuplckGw+CKXwZBKMcva=8_9a&n4tgq(_;x}27r zuAG6Kv7DKlrJS{#ot%T5vz(ipr<{+RpIo3^h+Mc_lw7P_f?Tp(np~z_wp@)|tK5*> zu-vHJxZI@NwA?1SEplABopOidj>sL8yC`={?y=l=c}O0W*OJ$f*ONDpH%W&y~-YFOaX4pON1n&y;7&Z<604&z0xP&&zLmVYY$M*h13tgu2sRY6@rQ$bI`K*31CM8QnKNx@GcN+DArTOn5= zU!h!~Lt#*1R)MXsT|uZIR@k9%O5w7?J%z`Lixf$Uup&iqiQ-bl<%-IRUW!4AF^Z{* z>57?(*^0S}`HF>##fqhh<%*q(bj1ydOhvZhCdFNfhZV0XURS)Scw6zV;(f)3ijNha zDn3_yNu^MiP?u7dQ)Q_rRgQ{NZK!tCRa8f+Gu4&qPK}_JQp>58)M{!iwVv8YZKY08 zXQ&&fOe&kYiMoZ#qy9}jMLk13Csjh1q&n!fRONh^YMkFvg+r3+n`Kh7&uj#Mo?kP3o7QaM1Dih;#aDIg;)>rJI4y`{9Aw~?0euF@hrSz3UnON;MpY2jTW zEt-d<1@n4ou{vW>ExvbS=+a=CJ)aD-`l$M<`l|-22CJs2HmkO(0@V)HF4Z2@KGjLp zovOQ4_p0t!J*awE^{DD8)px4D)G#%9H3c;#H5D~AH4QZ_H61lmwP>|iwRp8ewPdwa zwRE*iwQRLqwNkYvwHCEDwRW`uwGp*3wF$K;wHdVyYD~2QYKPR0s2x)~p>|U3wAwYb zA8NnUeycB1C#l2g6!j(QE7VQZ&DAZ{t<-JQ?bKJPyQ?Rwr>du`XR2qb=c?zcH>qz_ zXQ{K*H>qz?=c@D7=hb(sKUROL{#^Z~`fK%n)ZeLpQ2(UIphO%qKsO$*JHnvR;Wn(>;6n#r1} zn(3OEn%SDUn)#ZAn#G!>n&q06n$?;knq!*NnzNc4HCdW-nwvGZYYH{5XkOF2p?OR5 zj^;hh2byoSR%l^b@>+^oxR#QZik6y|u~wi~uvVy6xK^ZAv{tNEyjGFckk(qQ^;&eT zVXaZEaji)$fz~ChD_YmIZfM=qx~+9b>z>vFZAhD}jc6~{{!4qAwv6@)ZFOxgZ69r4 zZGY_m?I7)7?NIG-?F#KG?HcVm?FQ{8?G|bKo~|v@mT2$P-mSe?d%yNU?ZevVwcl%h z)c&mful9f1-?e{g|JIS$vC*;9S*7Esjy}Q>#<2)2P#|)2h>{ zGpDmzhoi&O5$J5y*{&nh*`aep=Y`HIoi{pfb>8WG(D|tIS?6C}d0jte!w`m)=>u^LiKcuIOFUyP!`H%)Gtd@?mPH8-_1 zwKBCawKH91>S*e08e`gM+HBft+Gg5r+G*Nt+H2Zp$}~M~`pNW*=~vVLOuw7{H2rP1 z$P6-bG;=faF!M6=G4nO^Hw!ciHVZW?H|sasYPQ`>XeKg~nC&#%ZMN5JpV_}=|CxO^ z`)T&ue33cH95$zztD1Y5dzt%~uQvBH4=@ih4>1ol&odt~Uu(YJoMz52A2A;@pD>>^ zzhZvP{D%20^E>AE%paIPGJj$bXi;WSVNqpKZBc7cZ_#McY|&ydYH`8hlEoE^YZf;w zZdu&1xM%Uel47Z0>1P>W8Dtr38EP4B8EF}98DqKDa=j(pa@caza@=y#a@um%@}%X1 zmC#DXm0BxxR_d)ZSZTDE4O>lHF|D>*iLLHiJ+yjd_0t-$mbF&4*0XlC&b4l|Znkc9tO3->tgF>>tX9<8)}F`p4q*$du0#X+u8@)XV{n6SK3$E z*V@qw9|v^@ z69;>TaECO93Wp&Fro)`W76*<4-$CH8%|YlOcG&5#$Kj~M35N>~Hys{1Jau^C@Y3Op z!xxAD9DX?bcKGAC*iqS0$5G$W$kD_x$}!ck$g$S3+i}=&+Hs2`&vD*yyW?)heUAGb zPdJ`(JmdJ%@vY+r$Ip&m9ltyNbo}FlIw?7+I;lHpI+;0HIoUY*I|Vw0IE6VyIAu8% zI{~LbCxO#~lh|p8(=MmIP6wP0JDqSk>-5O!iPLkZmrk#pVP_d<6=zLnTW5P`M`vee zH)nt680SRiWal#Hdgnpsac92sPUl0;7o2Z9zjFTM{M%)j3)RKR#nr{b#nZ*x#n&ak zCD0|yrOTz?Wyoc%%X$~O%ZSUk%e>2FmuoIJU2eJDak=mE$mOxiPggBhV^=d*3)hvd zR<1U#cCM>jU0gFlxQ`t`}Udx&Cnd z?FPA#+z_`VZp+-3yXm>bxW&39xFxxzx@EX!x#ha$xfQtGb$j6U*zJkibGH|6uif6d zeQ^8i_SNmX+b_4@?vOj|zSw=K`!aWRcLR50cQbbj_c-@N_Z0VZ_X77~_Y(JV_ciY8 z-5Ktq?i21)?)%&ix*v8w>VCrgl>1rt2OfyW5|3pb%ROX0Fb@R}MGte2P>%?YD32JA zIFCe+6pu=eF^@@)8IM_yjUH@|%^q7k4thNFc;fNQg zP%n8eMK5(P4KFP(2QPQ8WUo}ObgxXW9It$@Vy||uPOl!XKCc0<5wBUVIj?!I%U;*K zZhGDFy5n`v>!H^Zucux=y;pi$d)s>3dpml&c)NLfc!zoCdpCQJc#nBcde3-o^k#W) z^4{aU&-oiKzOwrI>RYRCum0?-|5qr?%VIn^PTtI=DXl4^4;OP+xL?158vN@kRQnp@muV-)KA7w*3ZDt$}iF{ z#xKqr~ze^q~5 ze^-BB|8V~V|7`ze{|){e|GoZ){7?Cx^*`_b+W)8jp8!$-96$-c1C#^Q0yG1(1Iz>5 z0z3nJ0{j930%8Jk0}29)1Ihv_0(t|+1EvCI1DFBqfE@vU2b>N#7jQA)a=@K{`vDIF z-Ua*$lnulJ*#| z3!Dht9{6|Q&A>Z>_W~aVJ`Q{v_$P=I1P3h+`YT8wNH54B$T-L>$RfxkC?qI6C@Lr> zC@v^Js3@p3s641LXdy@(v?FMD(7vF9K}Uj)2Av2x6?8V}TF}j)+d+4O{tG4tFALTT z)(+MSHVifnwhnd9Xf*%Aw3Vs^=JY;#uiV(RF`4GhrJVZG}Ekr%UE~GG|IHWA3BBVN`E~Fu(DWoH$ zD});&2-z00JwzBH3E360H)LPP)sXKYze4_mLZPHkIFu5)B-AX_GSoWMCe$v}A=Ek4 zB{Vv;CbT}ZF|;|fHMBjnE371}KCC}%C~RFAJ#09P6($PX5w<&QU)X`LV`10AZid|n zyC3#2>~+}Bus`9Xa3p+jxNNvaxK_AsxPG`{xI?&exLdeKxL0^scw~4?czk$6cyoAb zI0)|u?+)(`9}k}lp9$X>&JN!demeY0_?z%g5u}I}5pofV5k!PTgmZ*zgnNWn#OesY zi13KWi2R6#h~|jai1vuC2xbI3Vsiv1f)_C#u`S|I#F2>O5r0RVj<^=_Z{*_0rIE`c zS47H1T1VPNu8MSwbcuA2^o$IRjEIbmjEyXcEQuV59E+TcoQd2Rc{uV|$ zM_!D)9(gnJdF1EFuaVy)e?@6WnMGMft&FmcvW;35U`9ts83P6=H2;SH(KUI>)-jdd7OkhQx-&#>D2v z7Q`0Emc>@aGGa$#$73gBXJR+TvSJ0XTVs#LUW~mGdp-76?2p*rapX88ZgJewIGMN= zaVl{(aUOBralUZ@afNXuablPsN{&KOcW3{#yK<_?Pi-;@`!8Ops5YCJ+hA32F(N3EBx}2^I;C z3BCyd3Bd_r3FQe@2{j3I35^LY32h0T3Ec?;31bP92{Q>B6AmXFOE{5mGU0T>xr7S| z*Awn1yh(VM@G;>_A~jJdQ6*6=Q8Q5|Q7_RX(Jav>(Ie42(Kj(5u`sbDu`ID7u{yCX zu_3WF5hV5`G7?7}oTPn82a@h5y-9kP z^fBp6vU0LovPQC2vTm|LvQe^qvP1IfWdG!#4jHpM>0ImI<4 zFeNS}F(oA>J*7RRE2Sr;FJ&NQP0G5I>68sAtdzNwohg@79;7@@d6x1rb!nWWk> zRX&xPN~G$g`lrUE#-}Ewrlz)~cBFQt_N4Zu4yLY2ok-oCdLs2y>ec}hmZU99lTAa@w9{6mxu$ufd8hfNWu@h$<);;<6{nS@Rit&M(bG1hvC=lB zang>b{hf9??QGilv`cAM((b2yNc)`*rNilq({)2-6o(lgRa(<{=e z)9ccQ(?`=M(x=jA(l@5F(s!ocNPn9CBK>vx+l*xyG8t%wT!umho}rXsnh}zbn30l^ zo{^Q&mC>8gpD~!RHiMSI$lzxj%($3wCF6R=t&F=FPcpt_e9icl@iXI3=F&`+OtnnS zOr1>qOq)!fOyA6a%;3zh%hH6l8A8+@E!BA)?Zo6vox|Sv#hdgvsPs}Wrbv=W=U6pX60rTWVL0j&!T6IWQ}J{Wi4bK z$vT$xch>2wb6HQazGQvP`kwVGdr>wu+aTL0+ceuE+bY{9J0?3WJ25*YJ6(Fxv@5$O zyFYsIY)C&lV_K=D$gm;B`-8DJFh0MKCdaSHLpF7 zmA5HxOCC3mpSLw{d*0E!TY0bZ-sXMC`<(YRpU79qSIgJP*UHz;*Uz`k_s>trPtH%v z&&DiKb+6X-<5wV|7`w+{LA^*^1tSP&;ObKyI@fPxd17UFVHWr zFK{eyDR3|FD#$9xEyyn@EGRB0E2t;{1&o5Zf-MESg872&1(yo07F;j5S#Z1HZo&P6 zFNKuCzY3QX$`)dUc7+awj)l&Ju7w_j-i50R(+evKs|)K28w*#bw16#cPY# z7c+`SipPp4il>Tq74IqDUwp9maPiUNvyvAjuSyq}{#ClXRJIf?l`B;!U0G^fYFE0d)UnjL z)U`CRG`TdbG@~@DG^aGLw5znIw7+z)bWQ2HQd;R;sibsQ>E6-E)LEwd|09QWj4$MV4Pkn-^IsPfqI zs`8rh`tru|=JK}k_VRV*Gv!;$7s|!uJInW!-zdLTez*L7`NQ%j< zpyEu$xr&Pwmn)uCe69Fi@vCxCCAreD(xlR~(!A2L(yG$7(!SEGGNv-OvY@iKvaGVQ zvb%D$a-wp&aziDnQc`)M@>J#7$_tg3E1y+ehdsA^pmy=qg{j;h^N`>GCB9jUrj^}Om;)jw75t3Fjzs_|;& zYPD+3YMpAkYTxRB>fq|I>d5Mx>e}jt>gMXU>W=CS)y(R->dn=hYF@RV`e60N>MPaP zt8Z1`ts&JQHI$kqHA`!j*T~kOH99pmHTE@*H7+&oHOV!pH5oNoH90l;HH9^GH4Qb* zHLW%MHQbs#HT!E0)f}xkQS+qcS6me*F**3`Dw4%cp~<<#}pd$aaV?fu$EwSVfMI=GHfx1?@q-SRq( zI?FoiI=ecDI_J9dx~#hFy4#o(^sJm5n zx9)!3$9lPXoqGLxqk7YNi~6wo^!m*DocjFwqWZ!5>H67vX8m0Kmij&QXX?+@U#!1U zf4%-y{g3)z4T~Dc4U`6@2IB_P28#x(2HS?{hMb1HhQfxDhVq8>4a^31!{!EVgP`GT z!@Y(F4UZe1HN0&2*(lSvqEW6@P&YFynI-&oLC)L7bB(OBKs+c@4h z**Md{zEpA%cB-4a8=``6i*)=&dIXAgAMKonLWjEzD z6*iSLfhJlLqiM8hqG`HmThpPYBTdJfPBxusy5ID^>0{HErvI9LG|M(?G;1~MHXAe> zH@h^4G>0`uHpeu_Hy1TGH@7yoH+MDnHcvM5o9COiH;bBgG@oj|-F&zCLG$D0XU*SQ zmbNT!S=Mt@5qZR;5uBq4V@a0)ZE54S@ue$(woAwOF10;wdnO&%d(-v~pg`K>(*9Tb^7a+&+U?fucI^)B&h2jPN$n}^>Ft^A+3k7l1?_F^L+vx|8`@dzo7y?; zlJ*nrr`pf9UueJF{-*tH`}_8f?VsDfcEBB2hjxcm#|e(PG&rPpQHWzuEdwX(~m%f2hBE1|2bYp82& z7p-f!YpiRsYo=>&*QKs^T_3x?bp6-$qw9A!)UDBN+a1##-<{N*+MUsz-JRE6*uAEk z+r6WEfA^v8qunRE&vjqyzS4cY`&ReY9=V>CJyty)J)S+OJ?TAJJ-IyvJ;gm`J!^W{ zJ%@UZ_MGTB)pNGzLeJ%%550?f4SS7y&3Y|+t$Xcy9eSO66MD;fn|s@OJ9@i&`+5g^ zIla8z`QGilqTU_7yL)f-e(FQ}u~`}z;|AL&2d zf3pAMfWpAa0qX&~0fzzS0k;9qfy{yCfr){sf!P7(z}&!=0p7r=fd>P>2L23^29d!f zgUbeG2Q3Eu2XhAV1`7vE2FnMl25Se$1{Ve|4_+I*Ie2IA{@|m*r-LtsC_}14Mnk4U z7DHA;wnM9i;)jxkQin2zvWN193WrLD28WnKTZa~g#6vrW_6+SGx;=Dv=)utAp=U!c zhu*9~*O;vdSQE4+bWOyX=rwU`64$h@nOd`J&7L*;*Bn}Nbj^u1r`9}Q^J}g0TGh20 zYqi(vtu0G*izKt%VOX$1kd+7)0hv~=Yf74IX&(SZ^ zFVnBmuhVbRZ`1G6@6#XBAJd=GpVME`U(^4gzoUPkf1-b(f2Dt;|DgY(|6xE3G6P{O zX8gri#*k%T3*v~k~ILtW8IL`Q+af)$agXtU@rdz+@r?0;@rv<=@s{zP@saVF@h{^)#&^a~#_!=p!=zzk zc**dxVcB79SYa3+RvuOx)*RLu)*m(+HXXJbb{h^GjvS5|jvr1MP94q|&K}MiE*Ksi zo*14Qo*CXS%o^S_yk(d>%pVpHpBuh7d}a9h@U7vy!w-fZ4?i1zIs9h$-SEfZ&m(Fh znj_jHIwN``1|xI zn@6^d92z+?a(v|E$eEF=BiBZ5jJz6^AEk~Gqe`PHqiUn-qne}Iqk*F#qhX`rqmiT0 zqcNj#qY0zAqm0qf(ecsA(dp3*qpZe==;%+V;W=HW4dGdV}@fUW9DNk$E?R}$2`UY#)8Mf#v;ZF#~5RyW8-5JV^d=@ zW3yu$$5>-$#?Fsj9J@4jW$fD6^|6~{x5t-^FB_K`Uono2D~#jg%Hyiz>f<`&rsMwO zLF1w05#!P0apQ^ODdXwmS>w6m1>?ozW#g6O)#Eec8^>AW?D0+GTgEx#ym7(!weg$d zx5w{{-y450{&4*9_|plw3B?I~LTN%}LVZGOLU%%c!f?WD!fwK2!h6DZB48qTB5WdJ zqIIHuqI05qqIY6oV$H<*3HrqF1Z#pnv1elc#G#3!6DKB4O`M&$FmY+(>%{kopA)|) z{!BuXq)B*^GHEesHEACp4>FK zWs)~JKe>HUI4PdoJ9%vK)a2R83zL^7e@yF!gEb%hZ2UKc;?7{h5ZQmrg59t4?c7YftM<8%~={n@?L#M^DF1 zCrl?zCr_tNr%h)}XH5@HubZY#Gp0wT$ET;JXQnqyGpBjelIdO3d#4XfAD%uoePa6k z^rz`B(_g2*P5+$!GXu?#XOJ1$8O0f5MrB5QMsp^3CTu2RCTb>TCVnPqCUqu#CUd4> zrgElfrgf%$rfa5mrhi5_BbnJbvukG0%)XiZGY4l5&s?1OKJ#nl&nz@co~6wGHM?w9 zW_HCaF>5=!YSwAib=G6nd)9Y0U^aL*Y&LQ>W;T8{X*P8>eYShHZ+2jIXm-u)y4m%! z^x5Ir-Lv~<56m8#Jv@7K_So!+*^{$ZXYb8EoP9j|VfOp%pAAcxQOp=-95aEL%uHiu zGIN;u%pztfvw~U8tYbDZTbO{^$?ReFGl!V#m~`d{bDTNFoMkeZbIdJF9&?_#ohf4O zVD4t_V;*E4VIF6mWS(K3XI^4nW!_-kX5M2yWIka&XTD> zXC<*xSsAQsRvxR6Rl+J~Rk3PW4XkEX8>@rW&FW(fvevR_tYOv|Ymzm?+Q?$FHnX@a z0c#se$da&jvG%eKunx11vHoVAW}RbQWL;rhXWe4mWj$a$W<6uQWW8a%V|`?OVg1MY z!TQaH*km@sUd;ZBy^JlxUctuL@@z#m&Q@Zpu+`WaY%R79TaRtPHe#Ew&Da*~m27La zE!&>$z;#{@th=1Dkp=J&B@~wa!NSmoGMN&r-9SVY2$Qox;cHELC#tZjWf&{<4kg9I2$=^ z&SnmmBj9Y~2sskYF3w)g0nTC0G0xwd)0}ghi<~Q*>zrGhyPOA{$DC)Jmz+18cbt!$ zFP#54KRCa+5Etey<}T&Ra8a&2m&#S*s&X~B+FU)ZA=iX!&Rxm1;o5T@xh`CHt`~PT z*Pk224dq5~qq%Y1L~aT$~;w`I!}|Q z&C})S^9*?=JagVko(<2Q=g4#6x%0eut9kysAYLdhf)~w;<0bM^cUM{bISIjHp zRq|?h^}HrtE3ci`#p~q_@Ye9w^BBBQ-Z*cPH_e;nZRD|dbG*$w4v)tZ@V4@{^MpJx zZwGG|Zx3%D?*Q)*?+EW0?*#88?=z4!?=tTy?>g@$?>6r)?>_G#?=kNw?>X-! z?=|lq-aFn0-Y4D{-dEl?-Vfd{-XA{1C-V{hV*X#!#X&Os6?}{@&sXH*d?mgLUyZN9 z*XHZ-4f!T~bN))c4d0&c$amqp^S$`1`F{KWeh@!|AI^{B$MO^S$^0~aCO?Ot&oAPa z@+JeKgZv~=ke$H+xa5?4*qWbKK?=e z5&m)hN&XrBdHyB-RsIeBZT>y}L;e%~bN(y-Km7OnPyB!R-}pcIe*`1}B3L3=CXf|i z0tEpsP!^~OGzB^WeSwj{RA3>n64(k>37iD30uO<=z*i6;2o{71A_XymctMgNRgfXb z7UT&E1to%VL6x9Z&>(0QvU;{nKz$bId3y>Kkqp2GVea`HNSe^e?DkFbUtD} zdOmJGaXw`}eLibGcfMf0c)o1Da=vE1e!gkGb-sPRYrc1WV1CW~`gz9u=={VyYoT?a zeW7cicVS>*&BFQx#=_{r#KQE#h6UEbrUlLde_`vw!h(2V=fa+a{R@W{jxL;7IJIzg z;lje@g=-5p7w#xH)q9~M3@d|mjy@JqNzNET9re+ichR|w^Vib6uD zB2*V@33Y`ALSvzs&{AkEv=cfAorP{fPoa;{PZ%f+5rzw+gt5W|VX`nym?_K=<_n92 zrNRnfwXjauC~Oe|VW+T1*e@Ist`pLQBf@dvlyFwa6wV2^2zkPJ;dY@&xI?&GxKDUc zctm(ycv5&qcwTr(cvW~qcw2Z+_)z#n_+0o(_>b_t@RRUg;Wyz=;U5u6gou`imWgCV zm`Fi{ijtqKSq@W1>mXjA)~XE!r&NiUgulOh_=NbB_^kMX__Fw#_@?-d_`dj&_^J4X__g@0_=EVf_^bH4 z_?Kjnge;**{*o-0tdPh_6eWa2MWQazlIThdB*qdmiKWC^VkdEsI7{3lo)RC4pCnKc zA_QPLs-l1@pFq+c>5Stp@OMkM2sDaova zDVdXOk?Qq5-%epK}JiWjFTjpD9JKe(qyVkmzk0- zb0kCN%R*T!nX*i>WTj-w8p)CMk}I1ePqsT<)9SE5h;}8QY5FOSk6j` zT#!<^EM;;{%H^h1$Q`LfRRp6tLQo5#sE2SgL?oIZ8qE=lR%nCv=!h=phMwqy{uqeC zh{F&JLp+9KBoZ(RV~~ik7>^`Oz$7Fi1*u5G6imZ(%)l(9V>ad@1M{!|3$X}Gkcp*O zjx4OeDr93d)*=V%umQQ)h|S2uR%}N;c3>BF15h~jU?2A501lx5hjA2zIEE7_!bzM) zG0xx|N^l+*QHo2rf-+pib(G@e(9uI$Dh?8QFp#{nG3!HnY&4r4rrb0iZuies3_u^i7NPT(Xa zGli*4;}lNgbk5)`rgJvuGK2HDfD5^ZOPI-}T+S@6;3{TwHP$rir+{n$$<5q5G zK6h{zcN0`P_i!Kg^8gRAfQNaMg*?U+EaFL?W--t397}kf7g@?nyuvbG<#m?x25+%~ zw|SS9T1A62SgUCb4bhrfTSK+3hH1Dq&*u;lMO6d3PruSb`i&md@AQQJpr`dGJ*U6uMg2{$=pTAr|I}Ohx8Bu% zEXeM+8uozIwmKGO^)130S(G)k7;9mzt*v#iPS(}BTQBQt18k7R*-(qO5td-1Ez!nV zl1;Q^n`~(|)u!7_OSd_eVe@UFEw)TsW?8n`*yJqEf(<Yw=z|H9$sd;Cj3;9vV;|JINB_kPlU^fUgmpZ8z= ulK<{k{V%WZ+kyK6!GY?5&_I(wvq0xS??7T8rOJN=1>G}Z%RT>pAoX9OQJ9 Date: Wed, 30 Mar 2016 15:37:21 -0500 Subject: [PATCH 045/163] Added ascii art to documentation --- src/sage/combinat/binary_tree.py | 81 ++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/binary_tree.py b/src/sage/combinat/binary_tree.py index 91f8a7aa2ac..b0ce9d72dd8 100644 --- a/src/sage/combinat/binary_tree.py +++ b/src/sage/combinat/binary_tree.py @@ -2068,10 +2068,40 @@ def comb(self, side='left'): sage: BT.comb('right') [.] sage: BT = BinaryTree( '[[[[., [., .]], .], [[., .], [[[., .], [., .]], [., .]]]], [., [[[., .], [[[., .], [., .]], .]], .]]]' ) + sage: ascii_art(BT) + ________o________ + / \ + __o__ o + / \ \ + o __o___ o + / / \ / + o o _o_ __o__ + \ / \ / \ + o o o o o + / \ / + o o o + / \ + o o sage: BT.comb('left') [[[., .], [[[., .], [., .]], [., .]]], ., [., .]] + sage: ascii_art(BT.comb('left')) + [ __o___ , , o ] + [ / \ ] + [ o _o_ ] + [ / \ ] + [ o o ] + [ / \ ] + [ o o ] sage: BT.comb('right') [., [[., .], [[[., .], [., .]], .]]] + sage: ascii_art(BT.comb('right')) + [ , __o__ ] + [ / \ ] + [ o o ] + [ / ] + [ o ] + [ / \ ] + [ o o ] """ def _comb(side): if self.is_empty(): @@ -2108,10 +2138,29 @@ def hook_number(self): sage: BT = BinaryTree( '[.,.]' ) sage: BT.hook_number() 1 - sage: BT = BinaryTree( '[[[.,.], .], [.,.]]' ) + sage: BT = BinaryTree( '[[[.,.], .], [.,.]]' ); ascii_art(BT) + o + / \ + o o + / + o sage: BT.hook_number() 1 sage: BT = BinaryTree( '[[[[., [., .]], .], [[., .], [[[., .], [., .]], [., .]]]], [., [[[., .], [[[., .], [., .]], .]], .]]]' ) + sage: ascii_art(BT) + ________o________ + / \ + __o__ o + / \ \ + o __o___ o + / / \ / + o o _o_ __o__ + \ / \ / \ + o o o o o + / \ / + o o o + / \ + o o sage: BT.hook_number() 6 """ @@ -2136,13 +2185,39 @@ def twisting_number(self): sage: BT = BinaryTree( '[.,.]' ) sage: BT.twisting_number() [0, 0] - sage: BT = BinaryTree( '[[[.,.], .], [.,.]]' ) + sage: BT = BinaryTree( '[[[.,.], .], [.,.]]' ); ascii_art(BT) + o + / \ + o o + / + o sage: BT.twisting_number() [1, 1] sage: BT = BinaryTree( '[[[[., [., .]], .], [[., .], [[[., .], [., .]], [., .]]]], [., [[[., .], [[[., .], [., .]], .]], .]]]' ) + sage: ascii_art(BT) + ________o________ + / \ + __o__ o + / \ \ + o __o___ o + / / \ / + o o _o_ __o__ + \ / \ / \ + o o o o o + / \ / + o o o + / \ + o o sage: BT.twisting_number() [5, 6] - sage: BT = BinaryTree( '[.,[[[.,.],.],.]]' ) + sage: BT = BinaryTree( '[.,[[[.,.],.],.]]' ); ascii_art(BT) + o + \ + o + / + o + / + o sage: BT.twisting_number() [1, 1] """ From 5369226abbef6513bc9a692ebe4d8fa52dc4a1b2 Mon Sep 17 00:00:00 2001 From: Martin Albrecht Date: Thu, 31 Mar 2016 10:49:41 +0100 Subject: [PATCH 046/163] upgrade libfplll to 20160331 which adds pkg-config support --- build/pkgs/libfplll/checksums.ini | 6 +++--- build/pkgs/libfplll/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/libfplll/checksums.ini b/build/pkgs/libfplll/checksums.ini index 6528a6604d2..a3434773354 100644 --- a/build/pkgs/libfplll/checksums.ini +++ b/build/pkgs/libfplll/checksums.ini @@ -1,4 +1,4 @@ tarball=libfplll-VERSION.tar.gz -sha1=5154d73807ef4d5e935bc9ffcc366497034dfb03 -md5=fb3f0e8be532723dbee97d84628f41de -cksum=2223093635 +sha1=4f03734645abe800cd4d7838fcec0cbd2c551633 +md5=661440d1249e1f8d6346ab91a8658de6 +cksum=1133822281 diff --git a/build/pkgs/libfplll/package-version.txt b/build/pkgs/libfplll/package-version.txt index 3f0c8d2e5b4..1528ea5fdf3 100644 --- a/build/pkgs/libfplll/package-version.txt +++ b/build/pkgs/libfplll/package-version.txt @@ -1 +1 @@ -20160325 +20160331 From 2c0460863cc6ffb874252a1450e8fd885c1fad91 Mon Sep 17 00:00:00 2001 From: Martin Albrecht Date: Thu, 31 Mar 2016 11:29:18 +0100 Subject: [PATCH 047/163] add spkg-src for libfplll --- build/pkgs/libfplll/spkg-src | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100755 build/pkgs/libfplll/spkg-src diff --git a/build/pkgs/libfplll/spkg-src b/build/pkgs/libfplll/spkg-src new file mode 100755 index 00000000000..bd0b7bb7d4c --- /dev/null +++ b/build/pkgs/libfplll/spkg-src @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +if [ -z "$SAGE_LOCAL" ]; then + echo >&2 "Error: SAGE_LOCAL undefined - exiting..." + echo >&2 "Maybe run 'sage -sh'?" + exit 1 +fi + +FPLLL_VERSION="4.0.5" +FPLLL_SAGE_VERSION="20160331" +SPKG_ROOT="$SAGE_ROOT/build/pkgs/libfplll" + +set -e + +cd "$SPKG_ROOT" + +rm -rf fplll-checkout +git clone git://github.com/dstehle/fplll fplll-checkout + +cd fplll-checkout +./autogen.sh +./configure +make dist +tar xvfz libfplll-"$FPLLL_VERSION".tar.gz +rm libfplll-"$FPLLL_VERSION".tar.gz +mv libfplll-"$FPLLL_VERSION" libfplll-"$FPLLL_SAGE_VERSION" +tar cvfz libfplll-"$FPLLL_SAGE_VERSION".tar.gz libfplll-"$FPLLL_SAGE_VERSION" + +mv libfplll-"$FPLLL_SAGE_VERSION".tar.gz "$SAGE_DISTFILES/" +cd .. +rm -rf fplll-checkout From 269fd25cac4c4d032cefd651fc1f4dada3b220c7 Mon Sep 17 00:00:00 2001 From: Martin Albrecht Date: Thu, 31 Mar 2016 13:39:33 +0100 Subject: [PATCH 048/163] libfplll: fix fplll revision --- build/pkgs/libfplll/spkg-src | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build/pkgs/libfplll/spkg-src b/build/pkgs/libfplll/spkg-src index bd0b7bb7d4c..99534d180e2 100755 --- a/build/pkgs/libfplll/spkg-src +++ b/build/pkgs/libfplll/spkg-src @@ -6,18 +6,21 @@ if [ -z "$SAGE_LOCAL" ]; then exit 1 fi +FPLLL_REVISION="b50fd91ba0aeea2067dc9d82e6c352dbe0210eb3" FPLLL_VERSION="4.0.5" -FPLLL_SAGE_VERSION="20160331" SPKG_ROOT="$SAGE_ROOT/build/pkgs/libfplll" set -e cd "$SPKG_ROOT" +FPLLL_SAGE_VERSION=`cat package-version.txt |sed 's/[.]p.*//'` + rm -rf fplll-checkout git clone git://github.com/dstehle/fplll fplll-checkout cd fplll-checkout +git checkout "$FPLLL_REVISION" ./autogen.sh ./configure make dist From 8b7674311465d1a604ae44fc56aceef16687c0f0 Mon Sep 17 00:00:00 2001 From: Chris Wuthrich Date: Fri, 1 Apr 2016 13:19:23 +0100 Subject: [PATCH 049/163] trac 20254: delete some comments and add doc --- .../schemes/elliptic_curves/padic_lseries.py | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/padic_lseries.py b/src/sage/schemes/elliptic_curves/padic_lseries.py index 96c83d35ffd..501169cf210 100644 --- a/src/sage/schemes/elliptic_curves/padic_lseries.py +++ b/src/sage/schemes/elliptic_curves/padic_lseries.py @@ -146,7 +146,7 @@ class pAdicLseries(SageObject): 5 + 6*7 + 4*7^2 + 2*7^3 + 3*7^4 + 2*7^5 + O(7^6) + (6 + 4*7 + 7^2 + O(7^3))*T + (3 + 2*7^2 + O(7^3))*T^2 + (1 + 4*7 + 7^2 + O(7^3))*T^3 + (6 + 6*7 + 6*7^2 + O(7^3))*T^4 + O(T^5) sage: lp.series(4,eta=3) O(7^6) + (5 + 4*7 + 2*7^2 + O(7^3))*T + (6 + 5*7 + 2*7^2 + O(7^3))*T^2 + (5*7 + O(7^3))*T^3 + (7 + 4*7^2 + O(7^3))*T^4 + O(T^5) - + (Note that the last series vanishes at `T = 0`, which is consistent with :: sage: E.quadratic_twist(-7).rank() @@ -216,7 +216,7 @@ def __add_negative_space(self): verbose('Currently there is no negative modular symbols in eclib, so we have to fall back on the implementation of modular symbols in sage') # once there is a eclib implementation of -1, this should be changed. self._negative_modular_symbol = self._E.modular_symbol(sign=-1, use_eclib = False, normalize=self._normalize) - + def __cmp__(self,other): r""" Compare self and other. @@ -355,10 +355,6 @@ def modular_symbol(self, r, sign=+1, quadratic_twist= +1): return -sum([ kronecker_symbol(D,u) * m(r+ZZ(u)/D) \ for u in range(1,-D) ] ) - # without the ZZ here the u is treated as a 'int' and dividing by D gives 0 - # this only happens when it is called from __init__ (?) - #return s * sum([kronecker_symbol(D,u) * m(r+ZZ(u)/D) for u in range(1,abs(D))]) - def measure(self, a, n, prec, quadratic_twist=+1, sign = +1): r""" @@ -464,7 +460,7 @@ def measure(self, a, n, prec, quadratic_twist=+1, sign = +1): else: mu = chip**n * z * sum([kronecker_symbol(D,u) *( f(a/(p*w)+ZZ(u)/D) - chip /alpha * f(a/w+ZZ(u)/D) ) for u in range(1,D.abs())]) return s*mu - + def alpha(self, prec=20): r""" Return a `p`-adic root `\alpha` of the polynomial `x^2 - a_p x @@ -1091,7 +1087,7 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): OUTPUT: - a power series with coefficients in a quadratic ramified extension of + a power series with coefficients in a quadratic ramified extension of the `p`-adic numbers generated by a root `alpha` of the characteristic polynomial of Frobenius on `T_pE`. @@ -1153,7 +1149,6 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): K = alpha.parent() R = PowerSeriesRing(K,'T',1) L = self.modular_symbol(0, sign=+1, quadratic_twist=D) - #chip = kronecker_symbol(D,p) L *= (1-1/self.alpha())**2 L /= self._quotient_of_periods_to_twist(D)*self._E.real_components() L = R(L, 1) @@ -1165,7 +1160,6 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): else: prec = min(p**(n-1), prec) bounds = self._prec_bounds(n,prec) - #padic_prec = max(sum(bounds[1:],[])) + 5 alphaadic_prec = max(bounds[1:]) + 5 padic_prec = alphaadic_prec//2+1 @@ -1263,21 +1257,29 @@ def _prec_bounds(self, n,prec): sage: Lp._prec_bounds(10,5) [+Infinity, 6, 6, 6, 6] """ - #p = self._p e = self._e_bounds(n-1,prec) c0 = ZZ(n+2) return [infinity] + [ 2* e[j] - c0 for j in range(1,len(e))] - #return [[infinity,infinity]] + [[(e[j] - c0).floor(), (e[j] - c1).floor()] for j in range(1,len(e))] def _poly(self, a): """ - Given an element a in Qp[alpha] this return the list - containing the two coordinates in Qp. The last digits may be wrong. - - this should be implemented in elements of Eisenstein rings at some point - + Given an element a in Qp[alpha] this returns the list + containing the two coordinates in Qp. + + EXAMPLES:: + + sage: E = EllipticCurve("14a1") + sage: lp = E.padic_lseries(5) + sage: K = lp.alpha().parent() + sage: a = K(5) + sage: a + 4*alpha^2 + alpha^4 + O(alpha^42) + sage: lp._poly(a) + [5 + O(5^21), O(5^21)] """ + # this should be implemented in elements of Eisenstein rings at some point trac 20248 + if a.is_zero(): return [0,0] v, k = a._ntl_rep_abs() @@ -1338,8 +1340,6 @@ def Dp_valued_series(self, n=3, quadratic_twist = +1, prec=5): Hli.append( v[1] ) G = QpT( Gli, prec ) H = QpT( Hli, prec ) - #G = QpT([lps[n][0] for n in range(0,lps.prec())], prec) - #H = QpT([lps[n][1] for n in range(0,lps.prec())], prec) # now compute phi phi = matrix.matrix([[0,-1/p],[1,E.ap(p)/p]]) From a13fdf4122ddb95f67d341dd3b9acad09d890d70 Mon Sep 17 00:00:00 2001 From: Chris Wuthrich Date: Fri, 1 Apr 2016 23:48:44 +0100 Subject: [PATCH 050/163] trac 20254: one more doctest --- src/sage/modular/modform/element.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/modular/modform/element.py b/src/sage/modular/modform/element.py index d88df81ef65..31e52e8c273 100644 --- a/src/sage/modular/modform/element.py +++ b/src/sage/modular/modform/element.py @@ -573,13 +573,13 @@ def period(self, M, prec=53): to it, which can be computed using the method `:meth:~sage.schemes.elliptic_curves.ell_rational_field.EllipticCurve_rational_field.modular_symbol`. These can be used to express the periods of `f` as exact - linear combinations of a basis for the period lattice of `E`:: + linear combinations of the real and the imaginary period of `E`:: sage: s = E.modular_symbol(sign=+1) sage: t = E.modular_symbol(sign=-1) sage: s(3/11), t(3/11) - (1/10, 1) - sage: s(3/11)*omega1 + t(3/11)*omega2.imag()*I + (1/10, 1/2) + sage: s(3/11)*omega1 + t(3/11)*2*omega2.imag()*I 0.634604652139777 + 1.45881661693850*I ALGORITHM: From aab22d3f3f48ef63cb46cb3d5c401b2c790af86f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 2 Apr 2016 14:31:10 -0500 Subject: [PATCH 051/163] Adding support for general c_{ij} and some cleanup. --- .../combinat/crystals/monomial_crystals.py | 339 ++++++++++-------- 1 file changed, 197 insertions(+), 142 deletions(-) diff --git a/src/sage/combinat/crystals/monomial_crystals.py b/src/sage/combinat/crystals/monomial_crystals.py index 0cfaf8ba261..b35e57783b6 100644 --- a/src/sage/combinat/crystals/monomial_crystals.py +++ b/src/sage/combinat/crystals/monomial_crystals.py @@ -108,6 +108,9 @@ from sage.combinat.root_system.root_system import RootSystem from sage.rings.integer import Integer from sage.rings.infinity import Infinity +from sage.rings.integer_ring import ZZ +from sage.matrix.matrix import is_Matrix +from sage.matrix.matrix_space import MatrixSpace class NakajimaYMonomial(Element): r""" @@ -150,17 +153,13 @@ def _repr_(self): sage: M({(1,0):1,(2,2):-2,(0,5):10}) Y(0,5)^10 Y(1,0) Y(2,2)^-2 """ - if self._dict == {}: + if not self._dict: return "1" - else: - L = sorted(self._dict.iteritems(), key=lambda x:(x[0][0],x[0][1])) - return_str = '' - for x in range(len(L)): - if L[x][1] != 1: - return_str += "Y(%s,%s)"%(L[x][0][0],L[x][0][1]) + "^%s "%L[x][1] - else: - return_str += "Y(%s,%s) "%(L[x][0][0],L[x][0][1]) - return return_str + + L = sorted(self._dict.iteritems(), key=lambda x: (x[0][0], x[0][1])) + exp = lambda e: "^{}".format(e) if e != 1 else "" + return ' '.join("Y({},{})".format(mon[0][0], mon[0][1]) + exp(mon[1]) + for mon in L) def __hash__(self): r""" @@ -298,7 +297,7 @@ def weight(self): P = self.parent().weight_lattice_realization() return P(self.weight_in_root_lattice()) - def epsilon(self,i): + def epsilon(self, i): r""" Return the value of `\varepsilon_i` on ``self``. @@ -318,7 +317,7 @@ def epsilon(self,i): h = self.parent().weight_lattice_realization().simple_coroots() return self.phi(i) - self._classical_weight().scalar(h[i]) - def phi(self,i): + def phi(self, i): r""" Return the value of `\varphi_i` on ``self``. @@ -335,25 +334,20 @@ def phi(self,i): """ if i not in self.parent().index_set(): raise ValueError("i must be an element of the index set") - dict = self._dict - if dict == {}: - return 0 - else: - L = [x[0] for x in dict.keys()] - if i not in L: - return 0 + if not self._dict or all(x[0] != i for x in self._dict): + return ZZ.zero() + + d = copy(self._dict) + K = max(x[1] for x in d if x[0] == i) + for a in range(K): + if (i,a) in d: + continue else: - d = copy(dict) - K = max(x[1] for x in list(d) if x[0] ==i) - for a in range(K): - if (i,a) in d: - continue - else: - d[(i,a)] = 0 - S = sorted((x for x in d.iteritems() if x[0][0]==i), key=lambda x: x[0][1]) - return max(sum(S[k][1] for k in range(s)) for s in range(1,len(S)+1)) - - def _ke(self,i): + d[(i,a)] = 0 + S = sorted((x for x in d.iteritems() if x[0][0] == i), key=lambda x: x[0][1]) + return max(sum(S[k][1] for k in range(s)) for s in range(1,len(S)+1)) + + def _ke(self, i): r""" Return the value `k_e` with respect to ``i`` and ``self``. @@ -368,31 +362,29 @@ def _ke(self,i): sage: [m._ke(i) for i in M.index_set()] [+Infinity, 0, +Infinity] """ - dict = self._dict - sum = 0 - L = [] + h = self.parent().weight_lattice_realization().simple_coroots() phi = self.phi(i) - if self.epsilon(i) == 0: + if phi == self._classical_weight().scalar(h[i]): # self.epsilon(i) == 0 return Infinity - else: - d = copy(dict) - K = max(x[1] for x in list(d) if x[0] ==i) - for a in range(K): - if (i,a) in d: - continue - else: - d[(i,a)] = 0 - S = sorted((x for x in d.iteritems() if x[0][0]==i), key=lambda x: x[0][1]) - for var,exp in S: - sum += exp - if sum == phi: - L.append(var[1]) - if L == []: - return 0 + + d = copy(self._dict) + K = max(x[1] for x in d if x[0] == i) + for a in range(K): + if (i,a) in d: + continue else: - return max(L) + d[(i,a)] = 0 + total = ZZ.zero() + L = [] + S = sorted((x for x in d.iteritems() if x[0][0] == i), key=lambda x: x[0][1]) + for var,exp in S: + total += exp + if total == phi: + L.append(var[1]) + + return max(L) if L else ZZ.zero() - def _kf(self,i): + def _kf(self, i): r""" Return the value `k_f` with respect to ``i`` and ``self``. @@ -407,18 +399,17 @@ def _kf(self,i): sage: [m._kf(i) for i in M.index_set()] [0, 0, 2, 0, 0] """ - d = copy(self._dict) - I = [x[0] for x in d] - if i not in I: - return 0 + if all(i != x[0] for x in self._dict): + return ZZ.zero() else: - K = max(x[1] for x in list(d) if x[0] ==i) + d = copy(self._dict) + K = max(key[1] for key in d if key[0] == i) for a in range(K): if (i,a) in d: continue else: d[(i,a)] = 0 - S = sorted((x for x in d.iteritems() if x[0][0]==i), key=lambda x: x[0][1]) + S = sorted((x for x in d.iteritems() if x[0][0] == i), key=lambda x: x[0][1]) sum = 0 phi = self.phi(i) for var,exp in S: @@ -426,7 +417,7 @@ def _kf(self,i): if sum == phi: return var[1] - def e(self,i): + def e(self, i): r""" Return the action of `e_i` on ``self``. @@ -442,7 +433,7 @@ def e(self,i): [None, None, None, - Y(0,0)^-1 Y(1,1)^-1 Y(2,1) Y(3,0) Y(3,1) Y(4,0)^-1 Y(4,1)^-1 Y(5,0) , + Y(0,0)^-1 Y(1,1)^-1 Y(2,1) Y(3,0) Y(3,1) Y(4,0)^-1 Y(4,1)^-1 Y(5,0), None, None, None, @@ -451,9 +442,9 @@ def e(self,i): sage: M = crystals.infinity.NakajimaMonomials("C5") sage: m = M.module_generators[0].f_string([1,3]) sage: [m.e(i) for i in M.index_set()] - [Y(2,1) Y(3,0)^-1 Y(3,1)^-1 Y(4,0) , + [Y(2,1) Y(3,0)^-1 Y(3,1)^-1 Y(4,0), None, - Y(1,0)^-1 Y(1,1)^-1 Y(2,0) , + Y(1,0)^-1 Y(1,1)^-1 Y(2,0), None, None] """ @@ -464,33 +455,30 @@ def e(self,i): newdict = copy(self._dict) ke = self._ke(i) - Aik = {(i, ke):1, (i, ke+1):1} + Aik = {(i, ke): 1, (i, ke+1): 1} ct = self.parent().cartan_type() cm = ct.cartan_matrix() shift = 0 if self.parent().cartan_type().is_finite(): shift = 1 - for j in self.parent().index_set(): + for j_index,j in enumerate(self.parent().index_set()): if i == j: continue - c = 0 - if i > j: - c = 1 - if ct.is_affine() and ct.type() == 'A' and abs(i-j) == ct.rank() - 1: - c = 1 - c - if cm[j-shift][i-shift] != 0: - Aik[(j, ke+c)] = cm[j-shift][i-shift] + c = self.parent()._c[i-shift,j_index] + if cm[j_index,i-shift] != 0: + Aik[(j, ke+c)] = cm[j_index,i-shift] + # Multiply by Aik for key,value in Aik.iteritems(): if key in newdict: - newdict[key] +=value + if newdict[key] == -value: # The result would be a 0 exponent + del newdict[key] + else: + newdict[key] += value else: newdict[key] = value - for k in list(newdict): - if newdict[k] == 0: - newdict.pop(k) - return self.__class__(self.parent(),newdict) + return self.__class__(self.parent(), newdict) - def f(self,i): + def f(self, i): r""" Return the action of `f_i` on ``self``. @@ -503,40 +491,37 @@ def f(self,i): sage: M = crystals.infinity.NakajimaMonomials("B4") sage: m = M.module_generators[0].f_string([1,3,4]) sage: [m.f(i) for i in M.index_set()] - [Y(1,0)^-2 Y(1,1)^-2 Y(2,0)^2 Y(2,1) Y(3,0)^-1 Y(4,0) Y(4,1)^-1 , - Y(1,0)^-1 Y(1,1)^-1 Y(1,2) Y(2,0) Y(2,2)^-1 Y(3,0)^-1 Y(3,1) Y(4,0) Y(4,1)^-1 , - Y(1,0)^-1 Y(1,1)^-1 Y(2,0) Y(2,1)^2 Y(3,0)^-2 Y(3,1)^-1 Y(4,0)^3 Y(4,1)^-1 , - Y(1,0)^-1 Y(1,1)^-1 Y(2,0) Y(2,1) Y(3,0)^-1 Y(3,1) Y(4,1)^-2 ] + [Y(1,0)^-2 Y(1,1)^-2 Y(2,0)^2 Y(2,1) Y(3,0)^-1 Y(4,0) Y(4,1)^-1, + Y(1,0)^-1 Y(1,1)^-1 Y(1,2) Y(2,0) Y(2,2)^-1 Y(3,0)^-1 Y(3,1) Y(4,0) Y(4,1)^-1, + Y(1,0)^-1 Y(1,1)^-1 Y(2,0) Y(2,1)^2 Y(3,0)^-2 Y(3,1)^-1 Y(4,0)^3 Y(4,1)^-1, + Y(1,0)^-1 Y(1,1)^-1 Y(2,0) Y(2,1) Y(3,0)^-1 Y(3,1) Y(4,1)^-2] """ if i not in self.parent().index_set(): raise ValueError("i must be an element of the index set") newdict = copy(self._dict) kf = self._kf(i) - Aik = {(i, kf):-1, (i, kf+1):-1} + Aik = {(i, kf): -1, (i, kf+1): -1} ct = self.parent().cartan_type() cm = ct.cartan_matrix() shift = 0 if ct.is_finite(): shift = 1 - for j in self.parent().index_set(): + for j_index,j in enumerate(self.parent().index_set()): if i == j: continue - c = 0 - if i > j: - c = 1 - if ct.is_affine() and ct.type() == 'A' and abs(i-j) == ct.rank() - 1: - c = 1 - c - if cm[j-shift][i-shift] != 0: - Aik[(j, kf+c)] = -cm[j-shift][i-shift] + c = self.parent()._c[i-shift,j_index] + if cm[j_index,i-shift] != 0: + Aik[(j, kf+c)] = -cm[j_index,i-shift] + # Multiply by Aik for key,value in Aik.iteritems(): if key in newdict: - newdict[key] +=value + if newdict[key] == -value: # The result would be a 0 exponent + del newdict[key] + else: + newdict[key] += value else: newdict[key] = value - for k in list(newdict): - if newdict[k] == 0: - newdict.pop(k) - return self.__class__(self.parent(),newdict) + return self.__class__(self.parent(), newdict) class NakajimaAMonomial(NakajimaYMonomial): r""" @@ -565,17 +550,12 @@ def _repr_(self): sage: m A(1,1)^-1 A(2,0)^-1 A(4,0)^-1 """ - if self._dict == {}: + if not self._dict: return "1" - else: - L = sorted(self._dict.iteritems(), key=lambda x:(x[0][0],x[0][1])) - return_str = '' - for x in range(len(L)): - if L[x][1] != 1: - return_str += "A(%s,%s)"%(L[x][0][0],L[x][0][1]) + "^%s "%L[x][1] - else: - return_str += "A(%s,%s) "%(L[x][0][0],L[x][0][1]) - return return_str + L = sorted(self._dict.iteritems(), key=lambda x: (x[0][0], x[0][1])) + exp = lambda e: "^{}".format(e) if e != 1 else "" + return ' '.join("A({},{})".format(mon[0][0], mon[0][1]) + exp(mon[1]) + for mon in L) def _latex_(self): r""" @@ -602,6 +582,8 @@ def _latex_(self): def to_Y_monomial(self): r""" + Convert ``self`` to a monomial in the `Y_{ik}` variables. + Represent `\prod_{(i,k)} A_{i,k}^{a_{i}(k)}` in the form `\prod_{(i,k)} Y_{i,k}^{y_i(k)}` using the formula @@ -612,36 +594,31 @@ def to_Y_monomial(self): EXAMPLES:: - sage: M = crystals.infinity.NakajimaMonomials(['A',2,1],use_Y=False) + sage: M = crystals.infinity.NakajimaMonomials(['A',2,1], use_Y=False) sage: m = M.module_generators[0].f_string([2,0,1,2,1]) sage: m - A(0,0)^-1 A(1,0)^-1 A(1,1)^-1 A(2,0)^-1 A(2,1)^-1 + A(0,1)^-1 A(1,1)^-2 A(2,0)^-1 A(2,1)^-1 sage: m.to_Y_monomial() - Y(0,1) Y(0,2) Y(1,1)^-1 Y(2,2)^-1 + Y(0,2)^2 Y(1,2)^-1 Y(2,0)^-1 Y(2,1) Y(2,2)^-1 """ Y = {} - d = self._dict ct = self.parent().cartan_type() cm = ct.cartan_matrix() - for k,v in d.iteritems(): + shift = 0 + if ct.is_finite(): + shift = 1 + for k,v in self._dict.iteritems(): Y[k] = Y.get(k,0) + v Y[(k[0],k[1]+1)] = Y.get((k[0],k[1]+1), 0) + v - shift = 0 - if ct.is_finite(): - shift = 1 - for j in self.parent().index_set(): + for j_index,j in enumerate(self.parent().index_set()): if k[0] == j: continue - c = 0 - if k[0] > j: - c = 1 - if ct.is_affine() and ct.type() == 'A' and abs(k[0]-j) == ct.rank() - 1: - c = 1 - c - if cm[j-shift][k[0]-shift] != 0: - Y[(j, k[1]+c)] = Y.get((j,k[1]+c),0) + v*cm[j-shift][k[0]-shift] + c = self.parent()._c[k[0]-shift,j_index] + if cm[j_index,k[0]-shift] != 0: + Y[(j,k[1]+c)] = Y.get((j,k[1]+c), 0) + v*cm[j_index,k[0]-shift] for k in Y.keys(): if Y[k] == 0: - Y.pop(k) + del Y[k] return NakajimaYMonomial(self.parent(), Y) def weight(self): @@ -722,10 +699,10 @@ def e(self,i): sage: M = crystals.infinity.NakajimaMonomials(['D',4,1],use_Y=False) sage: m = M.module_generators[0].f_string([4,2,3,0]) sage: [m.e(i) for i in M.index_set()] - [A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 , + [A(2,1)^-1 A(3,1)^-1 A(4,0)^-1, None, None, - A(0,2)^-1 A(2,1)^-1 A(4,0)^-1 , + A(0,2)^-1 A(2,1)^-1 A(4,0)^-1, None] """ if i not in self.parent().index_set(): @@ -740,7 +717,7 @@ def e(self,i): d.pop(k) return self.__class__(self.parent(), d) - def f(self,i): + def f(self, i): r""" Return the action of `f_i` on ``self``. @@ -755,14 +732,14 @@ def f(self,i): sage: m A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1 sage: [m.f(i) for i in M.index_set()] - [A(1,2)^-1 A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1 , - A(2,0)^-1 A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1 , - A(2,1)^-1 A(3,0)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1 , - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(4,1)^-1 A(8,0)^-1 , - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(5,0)^-1 A(8,0)^-1 , - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(6,0)^-1 A(8,0)^-1 , - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(7,1)^-1 A(8,0)^-1 , - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-2 ] + [A(1,2)^-1 A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1, + A(2,0)^-1 A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,0)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(4,1)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(5,0)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(6,0)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(7,1)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-2] """ if i not in self.parent().index_set(): raise ValueError("i must be an element of the index set") @@ -855,9 +832,44 @@ class InfinityCrystalOfNakajimaMonomials(UniqueRepresentation, Parent): sage: BG.is_isomorphic(MG,edge_labels=True) # long time True """ + @staticmethod + def _normalize_c(c, n): + """ + Normalize the input ``c``. + + EXAMPLES:: + + sage: from sage.combinat.crystals.monomial_crystals import InfinityCrystalOfNakajimaMonomials + sage: InfinityCrystalOfNakajimaMonomials._normalize_c(None, 4) + [0 0 0 0] + [1 0 0 0] + [1 1 0 0] + [1 1 1 0] + sage: c = matrix([[0,1,1],[0,0,0],[0,1,0]]); c + [0 1 1] + [0 0 0] + [0 1 0] + sage: c.is_mutable() + True + sage: C = InfinityCrystalOfNakajimaMonomials._normalize_c(c, 3); C + [0 1 1] + [0 0 0] + [0 1 0] + sage: C.is_mutable() + False + """ + if c is None: + # Default is i > j <=> c_{ij} = 1 (0 otherwise) + c = [[1 if i > j else 0 for j in range(n)] for i in range(n)] + MS = MatrixSpace(ZZ, n, n) + c = MS(c) + c.set_immutable() + if any(c[i,i] != 0 for i in range(n)): + raise ValueError("the c matrix must have 0's on the diagonal") + return c @staticmethod - def __classcall_private__(cls, ct, category=None, use_Y=True): + def __classcall_private__(cls, ct, c=None, use_Y=True): r""" Normalize input to ensure a unique representation. @@ -881,9 +893,11 @@ def __classcall_private__(cls, ct, category=None, use_Y=True): else: elt_class = use_Y cartan_type = CartanType(ct) - return super(InfinityCrystalOfNakajimaMonomials,cls).__classcall__(cls,cartan_type,category,elt_class) + n = len(cartan_type.index_set()) + c = InfinityCrystalOfNakajimaMonomials._normalize_c(c, n) + return super(InfinityCrystalOfNakajimaMonomials, cls).__classcall__(cls, cartan_type, c, elt_class) - def __init__(self, ct, category, elt_class): + def __init__(self, ct, c, elt_class, category=None): r""" EXAMPLES:: @@ -891,6 +905,7 @@ def __init__(self, ct, category, elt_class): sage: TestSuite(Minf).run() # long time """ self._cartan_type = ct + self._c = c self.Element = elt_class if category is None: @@ -926,6 +941,27 @@ def _repr_(self): """ return "Infinity Crystal of modified Nakajima monomials of type {}".format(self._cartan_type) + def c(self): + """ + Return the matrix `c_{ij}` of ``self``. + + EXAMPLES:: + + sage: La = RootSystem(['B',3]).weight_lattice().fundamental_weights() + sage: M = crystals.NakajimaMonomials(La[1]+La[2]) + sage: M.c() + [0 0 0] + [1 0 0] + [1 1 0] + + sage: c = Matrix([[0,0,1],[1,0,0],[0,1,0]]) + sage: La = RootSystem(['A',2,1]).weight_lattice(extended=True).fundamental_weights() + sage: M = crystals.NakajimaMonomials(2*La[1], c=c) + sage: M.c() == c + True + """ + return self._c + def cardinality(self): r""" Return the cardinality of ``self``, which is always `\infty`. @@ -985,7 +1021,7 @@ class CrystalOfNakajimaMonomialsElement(NakajimaYMonomial): Y(0,0)^2 Y(0,1)^-1 Y(2,0) sage: TestSuite(m).run() """ - def f(self,i): + def f(self, i): r""" Return the action of `f_i` on ``self``. @@ -999,7 +1035,7 @@ def f(self,i): sage: M = crystals.NakajimaMonomials(['A',5,2],3*La[0]) sage: m = M.module_generators[0] sage: [m.f(i) for i in M.index_set()] - [Y(0,0)^2 Y(0,1)^-1 Y(2,0) , None, None, None] + [Y(0,0)^2 Y(0,1)^-1 Y(2,0), None, None, None] """ if self.phi(i) == 0: return None @@ -1087,9 +1123,23 @@ class CrystalOfNakajimaMonomials(InfinityCrystalOfNakajimaMonomials): sage: GB = B.digraph(subset=SB) sage: GM.is_isomorphic(GB,edge_labels=True) True + + sage: c = Matrix([[0,0,1],[1,0,0],[0,1,0]]) + sage: La = RootSystem(['A',2,1]).weight_lattice(extended=True).fundamental_weights() + sage: M = crystals.NakajimaMonomials(2*La[1], c=c) + sage: list(M.subcrystal(max_depth=3)) + [Y(1,0)^2, + Y(0,1) Y(1,0) Y(1,1)^-1 Y(2,0), + Y(0,2)^-1 Y(1,0) Y(2,0) Y(2,2), + Y(0,1)^2 Y(1,1)^-2 Y(2,0)^2, + Y(0,0) Y(0,1) Y(1,0) Y(2,1)^-1, + Y(0,0) Y(0,2)^-1 Y(1,0) Y(1,1) Y(2,1)^-1 Y(2,2), + Y(0,1) Y(0,2)^-1 Y(1,1)^-1 Y(2,0)^2 Y(2,2), + Y(0,0) Y(0,1)^2 Y(1,1)^-1 Y(2,0) Y(2,1)^-1, + Y(1,0) Y(1,3) Y(2,0) Y(2,3)^-1] """ @staticmethod - def __classcall_private__(cls, cartan_type, La): + def __classcall_private__(cls, cartan_type, La=None, c=None): r""" Normalize input to ensure a unique representation. @@ -1102,14 +1152,19 @@ def __classcall_private__(cls, cartan_type, La): sage: M is M1 is M2 True """ + if La is None: + La = cartan_type + cartan_type = La.parent().cartan_type() cartan_type = CartanType(cartan_type) if cartan_type.is_affine(): La = RootSystem(cartan_type).weight_lattice(extended=True)(La) else: La = RootSystem(cartan_type).weight_lattice()(La) - return super(CrystalOfNakajimaMonomials, cls).__classcall__(cls, cartan_type, La) + n = len(cartan_type.index_set()) + c = InfinityCrystalOfNakajimaMonomials._normalize_c(c, n) + return super(CrystalOfNakajimaMonomials, cls).__classcall__(cls, cartan_type, La, c) - def __init__(self, ct, La): + def __init__(self, ct, La, c): r""" EXAMPLES:: @@ -1125,8 +1180,8 @@ def __init__(self, ct, La): cat = ClassicalCrystals() else: cat = (RegularCrystals(), HighestWeightCrystals(), InfiniteEnumeratedSets()) - InfinityCrystalOfNakajimaMonomials.__init__( self, ct, cat, - CrystalOfNakajimaMonomialsElement ) + InfinityCrystalOfNakajimaMonomials.__init__(self, ct, c, + CrystalOfNakajimaMonomialsElement, cat) self._cartan_type = ct self.hw = La gen = {} From d4009d111f473b92e89296e462ebbf0de78161e3 Mon Sep 17 00:00:00 2001 From: Tara Fife Date: Tue, 5 Apr 2016 10:46:37 -0500 Subject: [PATCH 052/163] Fixed typo in src/doc/en/developer/sagenb/set_up_fork.rst Fixed the spelling of Detail. --- src/doc/en/developer/sagenb/set_up_fork.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/developer/sagenb/set_up_fork.rst b/src/doc/en/developer/sagenb/set_up_fork.rst index 66bd918a4da..b0c82043b49 100644 --- a/src/doc/en/developer/sagenb/set_up_fork.rst +++ b/src/doc/en/developer/sagenb/set_up_fork.rst @@ -15,7 +15,7 @@ Overview cd sagenb git remote add upstream git://github.com/sagemath/sagenb.git -In etail +In Detail ========= Clone Your Fork From 2ef42bcfe2b6ff88786c9b09a3f4dc8032aebf72 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 5 Apr 2016 11:21:26 -0500 Subject: [PATCH 053/163] Needing to do a transpose and adding an extra input check. --- .../combinat/crystals/monomial_crystals.py | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/crystals/monomial_crystals.py b/src/sage/combinat/crystals/monomial_crystals.py index b35e57783b6..46d163cabca 100644 --- a/src/sage/combinat/crystals/monomial_crystals.py +++ b/src/sage/combinat/crystals/monomial_crystals.py @@ -464,7 +464,7 @@ def e(self, i): for j_index,j in enumerate(self.parent().index_set()): if i == j: continue - c = self.parent()._c[i-shift,j_index] + c = self.parent()._c[j_index,i-shift] if cm[j_index,i-shift] != 0: Aik[(j, ke+c)] = cm[j_index,i-shift] # Multiply by Aik @@ -509,7 +509,7 @@ def f(self, i): for j_index,j in enumerate(self.parent().index_set()): if i == j: continue - c = self.parent()._c[i-shift,j_index] + c = self.parent()._c[j_index,i-shift] if cm[j_index,i-shift] != 0: Aik[(j, kf+c)] = -cm[j_index,i-shift] # Multiply by Aik @@ -613,7 +613,7 @@ def to_Y_monomial(self): for j_index,j in enumerate(self.parent().index_set()): if k[0] == j: continue - c = self.parent()._c[k[0]-shift,j_index] + c = self.parent()._c[j_index,k[0]-shift] if cm[j_index,k[0]-shift] != 0: Y[(j,k[1]+c)] = Y.get((j,k[1]+c), 0) + v*cm[j_index,k[0]-shift] for k in Y.keys(): @@ -841,10 +841,10 @@ def _normalize_c(c, n): sage: from sage.combinat.crystals.monomial_crystals import InfinityCrystalOfNakajimaMonomials sage: InfinityCrystalOfNakajimaMonomials._normalize_c(None, 4) + [0 1 1 1] + [0 0 1 1] + [0 0 0 1] [0 0 0 0] - [1 0 0 0] - [1 1 0 0] - [1 1 1 0] sage: c = matrix([[0,1,1],[0,0,0],[0,1,0]]); c [0 1 1] [0 0 0] @@ -859,13 +859,15 @@ def _normalize_c(c, n): False """ if c is None: - # Default is i > j <=> c_{ij} = 1 (0 otherwise) - c = [[1 if i > j else 0 for j in range(n)] for i in range(n)] + # Default is i < j <=> c_{ij} = 1 (0 otherwise) + c = [[1 if i < j else 0 for j in range(n)] for i in range(n)] MS = MatrixSpace(ZZ, n, n) c = MS(c) c.set_immutable() if any(c[i,i] != 0 for i in range(n)): raise ValueError("the c matrix must have 0's on the diagonal") + if any(c[i,j] + c[j,i] != 1 for i in range(n) for j in range(i)): + raise ValueError("transpose entries do not sum to 1") return c @staticmethod @@ -950,9 +952,9 @@ def c(self): sage: La = RootSystem(['B',3]).weight_lattice().fundamental_weights() sage: M = crystals.NakajimaMonomials(La[1]+La[2]) sage: M.c() + [0 1 1] + [0 0 1] [0 0 0] - [1 0 0] - [1 1 0] sage: c = Matrix([[0,0,1],[1,0,0],[0,1,0]]) sage: La = RootSystem(['A',2,1]).weight_lattice(extended=True).fundamental_weights() @@ -1124,7 +1126,7 @@ class CrystalOfNakajimaMonomials(InfinityCrystalOfNakajimaMonomials): sage: GM.is_isomorphic(GB,edge_labels=True) True - sage: c = Matrix([[0,0,1],[1,0,0],[0,1,0]]) + sage: c = matrix([[0,1,0],[0,0,1],[1,0,0]]) sage: La = RootSystem(['A',2,1]).weight_lattice(extended=True).fundamental_weights() sage: M = crystals.NakajimaMonomials(2*La[1], c=c) sage: list(M.subcrystal(max_depth=3)) From 421b3b3e7a36b66b6d4d87e2c37f155e5e6514c5 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Tue, 5 Apr 2016 16:25:40 -0300 Subject: [PATCH 054/163] Trac 19964: add doctest for all branches --- src/sage/rings/complex_interval.pyx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx index 1a07e49e778..e7f2fd8baa1 100644 --- a/src/sage/rings/complex_interval.pyx +++ b/src/sage/rings/complex_interval.pyx @@ -1125,6 +1125,17 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): 0.0588235294117647 - 0.200000000000000*I, 0.153846153846154 - 0.300000000000000*I) + TESTS: + + Check that the code is valid in all regions of the complex plane:: + + sage: rats = [15423/906, 337/59976, 145151/145112] + sage: for a in rats: + ....: for b in rats: + ....: x = CIF(a, b) + ....: assert (x * (~x) - 1).contains_zero() + ....: x = -CIF(a,b) + ....: assert (x * (~x) - 1).contains_zero() REFERENCES: From d57fee2259bf69d0f0471f1bc89c3a81f1e12e27 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Tue, 5 Apr 2016 16:26:57 -0300 Subject: [PATCH 055/163] Trac 19964: get rid of trailing whitespaces --- src/sage/rings/complex_interval.pyx | 32 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx index e7f2fd8baa1..5ce0b816b0b 100644 --- a/src/sage/rings/complex_interval.pyx +++ b/src/sage/rings/complex_interval.pyx @@ -781,8 +781,8 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: CIF(2,-3)._div_(CIF(1,-2)) 1.600000000000000? + 0.200000000000000?*I sage: a = CIF((1, 2), (3, 4)) - sage: b = CIF(-1, (2, 3)) - sage: c = a/b + sage: b = CIF(-1, (2, 3)) + sage: c = a/b sage: c.endpoints() (0.500000000000000 - 1.60000000000000*I, 1.50000000000000 - 0.600000000000000*I, @@ -1139,10 +1139,10 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): REFERENCES: - .. [RL] J. Rokne, P. Lancaster. Complex interval arithmetic. + .. [RL] J. Rokne, P. Lancaster. Complex interval arithmetic. Communications of the ACM 14. 1971. - """ - + """ + cdef ComplexIntervalFieldElement x x = self._new() @@ -1151,33 +1151,33 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): mpfr_init2(b, self._prec) mpfr_init2(c, self._prec) mpfr_init2(d, self._prec) - + cdef mpfr_t rmin, rmax, imin, imax mpfr_init2(rmin, self._prec) mpfr_init2(rmax, self._prec) mpfr_init2(imin, self._prec) mpfr_init2(imax, self._prec) - + cdef mpfr_t r mpfr_init2(r, self._prec) - + mpfi_get_left(a, self.__re) mpfi_get_right(b, self.__re) mpfi_get_left(c, self.__im) mpfi_get_right(d, self.__im) - + cdef mpfr_t a2, b2, d2, c2 mpfr_init2(a2, self._prec) mpfr_init2(b2, self._prec) mpfr_init2(c2, self._prec) mpfr_init2(d2, self._prec) - + cdef mpfr_t div1, div2, aux, aux2 mpfr_init2(div1, self._prec) mpfr_init2(div2, self._prec) mpfr_init2(aux, self._prec) mpfr_init2(aux2, self._prec) - + if mpfr_sgn(a) >= 0 and mpfr_sgn(c)>=0: #input interval lies in first quadrant # left endpoint mpfr_mul(a2, a, a, MPFR_RNDU) @@ -1197,7 +1197,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): mpfr_div(aux2, d, div2, MPFR_RNDU) mpfr_sub(aux2, aux, aux2, MPFR_RNDU) mpfr_max(imax, aux2, imax, MPFR_RNDU) - # lower endpoint, it is the lowest point of the circle or one of + # lower endpoint, it is the lowest point of the circle or one of if mpfr_cmp(d, a) >=0 and mpfr_cmp(c, a) <= 0: mpfr_add(imin, a, a, MPFR_RNDD) mpfr_set_si(aux, -1, MPFR_RNDD) @@ -1230,7 +1230,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): mpfr_mul(b2, b, b, MPFR_RNDD) mpfr_mul(c2, c, c, MPFR_RNDD) mpfr_add(div1, b2, c2, MPFR_RNDD) - mpfr_div(rmax, b, div1, MPFR_RNDU) + mpfr_div(rmax, b, div1, MPFR_RNDU) elif mpfr_sgn(c) > 0 and mpfr_sgn(b) > 0: #between first and second quadrant # left endpoint mpfr_abs(aux, a, MPFR_RNDU) @@ -1283,11 +1283,11 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): elif mpfr_sgn(a) >=0: #fourth or between fourth and first I = self.parent().gen(0) return I*(I*self).__invert__() - - + + mpfi_set_fr(x.__re, rmin) mpfi_put_fr(x.__re, rmax) - + mpfi_set_fr(x.__im, imin) mpfi_put_fr(x.__im, imax) From 53891f2886fabcaf7c40e4b30995c1ffffdf558f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 5 Apr 2016 18:31:17 -0500 Subject: [PATCH 056/163] Added some more documentation and input checking. --- .../combinat/crystals/monomial_crystals.py | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/crystals/monomial_crystals.py b/src/sage/combinat/crystals/monomial_crystals.py index 46d163cabca..cacff65d88c 100644 --- a/src/sage/combinat/crystals/monomial_crystals.py +++ b/src/sage/combinat/crystals/monomial_crystals.py @@ -750,6 +750,8 @@ def f(self, i): class InfinityCrystalOfNakajimaMonomials(UniqueRepresentation, Parent): r""" + Crystal `B(\infty)` in terms of (modified) Nakajima monomials. + Let `Y_{i,k}`, for `i \in I` and `k \in \ZZ`, be a commuting set of variables, and let `\boldsymbol{1}` be a new variable which commutes with each `Y_{i,k}`. (Here, `I` represents the index set of a Cartan datum.) One @@ -775,14 +777,15 @@ class InfinityCrystalOfNakajimaMonomials(UniqueRepresentation, Parent): \end{aligned} where `\{h_i : i \in I\}` and `\{\Lambda_i : i \in I \}` are the simple - coroots and fundamental weights, respectively. With a chosen set of integers - `C = (c_{ij})_{i\neq j}` such that `c_{ij}+c{ji} =1`, one defines + coroots and fundamental weights, respectively. With a chosen set of + non-negative integers `C = (c_{ij})_{i\neq j}` such that + `c_{ij} + c{ji} = 1`, one defines .. MATH:: A_{i,k} = Y_{i,k} Y_{i,k+1} \prod_{j\neq i} Y_{j,k+c_{ji}}^{a_{ji}}, - where `(a_{ij})` is a Cartan matrix. Then + where `(a_{ij})_{i,j \in I}` is a Cartan matrix. Then .. MATH:: @@ -801,7 +804,13 @@ class InfinityCrystalOfNakajimaMonomials(UniqueRepresentation, Parent): - ``cartan_type`` -- a Cartan type - - ``use_Y`` -- choice of monomials in terms of `A` or `Y` + - ``use_Y`` -- (default: ``True``) choice of monomials in terms + of `A` or `Y` + + - ``c`` -- (optional) the matrix `(c_{ij})_{i,j \in I}` such that + `c_{ii} = 0` for all `i \in I`, `c_{ij} \in ZZ_{>0}` for all + `i,j \in I`, and `c_{ij} + c_{ji} = 1` for all `i \neq j`; the + default is `c_{ij} = 0` if `i < j` and `0` otherwise EXAMPLES:: @@ -857,6 +866,24 @@ def _normalize_c(c, n): [0 1 0] sage: C.is_mutable() False + + TESTS:: + + sage: c = matrix([[0,1],[0,1]]) + sage: C = InfinityCrystalOfNakajimaMonomials._normalize_c(c, 2) + Traceback (most recent call last): + ... + ValueError: the c matrix must have 0's on the diagonal + sage: c = matrix([[0,2],[-1,0]]) + sage: C = InfinityCrystalOfNakajimaMonomials._normalize_c(c, 2) + Traceback (most recent call last): + ... + ValueError: the c matrix must have non-negative entries + sage: c = matrix([[0,1],[1,0]]) + sage: C = InfinityCrystalOfNakajimaMonomials._normalize_c(c, 2) + Traceback (most recent call last): + ... + ValueError: transpose entries do not sum to 1 """ if c is None: # Default is i < j <=> c_{ij} = 1 (0 otherwise) @@ -868,6 +895,8 @@ def _normalize_c(c, n): raise ValueError("the c matrix must have 0's on the diagonal") if any(c[i,j] + c[j,i] != 1 for i in range(n) for j in range(i)): raise ValueError("transpose entries do not sum to 1") + if any(c[i,j] < 0 or c[j,i] < 0 for i in range(n) for j in range(i)): + raise ValueError("the c matrix must have non-negative entries") return c @staticmethod From 825bf354258f8d0bf632bdd686054cf9f0119cf7 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 5 Apr 2016 23:10:31 -0500 Subject: [PATCH 057/163] Making the choice of monomial a global option. --- .../combinat/crystals/monomial_crystals.py | 666 +++++++++--------- 1 file changed, 342 insertions(+), 324 deletions(-) diff --git a/src/sage/combinat/crystals/monomial_crystals.py b/src/sage/combinat/crystals/monomial_crystals.py index cacff65d88c..18d608600c3 100644 --- a/src/sage/combinat/crystals/monomial_crystals.py +++ b/src/sage/combinat/crystals/monomial_crystals.py @@ -100,6 +100,7 @@ from sage.structure.element import Element from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.global_options import GlobalOptions from sage.categories.classical_crystals import ClassicalCrystals from sage.categories.highest_weight_crystals import HighestWeightCrystals from sage.categories.regular_crystals import RegularCrystals @@ -112,11 +113,44 @@ from sage.matrix.matrix import is_Matrix from sage.matrix.matrix_space import MatrixSpace -class NakajimaYMonomial(Element): + +NakajimaMonomialGlobalOptions=GlobalOptions(name='Nakajima_monomials', + doc=r""" + Sets the global options for Nakajima monomials. The default is to + use the `Y` variables. + + There are the following variables: + + - `Y` -- corresponds to fundamental weights + - `A` -- corresponds to simple roots + + If the `A` variables are used, the output is written as + `\prod_i Y_{i0}^{\lambda_i} \prod_{ik} A_{ik}^c_{ik}`, where + `\sum_{i \in I} \lambda_i \Lambda_i` is the corresponding + dominant weight. + """, + end_doc=r""" + If no parameters are set, then the function returns a copy of the + options dictionary. + + EXAMPLES:: + + sage: M = crystals.infinity.NakajimaMonomials(['A',2]) + """, + monomial=dict(default="Y", + description='Sets which variables to use for output', + values=dict(Y='use the Y variables', + A='use the A variables'), + case_sensitive=True) +) + +class NakajimaMonomial(Element): r""" - Monomials of the form `Y_{i_1,k_1}^{a_1}\cdots Y_{i_t,k_t}^{y_t}`, where - `i_1,\dots,i_t` are elements of the index set, `k_1,\dots,k_t` are - nonnegative integers, and `y_1,\dots,y_t` are integers. + An element of the monomial crystal. + + Monomials of the form `Y_{i_1,k_1}^{a_1} \cdots Y_{i_t,k_t}^{y_t}`, + where `i_1, \dots, i_t` are elements of the index set, `k_1, \dots, k_t` + are nonnegative integers, and `y_1, \dots, y_t` are integers. EXAMPLES:: @@ -125,14 +159,26 @@ class NakajimaYMonomial(Element): sage: mg 1 sage: mg.f_string([1,3,2,0,1,2,3,0,0,1]) - Y(0,0)^-1 Y(0,1)^-1 Y(0,2)^-1 Y(0,3)^-1 Y(1,0)^-3 Y(1,1)^-2 Y(1,2) Y(2,0)^3 Y(2,2) Y(3,0) Y(3,2)^-1 + Y(0,0)^-1 Y(0,1)^-1 Y(0,2)^-1 Y(0,3)^-1 Y(1,0)^-3 + Y(1,1)^-2 Y(1,2) Y(2,0)^3 Y(2,2) Y(3,0) Y(3,2)^-1 + + An example using the `A` variables:: + + sage: M = crystals.infinity.NakajimaMonomials("A3") + sage: M.global_options(monomial='A') + sage: mg = M.module_generators[0] + sage: mg.f_string([1,2,3,2,1]) + A(1,0)^-1 A(1,1)^-1 A(2,0)^-2 A(3,0)^-1 + sage: mg.f_string([3,2,1]) + A(1,2)^-1 A(2,1)^-1 A(3,0)^-1 + sage: M.global_options.reset() """ - def __init__(self,parent,dict): + def __init__(self, parent, Y, A): r""" INPUT: - - ``dict`` -- a dictionary of with pairs of the form ``{(i,k):y}`` + - ``d`` -- a dictionary of with pairs of the form ``{(i,k): y}`` EXAMPLES:: @@ -140,27 +186,75 @@ def __init__(self,parent,dict): sage: mg = M.module_generators[0] sage: TestSuite(mg).run() """ - self._dict = dict + self._Y = Y + self._A = A Element.__init__(self, parent) def _repr_(self): r""" Return a string representation of ``self``. + EXAMPLES:: + + sage: M = crystals.infinity.NakajimaMonomials(['A',5,2]) + sage: x = M({(1,0):1, (2,2):-2, (0,5):10}); x + Y(0,5)^10 Y(1,0) Y(2,2)^-2 + sage: M.global_options(monomial='A') + sage: x + A(1,0)^-2 A(1,1)^-2 A(2,0)^-4 A(2,1)^-2 A(3,0)^-2 + sage: M.global_options.reset() + """ + return self.parent().global_options.dispatch(self, '_repr_', 'monomial') + + def _repr_Y(self): + r""" + Return a string representation of ``self`` in the `Y` variables. + EXAMPLES:: sage: M = crystals.infinity.NakajimaMonomials(['A',5,2]) sage: M({(1,0):1,(2,2):-2,(0,5):10}) Y(0,5)^10 Y(1,0) Y(2,2)^-2 """ - if not self._dict: + if not self._Y: return "1" - L = sorted(self._dict.iteritems(), key=lambda x: (x[0][0], x[0][1])) + L = sorted(self._Y.iteritems(), key=lambda x: (x[0][0], x[0][1])) exp = lambda e: "^{}".format(e) if e != 1 else "" return ' '.join("Y({},{})".format(mon[0][0], mon[0][1]) + exp(mon[1]) for mon in L) + def _repr_A(self): + r""" + Return a string representation of ``self`` in the `A` variables. + + EXAMPLES:: + + sage: M = crystals.infinity.NakajimaMonomials(['B',4,1]) + sage: m = M.module_generators[0].f_string([4,2,1]) + sage: m._repr_A() + 'A(1,1)^-1 A(2,0)^-1 A(4,0)^-1' + """ + try: + Y = {(i,0): c for i,c in self.parent().hw} + except: + Y = {} + + if not Y and not self._A: + return "1" + + L = sorted(Y.iteritems(), key=lambda x: (x[0][0], x[0][1])) + exp = lambda e: "^{}".format(e) if e != 1 else "" + ret = ' '.join("Y({},{})".format(mon[0][0], mon[0][1]) + exp(mon[1]) + for mon in L) + if not self._A: + return ret + if Y: + ret += ' ' + L = sorted(self._A.iteritems(), key=lambda x: (x[0][0], x[0][1])) + return ret + ' '.join("A({},{})".format(mon[0][0], mon[0][1]) + exp(mon[1]) + for mon in L) + def __hash__(self): r""" TESTS:: @@ -171,9 +265,9 @@ def __hash__(self): 4715601665014767730 # 64-bit -512614286 # 32-bit """ - return hash(frozenset(tuple(self._dict.iteritems()))) + return hash(frozenset(tuple(self._Y.iteritems()))) - def __eq__(self,other): + def __eq__(self, other): r""" EXAMPLES:: @@ -185,11 +279,11 @@ def __eq__(self,other): sage: m1.__eq__(m1) True """ - if isinstance(other, NakajimaYMonomial): - return self._dict == other._dict - return self._dict == other + if isinstance(other, NakajimaMonomial): + return self._Y == other._Y + return self._Y == other - def __ne__(self,other): + def __ne__(self, other): r""" EXAMPLES:: @@ -204,7 +298,7 @@ def __ne__(self,other): """ return not self == other - def __lt__(self,other): + def __lt__(self, other): r""" EXAMPLES:: @@ -225,20 +319,71 @@ def _latex_(self): EXAMPLES:: sage: M = crystals.infinity.NakajimaMonomials(['G',2,1]) - sage: M.module_generators[0].f_string([1,0,2])._latex_() + sage: x = M.module_generators[0].f_string([1,0,2]) + sage: latex(x) + Y_{0,0}^{-1} Y_{1,0}^{-1} Y_{1,1}^{2} Y_{2,0} Y_{2,1}^{-1} + sage: M.global_options(monomial='A') + sage: latex(x) + A_{0,0}^{-1} A_{1,0}^{-1} A_{2,0}^{-1} + sage: M.global_options.reset() + """ + return self.parent().global_options.dispatch(self, '_latex_', 'monomial') + + def _latex_Y(self): + r""" + Return a `\LaTeX` representation of ``self`` in the `Y` variables. + + EXAMPLES:: + + sage: M = crystals.infinity.NakajimaMonomials(['G',2,1]) + sage: M.module_generators[0].f_string([1,0,2])._latex_Y() 'Y_{0,0}^{-1} Y_{1,0}^{-1} Y_{1,1}^{2} Y_{2,0} Y_{2,1}^{-1} ' """ - if self._dict == {}: + if not self._Y: return "\\boldsymbol{1}" - else: - L = sorted(self._dict.iteritems(), key=lambda x:(x[0][0],x[0][1])) - return_str = '' - for x in range(len(L)): - if L[x][1] != 1: - return_str += "Y_{%s,%s}"%(L[x][0][0],L[x][0][1]) + "^{%s} "%L[x][1] - else: - return_str += "Y_{%s,%s} "%(L[x][0][0],L[x][0][1]) - return return_str + + L = sorted(self._Y.iteritems(), key=lambda x:(x[0][0],x[0][1])) + return_str = '' + for x in L: + if x[1] != 1: + return_str += "Y_{%s,%s}"%(x[0][0],x[0][1]) + "^{%s} "%x[1] + else: + return_str += "Y_{%s,%s} "%(x[0][0],x[0][1]) + return return_str + + def _latex_A(self): + r""" + Return a `\LaTeX` representation of ``self`` in the `A` variables. + + EXAMPLES:: + + sage: M = crystals.infinity.NakajimaMonomials(['C',4,1]) + sage: m = M.module_generators[0].f_string([4,2,3]) + sage: m._latex_A() + 'A_{2,0}^{-1} A_{3,1}^{-1} A_{4,0}^{-1} ' + """ + try: + Y = {(i,0): c for i,c in self.parent().hw} + except: + Y = {} + + if not Y and not self._A: + return "\\boldsymbol{1}" + + L = sorted(Y.iteritems(), key=lambda x:(x[0][0],x[0][1])) + return_str = '' + for x in L: + if x[1] != 1: + return_str += "Y_{%s,%s}"%(x[0][0],x[0][1]) + "^{%s} "%x[1] + else: + return_str += "Y_{%s,%s} "%(x[0][0],x[0][1]) + L = sorted(self._A.iteritems(), key=lambda x:(x[0][0],x[0][1])) + for x in L: + if x[1] != 1: + return_str += "A_{%s,%s}"%(x[0][0],x[0][1]) + "^{%s} "%x[1] + else: + return_str += "A_{%s,%s} "%(x[0][0],x[0][1]) + return return_str def _classical_weight(self): r""" @@ -259,7 +404,7 @@ def _classical_weight(self): """ P = self.parent().weight_lattice_realization() La = P.fundamental_weights() - return P(sum(v*La[k[0]] for k,v in self._dict.iteritems())) + return P(sum(v*La[k[0]] for k,v in self._Y.iteritems())) def weight_in_root_lattice(self): r""" @@ -277,11 +422,15 @@ def weight_in_root_lattice(self): sage: m = mg.f_string([1,3,2,0,1,2,3,0,0,1]) sage: m.weight_in_root_lattice() -3*alpha[0] - 3*alpha[1] - 2*alpha[2] - 2*alpha[3] + + sage: M = crystals.infinity.NakajimaMonomials(['C',3,1]) + sage: m = M.module_generators[0].f_string([3,0,1,2,0]) + sage: m.weight_in_root_lattice() + -2*alpha[0] - alpha[1] - alpha[2] - alpha[3] """ Q = RootSystem(self.parent().cartan_type()).root_lattice() - alpha = Q.simple_roots() - path = self.to_highest_weight() - return Q(sum(-alpha[j] for j in path[1])) + al = Q.simple_roots() + return Q.sum(e*al[k[0]] for k,e in self._A.iteritems()) def weight(self): r""" @@ -290,9 +439,14 @@ def weight(self): EXAMPLES:: sage: C = crystals.infinity.NakajimaMonomials(['A',1,1]) - sage: v=C.highest_weight_vector() - sage: v.f(1).weight()+v.f(0).weight() + sage: v = C.highest_weight_vector() + sage: v.f(1).weight() + v.f(0).weight() -delta + + sage: M = crystals.infinity.NakajimaMonomials(['A',4,2]) + sage: m = M.highest_weight_vector().f_string([1,2,0,1]) + sage: m.weight() + 2*Lambda[0] - Lambda[1] - delta """ P = self.parent().weight_lattice_realization() return P(self.weight_in_root_lattice()) @@ -311,6 +465,11 @@ def epsilon(self, i): sage: m = M.module_generators[0].f(2) sage: [m.epsilon(i) for i in M.index_set()] [0, 0, 1] + + sage: M = crystals.infinity.NakajimaMonomials(['C',4,1]) + sage: m = M.module_generators[0].f_string([4,2,3]) + sage: [m.epsilon(i) for i in M.index_set()] + [0, 0, 0, 1, 0] """ if i not in self.parent().index_set(): raise ValueError("i must be an element of the index set") @@ -331,13 +490,18 @@ def phi(self, i): sage: m = M.module_generators[0].f(1) sage: [m.phi(i) for i in M.index_set()] [1, -1, 1] + + sage: M = crystals.infinity.NakajimaMonomials(['C',4,1]) + sage: m = M.module_generators[0].f_string([4,2,3]) + sage: [m.phi(i) for i in M.index_set()] + [0, 1, -1, 2, -1] """ if i not in self.parent().index_set(): raise ValueError("i must be an element of the index set") - if not self._dict or all(x[0] != i for x in self._dict): + if not self._Y or all(x[0] != i for x in self._Y): return ZZ.zero() - d = copy(self._dict) + d = copy(self._Y) K = max(x[1] for x in d if x[0] == i) for a in range(K): if (i,a) in d: @@ -367,7 +531,7 @@ def _ke(self, i): if phi == self._classical_weight().scalar(h[i]): # self.epsilon(i) == 0 return Infinity - d = copy(self._dict) + d = copy(self._Y) K = max(x[1] for x in d if x[0] == i) for a in range(K): if (i,a) in d: @@ -399,23 +563,23 @@ def _kf(self, i): sage: [m._kf(i) for i in M.index_set()] [0, 0, 2, 0, 0] """ - if all(i != x[0] for x in self._dict): + if all(i != x[0] for x in self._Y): return ZZ.zero() - else: - d = copy(self._dict) - K = max(key[1] for key in d if key[0] == i) - for a in range(K): - if (i,a) in d: - continue - else: - d[(i,a)] = 0 - S = sorted((x for x in d.iteritems() if x[0][0] == i), key=lambda x: x[0][1]) - sum = 0 - phi = self.phi(i) - for var,exp in S: - sum += exp - if sum == phi: - return var[1] + + d = copy(self._Y) + K = max(key[1] for key in d if key[0] == i) + for a in range(K): + if (i,a) in d: + continue + else: + d[(i,a)] = 0 + S = sorted((x for x in d.iteritems() if x[0][0] == i), key=lambda x: x[0][1]) + sum = 0 + phi = self.phi(i) + for var,exp in S: + sum += exp + if sum == phi: + return var[1] def e(self, i): r""" @@ -447,13 +611,24 @@ def e(self, i): Y(1,0)^-1 Y(1,1)^-1 Y(2,0), None, None] + + sage: M = crystals.infinity.NakajimaMonomials(['D',4,1]) + sage: M.global_options(monomial='A') + sage: m = M.module_generators[0].f_string([4,2,3,0]) + sage: [m.e(i) for i in M.index_set()] + [A(2,1)^-1 A(3,1)^-1 A(4,0)^-1, + None, + None, + A(0,2)^-1 A(2,1)^-1 A(4,0)^-1, + None] + sage: M.global_options.reset() """ if i not in self.parent().index_set(): raise ValueError("i must be an element of the index set") if self.epsilon(i) == 0: return None - newdict = copy(self._dict) + newdict = copy(self._Y) ke = self._ke(i) Aik = {(i, ke): 1, (i, ke+1): 1} ct = self.parent().cartan_type() @@ -476,7 +651,11 @@ def e(self, i): newdict[key] += value else: newdict[key] = value - return self.__class__(self.parent(), newdict) + A = copy(self._A) + A[(i,ke)] = A.get((i,ke),0) + 1 + if not A[(i,ke)]: + del A[(i,ke)] + return self.__class__(self.parent(), newdict, A) def f(self, i): r""" @@ -498,7 +677,7 @@ def f(self, i): """ if i not in self.parent().index_set(): raise ValueError("i must be an element of the index set") - newdict = copy(self._dict) + newdict = copy(self._Y) kf = self._kf(i) Aik = {(i, kf): -1, (i, kf+1): -1} ct = self.parent().cartan_type() @@ -521,259 +700,44 @@ def f(self, i): newdict[key] += value else: newdict[key] = value - return self.__class__(self.parent(), newdict) - -class NakajimaAMonomial(NakajimaYMonomial): - r""" - Monomials of the form `A_{i_1,k_1}^{a_1}\cdots A_{i_t,k_t}^{a_t}`, where - `i_1,\dots,i_t` are elements of the index set, `k_1,\dots,k_t` are - nonnegative integers, and `a_1,\dots,a_t` are integers. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials("A3",use_Y=False) - sage: mg = M.module_generators[0] - sage: mg.f_string([1,2,3,2,1]) - A(1,0)^-1 A(1,1)^-1 A(2,0)^-2 A(3,0)^-1 - sage: mg.f_string([3,2,1]) - A(1,2)^-1 A(2,1)^-1 A(3,0)^-1 - """ - - def _repr_(self): - r""" - Return a string representation of ``self``. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['B',4,1],use_Y=False) - sage: m = M.module_generators[0].f_string([4,2,1]) - sage: m - A(1,1)^-1 A(2,0)^-1 A(4,0)^-1 - """ - if not self._dict: - return "1" - L = sorted(self._dict.iteritems(), key=lambda x: (x[0][0], x[0][1])) - exp = lambda e: "^{}".format(e) if e != 1 else "" - return ' '.join("A({},{})".format(mon[0][0], mon[0][1]) + exp(mon[1]) - for mon in L) - - def _latex_(self): - r""" - Return a `\LaTeX` representation of ``self``. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['C',4,1],use_Y=False) - sage: m = M.module_generators[0].f_string([4,2,3]) - sage: m._latex_() - 'A_{2,0}^{-1} A_{3,1}^{-1} A_{4,0}^{-1} ' - """ - if self._dict == {}: - return "\\boldsymbol{1}" - else: - L = sorted(self._dict.iteritems(), key=lambda x:(x[0][0],x[0][1])) - return_str = '' - for x in range(len(L)): - if L[x][1] !=1: - return_str += "A_{%s,%s}"%(L[x][0][0],L[x][0][1]) + "^{%s} "%L[x][1] - else: - return_str += "A_{%s,%s} "%(L[x][0][0],L[x][0][1]) - return return_str - - def to_Y_monomial(self): - r""" - Convert ``self`` to a monomial in the `Y_{ik}` variables. - - Represent `\prod_{(i,k)} A_{i,k}^{a_{i}(k)}` in the form - `\prod_{(i,k)} Y_{i,k}^{y_i(k)}` using the formula - - .. MATH:: - - A_{i,k} = Y_{i,k} Y_{i,k+1} \prod_{\substack{j \in I \\ j\neq i}} - Y_{i,k+c_{ji}}^{a_{ji}}. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['A',2,1], use_Y=False) - sage: m = M.module_generators[0].f_string([2,0,1,2,1]) - sage: m - A(0,1)^-1 A(1,1)^-2 A(2,0)^-1 A(2,1)^-1 - sage: m.to_Y_monomial() - Y(0,2)^2 Y(1,2)^-1 Y(2,0)^-1 Y(2,1) Y(2,2)^-1 - """ - Y = {} - ct = self.parent().cartan_type() - cm = ct.cartan_matrix() - shift = 0 - if ct.is_finite(): - shift = 1 - for k,v in self._dict.iteritems(): - Y[k] = Y.get(k,0) + v - Y[(k[0],k[1]+1)] = Y.get((k[0],k[1]+1), 0) + v - for j_index,j in enumerate(self.parent().index_set()): - if k[0] == j: - continue - c = self.parent()._c[j_index,k[0]-shift] - if cm[j_index,k[0]-shift] != 0: - Y[(j,k[1]+c)] = Y.get((j,k[1]+c), 0) + v*cm[j_index,k[0]-shift] - for k in Y.keys(): - if Y[k] == 0: - del Y[k] - return NakajimaYMonomial(self.parent(), Y) - - def weight(self): - r""" - Return the weight of ``self`` as an element of - ``self.parent().weight_lattice_realization()``. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['A',4,2],use_Y=False) - sage: m = M.module_generators[0].f_string([1,2,0,1]) - sage: m.weight() - 2*Lambda[0] - Lambda[1] - delta - """ - return self.to_Y_monomial().weight() - - def weight_in_root_lattice(self): - r""" - Return the weight of ``self`` as an element of the root lattice. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['C',3,1],use_Y=False) - sage: m = M.module_generators[0].f_string([3,0,1,2,0]) - sage: m.weight_in_root_lattice() - -2*alpha[0] - alpha[1] - alpha[2] - alpha[3] - """ - return self.to_Y_monomial().weight_in_root_lattice() - - def epsilon(self,i): - r""" - Return the action of `\varepsilon_i` on ``self``. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['C',4,1],use_Y=False) - sage: m = M.module_generators[0].f_string([4,2,3]) - sage: [m.epsilon(i) for i in M.index_set()] - [0, 0, 0, 1, 0] - """ - if i not in self.parent().index_set(): - raise ValueError("i must be an element of the index set") - return self.to_Y_monomial().epsilon(i) - - def phi(self,i): - r""" - Return the action of `\varphi_i` on ``self``. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['C',4,1],use_Y=False) - sage: m = M.module_generators[0].f_string([4,2,3]) - sage: [m.phi(i) for i in M.index_set()] - [0, 1, -1, 2, -1] - """ - if i not in self.parent().index_set(): - raise ValueError("i must be an element of the index set") - return self.to_Y_monomial().phi(i) - - def e(self,i): - r""" - Return the action of `e_i` on ``self``. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['D',4,1],use_Y=False) - sage: m = M.module_generators[0].f_string([4,2,3,0]) - sage: [m.e(i) for i in M.index_set()] - [A(2,1)^-1 A(3,1)^-1 A(4,0)^-1, - None, - None, - A(0,2)^-1 A(2,1)^-1 A(4,0)^-1, - None] - """ - if i not in self.parent().index_set(): - raise ValueError("i must be an element of the index set") - if self.epsilon(i) == 0: - return None - ke = self.to_Y_monomial()._ke(i) - d = copy(self._dict) - d[(i,ke)] = d.get((i,ke),0)+1 - for k in list(d): - if d[k] == 0: - d.pop(k) - return self.__class__(self.parent(), d) - - def f(self, i): - r""" - Return the action of `f_i` on ``self``. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials("E8",use_Y=False) - sage: m = M.module_generators[0].f_string([4,2,3,8]) - sage: m - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1 - sage: [m.f(i) for i in M.index_set()] - [A(1,2)^-1 A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1, - A(2,0)^-1 A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1, - A(2,1)^-1 A(3,0)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1, - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(4,1)^-1 A(8,0)^-1, - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(5,0)^-1 A(8,0)^-1, - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(6,0)^-1 A(8,0)^-1, - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(7,1)^-1 A(8,0)^-1, - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-2] - """ - if i not in self.parent().index_set(): - raise ValueError("i must be an element of the index set") - kf = self.to_Y_monomial()._kf(i) - d = copy(self._dict) - d[(i,kf)] = d.get((i,kf),0) - 1 - return self.__class__(self.parent(), d) + A = copy(self._A) + A[(i,kf)] = A.get((i,kf),0) - 1 + if not A[(i,kf)]: + del A[(i,kf)] + return self.__class__(self.parent(), newdict, A) class InfinityCrystalOfNakajimaMonomials(UniqueRepresentation, Parent): r""" Crystal `B(\infty)` in terms of (modified) Nakajima monomials. Let `Y_{i,k}`, for `i \in I` and `k \in \ZZ`, be a commuting set of - variables, and let `\boldsymbol{1}` be a new variable which commutes with - each `Y_{i,k}`. (Here, `I` represents the index set of a Cartan datum.) One - may endow the structure of a crystal on the set `\widehat{\mathcal{M}}` of - monomials of the form + variables, and let `\boldsymbol{1}` be a new variable which commutes + with each `Y_{i,k}`. (Here, `I` represents the index set of a Cartan + datum.) One may endow the structure of a crystal on the + set `\widehat{\mathcal{M}}` of monomials of the form .. MATH:: M = \prod_{(i,k) \in I\times \ZZ_{\ge0}} Y_{i,k}^{y_i(k)}\boldsymbol{1}. - Elements of `\widehat{\mathcal{M}}` are called *modified Nakajima monomials*. - We will omit the `\boldsymbol{1}` from the end of a monomial if there exists - at least one `y_i(k) \neq 0`. The crystal structure on this set is defined by + Elements of `\widehat{\mathcal{M}}` are called + *modified Nakajima monomials*. We will omit the `\boldsymbol{1}` + from the end of a monomial if there exists at least one `y_i(k) \neq 0`. + The crystal structure on this set is defined by .. MATH:: \begin{aligned} - \mathrm{wt}(M) &= \sum_{i\in I} \Bigl( \sum_{k\ge 0} y_i(k) \Bigr) \Lambda_i, \\ - \varphi_i(M) &= \max\Bigl\{ \sum_{0\le j \le k} y_i(j) : k\ge 0 \Bigr\}, \\ - \varepsilon_i(M) &= \varphi_i(M) - \langle h_i, \mathrm{wt}(M) \rangle, \\ - k_f = k_f(M) &= \min\Bigl\{ k\ge 0 : \varphi_i(M) = \sum_{0\le j\le k} y_i(j) \Bigr\}, \\ - k_e = k_e(M) &= \max\Bigl\{ k\ge 0 : \varphi_i(M) = \sum_{0\le j\le k} y_i(j) \Bigr\}, + \mathrm{wt}(M) & = \sum_{i\in I} \Bigl( \sum_{k \ge 0} + y_i(k) \Bigr) \Lambda_i, \\ + \varphi_i(M) & = \max\Bigl\{ \sum_{0 \le j \le k} y_i(j) : + k \ge 0 \Bigr\}, \\ + \varepsilon_i(M) & = \varphi_i(M) - + \langle h_i, \mathrm{wt}(M) \rangle, \\ + k_f = k_f(M) & = \min\Bigl\{ k \ge 0 : + \varphi_i(M) = \sum_{0 \le j \le k} y_i(j) \Bigr\}, \\ + k_e = k_e(M) & = \max\Bigl\{ k \ge 0 : + \varphi_i(M) = \sum_{0 \le j \le k} y_i(j) \Bigr\}, \end{aligned} where `\{h_i : i \in I\}` and `\{\Lambda_i : i \in I \}` are the simple @@ -796,17 +760,14 @@ class InfinityCrystalOfNakajimaMonomials(UniqueRepresentation, Parent): \end{aligned} It is shown in [KKS07]_ that the connected component of - `\widehat{\mathcal{M}}` containing the element `\boldsymbol{1}`, which we - denote by `\mathcal{M}(\infty)`, is crystal isomorphic to the crystal - `B(\infty)`. + `\widehat{\mathcal{M}}` containing the element `\boldsymbol{1}`, + which we denote by `\mathcal{M}(\infty)`, is crystal isomorphic + to the crystal `B(\infty)`. INPUT: - ``cartan_type`` -- a Cartan type - - ``use_Y`` -- (default: ``True``) choice of monomials in terms - of `A` or `Y` - - ``c`` -- (optional) the matrix `(c_{ij})_{i,j \in I}` such that `c_{ii} = 0` for all `i \in I`, `c_{ij} \in ZZ_{>0}` for all `i,j \in I`, and `c_{ij} + c_{ji} = 1` for all `i \neq j`; the @@ -900,7 +861,7 @@ def _normalize_c(c, n): return c @staticmethod - def __classcall_private__(cls, ct, c=None, use_Y=True): + def __classcall_private__(cls, ct, c=None, use_Y=None): r""" Normalize input to ensure a unique representation. @@ -916,19 +877,16 @@ def __classcall_private__(cls, ct, c=None, use_Y=True): sage: M is M1 is M2 True """ - if isinstance(use_Y, bool): - if use_Y: - elt_class = NakajimaYMonomial - else: - elt_class = NakajimaAMonomial - else: - elt_class = use_Y + if use_Y is not None: + from sage.misc.superseded import deprecation + deprecation(18895, 'use_Y is deprecated; use the global_options variable "monomial" instead.') + cartan_type = CartanType(ct) n = len(cartan_type.index_set()) c = InfinityCrystalOfNakajimaMonomials._normalize_c(c, n) - return super(InfinityCrystalOfNakajimaMonomials, cls).__classcall__(cls, cartan_type, c, elt_class) + return super(InfinityCrystalOfNakajimaMonomials, cls).__classcall__(cls, cartan_type, c) - def __init__(self, ct, c, elt_class, category=None): + def __init__(self, ct, c, category=None): r""" EXAMPLES:: @@ -938,19 +896,21 @@ def __init__(self, ct, c, elt_class, category=None): self._cartan_type = ct self._c = c - self.Element = elt_class if category is None: category = (HighestWeightCrystals(), InfiniteEnumeratedSets()) Parent.__init__(self, category=category) - self.module_generators = (self.element_class(self,{}),) + self.module_generators = (self.element_class(self, {}, {}),) - def _element_constructor_(self,dict): + def _element_constructor_(self, Y=None, A=None): r""" - Construct an element of ``self`` from ``dict``. + Construct an element of ``self`` from ``Y``. INPUT: - - ``dict`` -- a dictionary whose key is a pair and whose value is an integer + - ``Y`` -- a dictionary whose key is a pair and whose value + is an integer + - ``A`` -- a dictionary whose key is a pair and whose value + is an integer EXAMPLES:: @@ -958,8 +918,46 @@ def _element_constructor_(self,dict): sage: m = M({(1,0):-1,(1,1):-1,(2,0):1}) sage: m Y(1,0)^-1 Y(1,1)^-1 Y(2,0) + + sage: M = crystals.infinity.NakajimaMonomials(['A',2,1]) + sage: m = M(A={(0,1): -1, (1,1): -2, (2,0): -1, (2,1): -1}) + sage: m._repr_A() + 'A(0,1)^-1 A(1,1)^-2 A(2,0)^-1 A(2,1)^-1' + sage: m + Y(0,2)^2 Y(1,2)^-1 Y(2,0)^-1 Y(2,1) Y(2,2)^-1 + sage: m == M.highest_weight_vector().f_string([2,0,1,2,1]) + True """ - return self.element_class(self,dict) + if A is None: + if Y is None: + return self.module_generators[0] + # This is a crude way to determine the A, but it works + hw,path = self.element_class(self, Y, {}).to_highest_weight() + hw._A = {} + return hw.f_string(reversed(path)) + elif Y is None or Y == 0: + # The Y == 0 check is because the parent's __call__ has that + # as the first default value + ct = self.cartan_type() + cm = ct.cartan_matrix() + I = self.index_set() + shift = 0 + if ct.is_finite(): + shift = 1 + Y = {} + for k,v in A.iteritems(): + Y[k] = Y.get(k, 0) + v + Y[(k[0],k[1]+1)] = Y.get((k[0],k[1]+1), 0) + v + for j_index,j in enumerate(I): + if k[0] == j: + continue + c = self._c[j_index,k[0]-shift] + if cm[j_index,k[0]-shift] != 0: + Y[(j,k[1]+c)] = Y.get((j,k[1]+c), 0) + v*cm[j_index,k[0]-shift] + for k in Y.keys(): + if Y[k] == 0: + del Y[k] + return self.element_class(self, Y, A) def _repr_(self): r""" @@ -1029,13 +1027,16 @@ def weight_lattice_realization(self): return F.weight_lattice(extended=True) return F.weight_lattice() -class CrystalOfNakajimaMonomialsElement(NakajimaYMonomial): + Element = NakajimaMonomial + global_options = NakajimaMonomialGlobalOptions + +class CrystalOfNakajimaMonomialsElement(NakajimaMonomial): r""" Element class for :class:`~sage.combinat.crystals.monomial_crystals.CrystalOfNakajimaMonomials`. The `f_i` operators need to be modified from the version in - :class:`~sage.combinat.crystals.monomial_crystalsNakajimaYMonomial` + :class:`~sage.combinat.crystals.monomial_crystalsNakajimaMonomial` in order to create irreducible highest weight realizations. This modified `f_i` is defined as @@ -1067,6 +1068,24 @@ def f(self, i): sage: m = M.module_generators[0] sage: [m.f(i) for i in M.index_set()] [Y(0,0)^2 Y(0,1)^-1 Y(2,0), None, None, None] + + :: + + sage: M = crystals.infinity.NakajimaMonomials("E8") + sage: M.global_options(monomial='A') + sage: m = M.module_generators[0].f_string([4,2,3,8]) + sage: m + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1 + sage: [m.f(i) for i in M.index_set()] + [A(1,2)^-1 A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1, + A(2,0)^-1 A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,0)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(4,1)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(5,0)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(6,0)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(7,1)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-2] + sage: M.global_options.reset() """ if self.phi(i) == 0: return None @@ -1211,14 +1230,11 @@ def __init__(self, ct, La, c): cat = ClassicalCrystals() else: cat = (RegularCrystals(), HighestWeightCrystals(), InfiniteEnumeratedSets()) - InfinityCrystalOfNakajimaMonomials.__init__(self, ct, c, - CrystalOfNakajimaMonomialsElement, cat) + InfinityCrystalOfNakajimaMonomials.__init__(self, ct, c, cat) self._cartan_type = ct self.hw = La - gen = {} - for j in range(len(La.support())): - gen[(La.support()[j],0)] = La.coefficients()[j] - self.module_generators = (self.element_class(self,gen),) + gen = {(i,0): c for i,c in La} + self.module_generators = (self.element_class(self, gen, {}),) def _repr_(self): r""" @@ -1253,3 +1269,5 @@ def cardinality(self): return Infinity return super(InfinityCrystalOfNakajimaMonomials, self).cardinality() + Element = CrystalOfNakajimaMonomialsElement + From 12dbbd7e433743d27324eb91bad13647abc5f255 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Mon, 19 Oct 2015 18:00:36 +0200 Subject: [PATCH 058/163] dispersion sets of polynomials --- .../rings/polynomial/polynomial_element.pyx | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index cf365a71f2e..3c8857776c3 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -5266,6 +5266,90 @@ cdef class Polynomial(CommutativeAlgebraElement): return slopes + def dispersion_set(self, other=None): + r""" + Compute the dispersion set of two polynomials. + + The dispersion set of `p` and `q` is the set of nonnegative integers + `n` such that `f(x + n)` and `g(x)` have a nonconstant common factor. + + When ``other`` is ``None``, compute the auto-dispersion set of + ``self``, i.e., its dispersion set with itself. + + ALGORITHM: + + See Section 4 of Man & Wright [ManWright1994]_. + + .. [ManWright1994] Yiu-Kwong Man and Francis J. Wright. + *Fast Polynomial Dispersion Computation and its Application to + Indefinite Summation*. ISSAC 1994. + + .. SEEALSO:: :meth:`dispersion` + + EXAMPLES:: + + sage: Pol. = QQ[] + sage: x.dispersion_set(x + 1) + [1] + sage: (x + 1).dispersion_set(x) + [] + + sage: pol = x^3 + x - 7 + sage: (pol*pol(x+3)^2).dispersion_set() + [0, 3] + """ + other = self if other is None else self._parent.coerce(other) + x = self._parent.gen() + dispersions = set() + for p, _ in self.factor(): + # need both due to the semantics of is_primitive() over fields + assert p.is_monic() or p.is_primitive() + for q, _ in other.factor(): + m, n = p.degree(), q.degree() + assert q.is_monic() or q.is_primitive() + if m != n or p[n] != q[n]: + continue + alpha = (q[n-1] - p[n-1])/(n*p[n]) + if alpha.is_integer(): # ZZ() might work for non-integers... + alpha = ZZ(alpha) + else: + continue + if alpha < 0 or alpha in dispersions: + continue + if n >= 1 and p(x + alpha) != q: + continue + dispersions.add(alpha) + return list(dispersions) + + def dispersion(self, other=None): + r""" + Compute the dispersion of a pair of polynomials. + + The dispersion of `p` and `q` is the largest nonnegative integer `n` + such that `f(x + n)` and `g(x)` have a nonconstant common factor. + + When ``other`` is ``None``, compute the auto-dispersion of ``self``, + i.e., its dispersion with itself. + + .. SEEALSO:: :meth:`dispersion_set` + + EXAMPLES:: + + sage: Pol. = QQ[] + sage: x.dispersion(x + 1) + 1 + sage: (x + 1).dispersion(x) + -Infinity + + sage: Pol. = QQbar[] + sage: pol = Pol([sqrt(5), 1, 3/2]) + sage: pol.dispersion() + 0 + sage: (pol*pol(x+3)).dispersion() + 3 + """ + dispersions = self.dispersion_set(other) + return max(dispersions) if len(dispersions) > 0 else infinity.minus_infinity ##################################################################### # Conversions to other systems From 161ac3ba998689b9a3a3435ed85e4316cd61fb77 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 6 Apr 2016 20:24:25 +0200 Subject: [PATCH 059/163] Split up the sagenb distribution --- build/pkgs/babel/SPKG.txt | 7 ++ build/pkgs/babel/checksums.ini | 4 + build/pkgs/babel/dependencies | 5 + build/pkgs/babel/package-version.txt | 1 + build/pkgs/babel/spkg-install | 3 + build/pkgs/babel/type | 1 + build/pkgs/flask/SPKG.txt | 8 ++ build/pkgs/flask/checksums.ini | 4 + build/pkgs/flask/dependencies | 5 + build/pkgs/flask/package-version.txt | 1 + build/pkgs/flask/spkg-install | 3 + build/pkgs/flask/type | 1 + build/pkgs/flask_autoindex/SPKG.txt | 9 ++ build/pkgs/flask_autoindex/checksums.ini | 4 + build/pkgs/flask_autoindex/dependencies | 5 + .../pkgs/flask_autoindex/package-version.txt | 1 + build/pkgs/flask_autoindex/spkg-install | 3 + build/pkgs/flask_autoindex/type | 1 + build/pkgs/flask_babel/SPKG.txt | 6 + build/pkgs/flask_babel/checksums.ini | 4 + build/pkgs/flask_babel/dependencies | 5 + build/pkgs/flask_babel/package-version.txt | 1 + build/pkgs/flask_babel/spkg-install | 3 + build/pkgs/flask_babel/type | 1 + build/pkgs/flask_oldsessions/SPKG.txt | 5 + build/pkgs/flask_oldsessions/checksums.ini | 4 + build/pkgs/flask_oldsessions/dependencies | 5 + .../flask_oldsessions/package-version.txt | 1 + build/pkgs/flask_oldsessions/spkg-install | 3 + build/pkgs/flask_oldsessions/type | 1 + build/pkgs/flask_openid/SPKG.txt | 5 + build/pkgs/flask_openid/checksums.ini | 4 + build/pkgs/flask_openid/dependencies | 5 + build/pkgs/flask_openid/package-version.txt | 1 + build/pkgs/flask_openid/spkg-install | 3 + build/pkgs/flask_openid/type | 1 + build/pkgs/flask_silk/SPKG.txt | 5 + build/pkgs/flask_silk/checksums.ini | 4 + build/pkgs/flask_silk/dependencies | 5 + build/pkgs/flask_silk/package-version.txt | 1 + build/pkgs/flask_silk/spkg-install | 3 + build/pkgs/flask_silk/type | 1 + build/pkgs/itsdangerous/SPKG.txt | 6 + build/pkgs/itsdangerous/checksums.ini | 4 + build/pkgs/itsdangerous/dependencies | 5 + build/pkgs/itsdangerous/package-version.txt | 1 + build/pkgs/itsdangerous/spkg-install | 3 + build/pkgs/itsdangerous/type | 1 + build/pkgs/matplotlib/dependencies | 5 +- build/pkgs/python_openid/SPKG.txt | 11 ++ build/pkgs/python_openid/checksums.ini | 4 + build/pkgs/python_openid/dependencies | 5 + build/pkgs/python_openid/package-version.txt | 1 + build/pkgs/python_openid/spkg-install | 3 + build/pkgs/python_openid/type | 1 + build/pkgs/pytz/SPKG.txt | 12 ++ build/pkgs/pytz/checksums.ini | 4 + build/pkgs/pytz/dependencies | 5 + build/pkgs/pytz/package-version.txt | 1 + build/pkgs/pytz/spkg-install | 3 + build/pkgs/pytz/type | 1 + build/pkgs/sagenb/SPKG.txt | 103 +++--------------- build/pkgs/sagenb/checksums.ini | 8 +- build/pkgs/sagenb/dependencies | 2 +- build/pkgs/sagenb/package-version.txt | 2 +- build/pkgs/sagenb/spkg-install | 78 +++---------- build/pkgs/speaklater/SPKG.txt | 12 ++ build/pkgs/speaklater/checksums.ini | 4 + build/pkgs/speaklater/dependencies | 5 + build/pkgs/speaklater/package-version.txt | 1 + build/pkgs/speaklater/spkg-install | 3 + build/pkgs/speaklater/type | 1 + build/pkgs/twisted/SPKG.txt | 8 ++ build/pkgs/twisted/checksums.ini | 4 + build/pkgs/twisted/dependencies | 5 + build/pkgs/twisted/package-version.txt | 1 + build/pkgs/twisted/spkg-install | 3 + build/pkgs/twisted/type | 1 + build/pkgs/werkzeug/SPKG.txt | 18 +++ build/pkgs/werkzeug/checksums.ini | 4 + build/pkgs/werkzeug/dependencies | 5 + build/pkgs/werkzeug/package-version.txt | 1 + build/pkgs/werkzeug/spkg-install | 3 + build/pkgs/werkzeug/type | 1 + build/pkgs/zope_interface/SPKG.txt | 13 +++ build/pkgs/zope_interface/checksums.ini | 4 + build/pkgs/zope_interface/dependencies | 5 + build/pkgs/zope_interface/package-version.txt | 1 + build/pkgs/zope_interface/spkg-install | 3 + build/pkgs/zope_interface/type | 1 + 90 files changed, 355 insertions(+), 164 deletions(-) create mode 100644 build/pkgs/babel/SPKG.txt create mode 100644 build/pkgs/babel/checksums.ini create mode 100644 build/pkgs/babel/dependencies create mode 100644 build/pkgs/babel/package-version.txt create mode 100755 build/pkgs/babel/spkg-install create mode 100644 build/pkgs/babel/type create mode 100644 build/pkgs/flask/SPKG.txt create mode 100644 build/pkgs/flask/checksums.ini create mode 100644 build/pkgs/flask/dependencies create mode 100644 build/pkgs/flask/package-version.txt create mode 100755 build/pkgs/flask/spkg-install create mode 100644 build/pkgs/flask/type create mode 100644 build/pkgs/flask_autoindex/SPKG.txt create mode 100644 build/pkgs/flask_autoindex/checksums.ini create mode 100644 build/pkgs/flask_autoindex/dependencies create mode 100644 build/pkgs/flask_autoindex/package-version.txt create mode 100755 build/pkgs/flask_autoindex/spkg-install create mode 100644 build/pkgs/flask_autoindex/type create mode 100644 build/pkgs/flask_babel/SPKG.txt create mode 100644 build/pkgs/flask_babel/checksums.ini create mode 100644 build/pkgs/flask_babel/dependencies create mode 100644 build/pkgs/flask_babel/package-version.txt create mode 100755 build/pkgs/flask_babel/spkg-install create mode 100644 build/pkgs/flask_babel/type create mode 100644 build/pkgs/flask_oldsessions/SPKG.txt create mode 100644 build/pkgs/flask_oldsessions/checksums.ini create mode 100644 build/pkgs/flask_oldsessions/dependencies create mode 100644 build/pkgs/flask_oldsessions/package-version.txt create mode 100755 build/pkgs/flask_oldsessions/spkg-install create mode 100644 build/pkgs/flask_oldsessions/type create mode 100644 build/pkgs/flask_openid/SPKG.txt create mode 100644 build/pkgs/flask_openid/checksums.ini create mode 100644 build/pkgs/flask_openid/dependencies create mode 100644 build/pkgs/flask_openid/package-version.txt create mode 100755 build/pkgs/flask_openid/spkg-install create mode 100644 build/pkgs/flask_openid/type create mode 100644 build/pkgs/flask_silk/SPKG.txt create mode 100644 build/pkgs/flask_silk/checksums.ini create mode 100644 build/pkgs/flask_silk/dependencies create mode 100644 build/pkgs/flask_silk/package-version.txt create mode 100755 build/pkgs/flask_silk/spkg-install create mode 100644 build/pkgs/flask_silk/type create mode 100644 build/pkgs/itsdangerous/SPKG.txt create mode 100644 build/pkgs/itsdangerous/checksums.ini create mode 100644 build/pkgs/itsdangerous/dependencies create mode 100644 build/pkgs/itsdangerous/package-version.txt create mode 100755 build/pkgs/itsdangerous/spkg-install create mode 100644 build/pkgs/itsdangerous/type create mode 100644 build/pkgs/python_openid/SPKG.txt create mode 100644 build/pkgs/python_openid/checksums.ini create mode 100644 build/pkgs/python_openid/dependencies create mode 100644 build/pkgs/python_openid/package-version.txt create mode 100755 build/pkgs/python_openid/spkg-install create mode 100644 build/pkgs/python_openid/type create mode 100644 build/pkgs/pytz/SPKG.txt create mode 100644 build/pkgs/pytz/checksums.ini create mode 100644 build/pkgs/pytz/dependencies create mode 100644 build/pkgs/pytz/package-version.txt create mode 100755 build/pkgs/pytz/spkg-install create mode 100644 build/pkgs/pytz/type create mode 100644 build/pkgs/speaklater/SPKG.txt create mode 100644 build/pkgs/speaklater/checksums.ini create mode 100644 build/pkgs/speaklater/dependencies create mode 100644 build/pkgs/speaklater/package-version.txt create mode 100755 build/pkgs/speaklater/spkg-install create mode 100644 build/pkgs/speaklater/type create mode 100644 build/pkgs/twisted/SPKG.txt create mode 100644 build/pkgs/twisted/checksums.ini create mode 100644 build/pkgs/twisted/dependencies create mode 100644 build/pkgs/twisted/package-version.txt create mode 100755 build/pkgs/twisted/spkg-install create mode 100644 build/pkgs/twisted/type create mode 100644 build/pkgs/werkzeug/SPKG.txt create mode 100644 build/pkgs/werkzeug/checksums.ini create mode 100644 build/pkgs/werkzeug/dependencies create mode 100644 build/pkgs/werkzeug/package-version.txt create mode 100755 build/pkgs/werkzeug/spkg-install create mode 100644 build/pkgs/werkzeug/type create mode 100644 build/pkgs/zope_interface/SPKG.txt create mode 100644 build/pkgs/zope_interface/checksums.ini create mode 100644 build/pkgs/zope_interface/dependencies create mode 100644 build/pkgs/zope_interface/package-version.txt create mode 100755 build/pkgs/zope_interface/spkg-install create mode 100644 build/pkgs/zope_interface/type diff --git a/build/pkgs/babel/SPKG.txt b/build/pkgs/babel/SPKG.txt new file mode 100644 index 00000000000..ae0451d58be --- /dev/null +++ b/build/pkgs/babel/SPKG.txt @@ -0,0 +1,7 @@ += babel = + +== Description == + +Internationalization utilities + +A collection of tools for internationalizing Python applications. diff --git a/build/pkgs/babel/checksums.ini b/build/pkgs/babel/checksums.ini new file mode 100644 index 00000000000..adfcefcf9db --- /dev/null +++ b/build/pkgs/babel/checksums.ini @@ -0,0 +1,4 @@ +tarball=Babel-VERSION.tar.gz +sha1=e02392bc9a16f7672686bce23e4e3cdadcc1b1c8 +md5=1b69e4b2ab3795119266ccaa36b36f15 +cksum=2897183559 diff --git a/build/pkgs/babel/dependencies b/build/pkgs/babel/dependencies new file mode 100644 index 00000000000..247fe4de4d9 --- /dev/null +++ b/build/pkgs/babel/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip pytz + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/babel/package-version.txt b/build/pkgs/babel/package-version.txt new file mode 100644 index 00000000000..ccbccc3dc62 --- /dev/null +++ b/build/pkgs/babel/package-version.txt @@ -0,0 +1 @@ +2.2.0 diff --git a/build/pkgs/babel/spkg-install b/build/pkgs/babel/spkg-install new file mode 100755 index 00000000000..b28ab85c90c --- /dev/null +++ b/build/pkgs/babel/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && pip install . diff --git a/build/pkgs/babel/type b/build/pkgs/babel/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/babel/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/flask/SPKG.txt b/build/pkgs/flask/SPKG.txt new file mode 100644 index 00000000000..cf46f1da69e --- /dev/null +++ b/build/pkgs/flask/SPKG.txt @@ -0,0 +1,8 @@ += Flask = + +== Description == + +A microframework based on Werkzeug, Jinja2 and good intentions + +Flask is a microframework for Python based on Werkzeug, Jinja 2 and good +intentions. And before you ask: It’s BSD licensed! diff --git a/build/pkgs/flask/checksums.ini b/build/pkgs/flask/checksums.ini new file mode 100644 index 00000000000..f9167d09e4a --- /dev/null +++ b/build/pkgs/flask/checksums.ini @@ -0,0 +1,4 @@ +tarball=Flask-VERSION.tar.gz +sha1=d3d078262b053f4438e2ed3fd6f9b923c2c92172 +md5=378670fe456957eb3c27ddaef60b2b24 +cksum=2901487846 diff --git a/build/pkgs/flask/dependencies b/build/pkgs/flask/dependencies new file mode 100644 index 00000000000..4c9f9037ab5 --- /dev/null +++ b/build/pkgs/flask/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip werkzeug jinja2 itsdangerous + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask/package-version.txt b/build/pkgs/flask/package-version.txt new file mode 100644 index 00000000000..571215736a6 --- /dev/null +++ b/build/pkgs/flask/package-version.txt @@ -0,0 +1 @@ +0.10.1 diff --git a/build/pkgs/flask/spkg-install b/build/pkgs/flask/spkg-install new file mode 100755 index 00000000000..b28ab85c90c --- /dev/null +++ b/build/pkgs/flask/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && pip install . diff --git a/build/pkgs/flask/type b/build/pkgs/flask/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/flask/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/flask_autoindex/SPKG.txt b/build/pkgs/flask_autoindex/SPKG.txt new file mode 100644 index 00000000000..6ef8325067a --- /dev/null +++ b/build/pkgs/flask_autoindex/SPKG.txt @@ -0,0 +1,9 @@ += Flask-AutoIndex = + +== Description == + +The mod_autoindex for Flask + +Flask-AutoIndex generates an index page for your Flask application +automatically. The result just like mod_autoindex, but the look is more +awesome! diff --git a/build/pkgs/flask_autoindex/checksums.ini b/build/pkgs/flask_autoindex/checksums.ini new file mode 100644 index 00000000000..032dbb85894 --- /dev/null +++ b/build/pkgs/flask_autoindex/checksums.ini @@ -0,0 +1,4 @@ +tarball=Flask_AutoIndex-VERSION.tar.gz +sha1=15e08bd3a516aa327ab4af767a29154a4bebdae3 +md5=24984602365704737468bb4d2586a739 +cksum=1140907747 diff --git a/build/pkgs/flask_autoindex/dependencies b/build/pkgs/flask_autoindex/dependencies new file mode 100644 index 00000000000..3904350ca14 --- /dev/null +++ b/build/pkgs/flask_autoindex/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip flask_silk + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_autoindex/package-version.txt b/build/pkgs/flask_autoindex/package-version.txt new file mode 100644 index 00000000000..2eb3c4fe4ee --- /dev/null +++ b/build/pkgs/flask_autoindex/package-version.txt @@ -0,0 +1 @@ +0.5 diff --git a/build/pkgs/flask_autoindex/spkg-install b/build/pkgs/flask_autoindex/spkg-install new file mode 100755 index 00000000000..e24852a395d --- /dev/null +++ b/build/pkgs/flask_autoindex/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd Flask* && pip install . diff --git a/build/pkgs/flask_autoindex/type b/build/pkgs/flask_autoindex/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/flask_autoindex/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/flask_babel/SPKG.txt b/build/pkgs/flask_babel/SPKG.txt new file mode 100644 index 00000000000..f96f6411918 --- /dev/null +++ b/build/pkgs/flask_babel/SPKG.txt @@ -0,0 +1,6 @@ += Flask-Babel = + +== Description == + +Adds i18n/l10n support to Flask applications with the help of the Babel +library. diff --git a/build/pkgs/flask_babel/checksums.ini b/build/pkgs/flask_babel/checksums.ini new file mode 100644 index 00000000000..49652906818 --- /dev/null +++ b/build/pkgs/flask_babel/checksums.ini @@ -0,0 +1,4 @@ +tarball=Flask_Babel-VERSION.tar.gz +sha1=977d3b152f876e06c215f6bb72616b4ce138fa49 +md5=4762e0392303f464d53cbebedfb87ded +cksum=2286190911 diff --git a/build/pkgs/flask_babel/dependencies b/build/pkgs/flask_babel/dependencies new file mode 100644 index 00000000000..06da0f070bc --- /dev/null +++ b/build/pkgs/flask_babel/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip flask speaklater + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_babel/package-version.txt b/build/pkgs/flask_babel/package-version.txt new file mode 100644 index 00000000000..b63ba696b7a --- /dev/null +++ b/build/pkgs/flask_babel/package-version.txt @@ -0,0 +1 @@ +0.9 diff --git a/build/pkgs/flask_babel/spkg-install b/build/pkgs/flask_babel/spkg-install new file mode 100755 index 00000000000..e24852a395d --- /dev/null +++ b/build/pkgs/flask_babel/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd Flask* && pip install . diff --git a/build/pkgs/flask_babel/type b/build/pkgs/flask_babel/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/flask_babel/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/flask_oldsessions/SPKG.txt b/build/pkgs/flask_oldsessions/SPKG.txt new file mode 100644 index 00000000000..2af9b2ebb23 --- /dev/null +++ b/build/pkgs/flask_oldsessions/SPKG.txt @@ -0,0 +1,5 @@ += Flask-OldSessions = + +== Description == + +Provides a session class that works like the one in Flask before 0.10. diff --git a/build/pkgs/flask_oldsessions/checksums.ini b/build/pkgs/flask_oldsessions/checksums.ini new file mode 100644 index 00000000000..c5fc0993ff0 --- /dev/null +++ b/build/pkgs/flask_oldsessions/checksums.ini @@ -0,0 +1,4 @@ +tarball=Flask_OldSessions-VERSION.tar.gz +sha1=1c0bbcd79b4fc626da2fd34ce9b59b2d43f6d81e +md5=3d731d343d5380bb9f502742ad62df50 +cksum=709943870 diff --git a/build/pkgs/flask_oldsessions/dependencies b/build/pkgs/flask_oldsessions/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/flask_oldsessions/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_oldsessions/package-version.txt b/build/pkgs/flask_oldsessions/package-version.txt new file mode 100644 index 00000000000..68c123cf10e --- /dev/null +++ b/build/pkgs/flask_oldsessions/package-version.txt @@ -0,0 +1 @@ +0.10 diff --git a/build/pkgs/flask_oldsessions/spkg-install b/build/pkgs/flask_oldsessions/spkg-install new file mode 100755 index 00000000000..980f25cb577 --- /dev/null +++ b/build/pkgs/flask_oldsessions/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd *flask-oldsessions* && pip install . diff --git a/build/pkgs/flask_oldsessions/type b/build/pkgs/flask_oldsessions/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/flask_oldsessions/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/flask_openid/SPKG.txt b/build/pkgs/flask_openid/SPKG.txt new file mode 100644 index 00000000000..77aa5b1c152 --- /dev/null +++ b/build/pkgs/flask_openid/SPKG.txt @@ -0,0 +1,5 @@ += Flask-OpenID = + +== Description == + +OpenID support for Flask diff --git a/build/pkgs/flask_openid/checksums.ini b/build/pkgs/flask_openid/checksums.ini new file mode 100644 index 00000000000..a17be5fa13a --- /dev/null +++ b/build/pkgs/flask_openid/checksums.ini @@ -0,0 +1,4 @@ +tarball=Flask_OpenID-VERSION.tar.gz +sha1=18d39e03417cd2b577cd5c1f5c2ac117493f3fef +md5=a40c63df701ec634450d03490ddfb6c1 +cksum=995771756 diff --git a/build/pkgs/flask_openid/dependencies b/build/pkgs/flask_openid/dependencies new file mode 100644 index 00000000000..5367d7a02a6 --- /dev/null +++ b/build/pkgs/flask_openid/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip flask python_openid + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_openid/package-version.txt b/build/pkgs/flask_openid/package-version.txt new file mode 100644 index 00000000000..c813fe116c9 --- /dev/null +++ b/build/pkgs/flask_openid/package-version.txt @@ -0,0 +1 @@ +1.2.5 diff --git a/build/pkgs/flask_openid/spkg-install b/build/pkgs/flask_openid/spkg-install new file mode 100755 index 00000000000..e24852a395d --- /dev/null +++ b/build/pkgs/flask_openid/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd Flask* && pip install . diff --git a/build/pkgs/flask_openid/type b/build/pkgs/flask_openid/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/flask_openid/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/flask_silk/SPKG.txt b/build/pkgs/flask_silk/SPKG.txt new file mode 100644 index 00000000000..92d3009a886 --- /dev/null +++ b/build/pkgs/flask_silk/SPKG.txt @@ -0,0 +1,5 @@ += Flask-Silk = + +== Description == + +Adds silk icons to your Flask application or blueprint, or extension. diff --git a/build/pkgs/flask_silk/checksums.ini b/build/pkgs/flask_silk/checksums.ini new file mode 100644 index 00000000000..a54c85cdfd6 --- /dev/null +++ b/build/pkgs/flask_silk/checksums.ini @@ -0,0 +1,4 @@ +tarball=Flask_Silk-VERSION.tar.gz +sha1=cef42b469c9ebb69a766d0cd33ad27480800d518 +md5=aca545a94063dc4acd21779ea5dde330 +cksum=1557295080 diff --git a/build/pkgs/flask_silk/dependencies b/build/pkgs/flask_silk/dependencies new file mode 100644 index 00000000000..05fe89830e0 --- /dev/null +++ b/build/pkgs/flask_silk/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip flask + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_silk/package-version.txt b/build/pkgs/flask_silk/package-version.txt new file mode 100644 index 00000000000..3b04cfb60da --- /dev/null +++ b/build/pkgs/flask_silk/package-version.txt @@ -0,0 +1 @@ +0.2 diff --git a/build/pkgs/flask_silk/spkg-install b/build/pkgs/flask_silk/spkg-install new file mode 100755 index 00000000000..e24852a395d --- /dev/null +++ b/build/pkgs/flask_silk/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd Flask* && pip install . diff --git a/build/pkgs/flask_silk/type b/build/pkgs/flask_silk/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/flask_silk/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/itsdangerous/SPKG.txt b/build/pkgs/itsdangerous/SPKG.txt new file mode 100644 index 00000000000..32264811471 --- /dev/null +++ b/build/pkgs/itsdangerous/SPKG.txt @@ -0,0 +1,6 @@ += itsdangerous = + +== Description == + +Various helpers to pass data to untrusted environments and to get it back +safe and sound. diff --git a/build/pkgs/itsdangerous/checksums.ini b/build/pkgs/itsdangerous/checksums.ini new file mode 100644 index 00000000000..f34dca2926d --- /dev/null +++ b/build/pkgs/itsdangerous/checksums.ini @@ -0,0 +1,4 @@ +tarball=itsdangerous-VERSION.tar.gz +sha1=0a6ae9c20cd72e89d75314ebc7b0f390f93e6a0d +md5=a3d55aa79369aef5345c036a8a26307f +cksum=2767256127 diff --git a/build/pkgs/itsdangerous/dependencies b/build/pkgs/itsdangerous/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/itsdangerous/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/itsdangerous/package-version.txt b/build/pkgs/itsdangerous/package-version.txt new file mode 100644 index 00000000000..fd137eb17eb --- /dev/null +++ b/build/pkgs/itsdangerous/package-version.txt @@ -0,0 +1 @@ +0.24 diff --git a/build/pkgs/itsdangerous/spkg-install b/build/pkgs/itsdangerous/spkg-install new file mode 100755 index 00000000000..b28ab85c90c --- /dev/null +++ b/build/pkgs/itsdangerous/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && pip install . diff --git a/build/pkgs/itsdangerous/type b/build/pkgs/itsdangerous/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/itsdangerous/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/matplotlib/dependencies b/build/pkgs/matplotlib/dependencies index 7ea96bd9de2..42b838e8213 100644 --- a/build/pkgs/matplotlib/dependencies +++ b/build/pkgs/matplotlib/dependencies @@ -1,7 +1,4 @@ -$(PYTHON) numpy freetype libpng dateutil pyparsing tornado six cycler | sagenb setuptools - -The sagenb dependency is because of the "pytz" package. If that package -is ever pulled out from sagenb, replace "sagenb" by "pytz" above. +$(PYTHON) numpy freetype libpng dateutil pyparsing tornado six cycler | pytz setuptools ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/python_openid/SPKG.txt b/build/pkgs/python_openid/SPKG.txt new file mode 100644 index 00000000000..390a18695b1 --- /dev/null +++ b/build/pkgs/python_openid/SPKG.txt @@ -0,0 +1,11 @@ += python-openid = + +== Description == + +OpenID support for servers and consumers. + +This is a set of Python packages to support use of the OpenID decentralized +identity system in your application. Want to enable single sign-on for your +web site? Use the openid.consumer package. Want to run your own OpenID server? +Check out openid.server. Includes example code and support for a variety of +storage back-ends. diff --git a/build/pkgs/python_openid/checksums.ini b/build/pkgs/python_openid/checksums.ini new file mode 100644 index 00000000000..6e35600da54 --- /dev/null +++ b/build/pkgs/python_openid/checksums.ini @@ -0,0 +1,4 @@ +tarball=python_openid-VERSION.tar.gz +sha1=e1a8e9502abf45e22dbc5f5ef76e56e139ea1b3d +md5=393f48b162ec29c3de9e2973548ea50d +cksum=2866118713 diff --git a/build/pkgs/python_openid/dependencies b/build/pkgs/python_openid/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/python_openid/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/python_openid/package-version.txt b/build/pkgs/python_openid/package-version.txt new file mode 100644 index 00000000000..21bb5e156fb --- /dev/null +++ b/build/pkgs/python_openid/package-version.txt @@ -0,0 +1 @@ +2.2.5 diff --git a/build/pkgs/python_openid/spkg-install b/build/pkgs/python_openid/spkg-install new file mode 100755 index 00000000000..fd983004d0f --- /dev/null +++ b/build/pkgs/python_openid/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd python-openid-* && pip install . diff --git a/build/pkgs/python_openid/type b/build/pkgs/python_openid/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/python_openid/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/pytz/SPKG.txt b/build/pkgs/pytz/SPKG.txt new file mode 100644 index 00000000000..94190513f91 --- /dev/null +++ b/build/pkgs/pytz/SPKG.txt @@ -0,0 +1,12 @@ += pytz = + +== Description == + +World Timezone Definitions for Python + +== Special Update/Build Instructions == + +The upstream tarball was repackaged after sanitizing the file +permissions with + +$ chmod go-w diff --git a/build/pkgs/pytz/checksums.ini b/build/pkgs/pytz/checksums.ini new file mode 100644 index 00000000000..5cdbb099c98 --- /dev/null +++ b/build/pkgs/pytz/checksums.ini @@ -0,0 +1,4 @@ +tarball=pytz-VERSION.tar.bz2 +sha1=5859c52c8a01da679c0047c3e463180d7d0de4bf +md5=bdad4eee126bfa2f1f6ad5aaf4e22ee0 +cksum=2104846726 diff --git a/build/pkgs/pytz/dependencies b/build/pkgs/pytz/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/pytz/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/pytz/package-version.txt b/build/pkgs/pytz/package-version.txt new file mode 100644 index 00000000000..648f66d05bd --- /dev/null +++ b/build/pkgs/pytz/package-version.txt @@ -0,0 +1 @@ +2016.3 diff --git a/build/pkgs/pytz/spkg-install b/build/pkgs/pytz/spkg-install new file mode 100755 index 00000000000..b28ab85c90c --- /dev/null +++ b/build/pkgs/pytz/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && pip install . diff --git a/build/pkgs/pytz/type b/build/pkgs/pytz/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/pytz/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/sagenb/SPKG.txt b/build/pkgs/sagenb/SPKG.txt index 83cf729472b..92ab236a0e8 100644 --- a/build/pkgs/sagenb/SPKG.txt +++ b/build/pkgs/sagenb/SPKG.txt @@ -12,104 +12,29 @@ GPLv3+ == Upstream Contact == * Keshav Kini - * Homepage: http://nb.sagemath.org/ + * Homepage: https://github.com/sagemath/sagenb == Dependencies == -Included dependencies (for specific version numbers, see the file -util/fetch_deps.py in the sagenb directory): +Build-time dependencies: - * zope.interface + * Python + * setuptools * twisted - * pytz - * Babel - * Werkzeug - * speaklater - * python-openid - * itsdangerous - * Flask - * Flask-Silk - * Flask-AutoIndex - * Flask-Babel - * Flask-OpenID - * pyOpenSSL - * webassets + * flask + - flask-autoindex + - flask-babel + - flask-openid + - flask-oldsessions -Dependencies that are not included in this SPKG but are packaged for -Sage: +Run-time dependencies: - * Python - * setuptools - * pexpect + * Sage * jinja2 - * sphinx + * pexpect * docutils + * sphinx -Dependencies that are not included in this SPKG, and are not packaged -for Sage by default for licensing reasons: +Optional dependency: * OpenSSL (including headers) - -For this last category, please install systemwide using your operating -system's package manager, or manually install the OpenSSL optional SPKG -before attempting to install this sagenb SPKG (see the Installation -section of the Sage documentation for more information). - -== Packaging Instructions == - -To produce a new spkg: - - * Go to your sagenb repository - * Check out the version of sagenb you want to package - * Run dist.sh, optionally with the switch -g to include a copy of the - git repo - * Replace the src/ directory in this SPKG with the newly created dist/ - directory that has now appeared in your sagenb repository - * `cd .. && sage --pkg --no-compress sagenb-x.y.z` - * A winner is you! - -== Changelog == - -=== sagenb-0.10.7.2 (Punarbasu Purkayastha, 2013-08-08) === - - * Upgraded to 0.10.7.2 (minor fix for trac ticket 14469) - -=== sagenb-0.10.7.1 (Keshav Kini, 2013-06-25) === - - * Upgraded to 0.10.7.1 (revert published worksheet sanitization) - -=== sagenb-0.10.7 (Keshav Kini, 2013-06-22) === - - * Upgraded to 0.10.7 (MathJax 2.2, GPLv3+, bugfixes) - -=== sagenb-0.10.6 (Keshav Kini, 2013-04-27) === - - * Upgraded to 0.10.6 (bugfixes) - -=== sagenb-0.10.5 (Keshav Kini, 2013-04-01) === - - * Upgraded to 0.10.5 (bugfixes, LDAP) - -=== sagenb-0.10.4 (Keshav Kini, 2013-01-16) === - - * Upgraded to 0.10.4 (bugfixes, #13504, #13678) - -=== sagenb-0.10.3 (Jason Grout, 2012-11-16) === - - * Upgraded to 0.10.3 (bugfixes) - * Include zope.interface tarball - -=== sagenb-0.10.2 (Keshav Kini, 2012-09-01) === - - * Upgraded to 0.10.2 (bugfixes) - -=== sagenb-0.10.1 (Keshav Kini, 2012-07-08) === - - * Upgraded to 0.10.1 (bugfixes) - -=== sagenb-0.10.0 (Keshav Kini, 2012-06-21) === - - * Upgraded to 0.10.0 - -For an full log of changes, look at the history on github: -http://github.com/sagemath/sagenb diff --git a/build/pkgs/sagenb/checksums.ini b/build/pkgs/sagenb/checksums.ini index 5f53c7fc1dd..9c0d2484fc1 100644 --- a/build/pkgs/sagenb/checksums.ini +++ b/build/pkgs/sagenb/checksums.ini @@ -1,4 +1,4 @@ -tarball=sagenb-VERSION.tar -sha1=8e15129e2014d8ba9a06630ea4269251f2daac61 -md5=c0975aa7af1a0b9d7d36d3ddae95d043 -cksum=616807847 +tarball=sagenb-VERSION.tar.bz2 +sha1=7bde7c50ed46ef75b4f8c107e29aba8aef6711e1 +md5=f421c5c4d347226534ca0ca95ac726e2 +cksum=1196088822 diff --git a/build/pkgs/sagenb/dependencies b/build/pkgs/sagenb/dependencies index 7f09065715b..d5481b6b4e8 100644 --- a/build/pkgs/sagenb/dependencies +++ b/build/pkgs/sagenb/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) setuptools pexpect jinja2 sphinx docutils +$(PYTHON) | pip babel flask flask_autoindex flask_babel flask_oldsessions flask_openid mathjax twisted ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/sagenb/package-version.txt b/build/pkgs/sagenb/package-version.txt index b80f98e66f1..c43e1055fd3 100644 --- a/build/pkgs/sagenb/package-version.txt +++ b/build/pkgs/sagenb/package-version.txt @@ -1 +1 @@ -0.11.7 +0.12 diff --git a/build/pkgs/sagenb/spkg-install b/build/pkgs/sagenb/spkg-install index 769ce70a2df..08fa12dddc3 100755 --- a/build/pkgs/sagenb/spkg-install +++ b/build/pkgs/sagenb/spkg-install @@ -1,79 +1,27 @@ #!/usr/bin/env bash -# This file is no longer autogenerated. -# don't build on python 3 -if [ $(python --version 2>&1 | cut -d' ' -f2 | cut -d'.' -f1) = "3" ]; then - exit -fi - - -# Begin boilerplate - -CUR=$PWD - -die () { - echo >&2 "$@" +if [ -z "$SAGE_LOCAL" ]; then + echo >&2 "Error: SAGE_LOCAL undefined - exiting..." + echo >&2 "Maybe run 'sage -sh'?" exit 1 -} - -[ -n "$SAGE_LOCAL" ] || die 'Error: $SAGE_LOCAL not set. Source sage-env or run this script from `sage -sh`.' - -[ -z "$CPATH" ] || CPATH="$CPATH": -[ -z "$LIBRARY_PATH" ] || LIBRARY_PATH="$LIBRARY_PATH": -export CPATH="$CPATH""$SAGE_LOCAL"/include -export LIBRARY_PATH="$LIBRARY_PATH""$SAGE_LOCAL"/lib - -# note: the -D_XOPEN_SOURCE=500 is to fix build errors in Twisted 12.1 on -# Solaris. See http://trac.sagemath.org/sage_trac/ticket/11080#comment:314 and -# the subsequent few comments. -export CPPFLAGS="-I$SAGE_LOCAL/include -D_XOPEN_SOURCE=500 $CPPFLAGS" - -if [ $SAGE64 = "yes" ]; then - echo "Building with extra 64-bit flags for MacOS X and Open Solaris." - if [ -z $CFLAG64 ]; then - CFLAG64=-m64 - fi - export CFLAGS="$CFLAGS $CFLAG64" - export CPPFLAGS="$CPPFLAGS $CFLAG64" - export CXXFLAGS="$CXXFLAGS $CFLAG64" - export LDFLAGS="$LDFLAGS $CFLAG64" fi -# End boilerplate - -# delete old sagenb install, if any -rm -rf "${SAGE_LOCAL}/lib/python/site-packages"/sagenb* - - -# TODO: clean up this crap -# * dependencies should not be part of the sagenb tarball at all -# * get rid of easy_install -# * note that pip currently depends on ssl -# Install dependencies -for PKG in $(cat src/install_order); do - easy_install -Z -H None "src/$PKG" - if [ $? -ne 0 ]; then - echo >&2 "Error: Installing $PKG failed." - exit 1 - fi -done +cd src - -# Install sagenb into site-packages -PKG=$(ls -1 src | GREP_OPTIONS= grep sagenb-) -easy_install -Z -H None "src/$PKG" +pip install . if [ $? -ne 0 ]; then - echo >&2 "Error: Installing SageNB failed." + echo >&2 "Error installing SageNB" exit 1 fi - -# let sagenb use mathjax spkg -cd "${SAGE_LOCAL}/lib/python/site-packages"/sagenb-*.egg/sagenb/data +# let sagenb use mathjax +cd "${SAGE_LOCAL}/lib/python/site-packages/sagenb/data" if [ $? -ne 0 ]; then echo >&2 "Error: Cannot find SageNB data directory." exit 1 fi -# the following line can be removed once sagenb does not ship mathjax anymore. -rm -rf mathjax -ln -s ../../../../../../share/mathjax/ mathjax +ln -s ../../../../../share/mathjax/ mathjax +if [ ! -d mathjax ]; then + echo >&2 "Error: Cannot symlink mathjax into the SageNB data directory." + exit 1 +fi diff --git a/build/pkgs/speaklater/SPKG.txt b/build/pkgs/speaklater/SPKG.txt new file mode 100644 index 00000000000..3143efa59f3 --- /dev/null +++ b/build/pkgs/speaklater/SPKG.txt @@ -0,0 +1,12 @@ += speaklater = + +== Description == + +Implements a lazy string for python useful for use with gettext + +A module that provides lazy strings for translations. Basically you get an +object that appears to be a string but changes the value every time the value +is evaluated based on a callable you provide. + +For example you can have a global lazy_gettext function that returns a lazy +string with the value of the current set language. diff --git a/build/pkgs/speaklater/checksums.ini b/build/pkgs/speaklater/checksums.ini new file mode 100644 index 00000000000..7b88b7ec37e --- /dev/null +++ b/build/pkgs/speaklater/checksums.ini @@ -0,0 +1,4 @@ +tarball=speaklater-VERSION.tar.gz +sha1=65551c6896de20e58dbae795392b7e551ccfe318 +md5=e8d5dbe36e53d5a35cff227e795e8bbf +cksum=75663587 diff --git a/build/pkgs/speaklater/dependencies b/build/pkgs/speaklater/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/speaklater/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/speaklater/package-version.txt b/build/pkgs/speaklater/package-version.txt new file mode 100644 index 00000000000..7e32cd56983 --- /dev/null +++ b/build/pkgs/speaklater/package-version.txt @@ -0,0 +1 @@ +1.3 diff --git a/build/pkgs/speaklater/spkg-install b/build/pkgs/speaklater/spkg-install new file mode 100755 index 00000000000..b28ab85c90c --- /dev/null +++ b/build/pkgs/speaklater/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && pip install . diff --git a/build/pkgs/speaklater/type b/build/pkgs/speaklater/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/speaklater/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/twisted/SPKG.txt b/build/pkgs/twisted/SPKG.txt new file mode 100644 index 00000000000..d2122e44f7e --- /dev/null +++ b/build/pkgs/twisted/SPKG.txt @@ -0,0 +1,8 @@ += twisted = + +== Description == + +An asynchronous networking framework written in Python + +An extensible framework for Python programming, with special focus on +event-based network programming and multiprotocol integration. diff --git a/build/pkgs/twisted/checksums.ini b/build/pkgs/twisted/checksums.ini new file mode 100644 index 00000000000..58cda2684f5 --- /dev/null +++ b/build/pkgs/twisted/checksums.ini @@ -0,0 +1,4 @@ +tarball=Twisted-VERSION.tar.bz2 +sha1=c7db4b949fc27794ca94677f66082f49be43f283 +md5=0831d7c90d0020062de0f7287530a285 +cksum=3874291662 diff --git a/build/pkgs/twisted/dependencies b/build/pkgs/twisted/dependencies new file mode 100644 index 00000000000..7d60db4e7a0 --- /dev/null +++ b/build/pkgs/twisted/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | setuptools pip zope_interface + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/twisted/package-version.txt b/build/pkgs/twisted/package-version.txt new file mode 100644 index 00000000000..188dd74f5f5 --- /dev/null +++ b/build/pkgs/twisted/package-version.txt @@ -0,0 +1 @@ +15.5.0 diff --git a/build/pkgs/twisted/spkg-install b/build/pkgs/twisted/spkg-install new file mode 100755 index 00000000000..b28ab85c90c --- /dev/null +++ b/build/pkgs/twisted/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && pip install . diff --git a/build/pkgs/twisted/type b/build/pkgs/twisted/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/twisted/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/werkzeug/SPKG.txt b/build/pkgs/werkzeug/SPKG.txt new file mode 100644 index 00000000000..fac6f309dee --- /dev/null +++ b/build/pkgs/werkzeug/SPKG.txt @@ -0,0 +1,18 @@ += Werkzeug = + +== Description == + +The Swiss Army knife of Python web development + +Werkzeug started as simple collection of various utilities for WSGI +applications and has become one of the most advanced WSGI utility modules. It +includes a powerful debugger, full featured request and response objects, HTTP +utilities to handle entity tags, cache control headers, HTTP dates, cookie +handling, file uploads, a powerful URL routing system and a bunch of community +contributed addon modules. + +Werkzeug is unicode aware and doesn't enforce a specific template engine, +database adapter or anything else. It doesn’t even enforce a specific way of +handling requests and leaves all that up to the developer. It's most useful +for end user applications which should work on as many server environments as +possible (such as blogs, wikis, bulletin boards, etc.). diff --git a/build/pkgs/werkzeug/checksums.ini b/build/pkgs/werkzeug/checksums.ini new file mode 100644 index 00000000000..55b68e03b80 --- /dev/null +++ b/build/pkgs/werkzeug/checksums.ini @@ -0,0 +1,4 @@ +tarball=Werkzeug-VERSION.tar.gz +sha1=9e1dca470691ae2912d51d382ff086590e3e1453 +md5=daf443a939459e12f14fd2e4658a26df +cksum=606026512 diff --git a/build/pkgs/werkzeug/dependencies b/build/pkgs/werkzeug/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/werkzeug/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/werkzeug/package-version.txt b/build/pkgs/werkzeug/package-version.txt new file mode 100644 index 00000000000..62d5dbdf3c7 --- /dev/null +++ b/build/pkgs/werkzeug/package-version.txt @@ -0,0 +1 @@ +0.11.5 diff --git a/build/pkgs/werkzeug/spkg-install b/build/pkgs/werkzeug/spkg-install new file mode 100755 index 00000000000..b28ab85c90c --- /dev/null +++ b/build/pkgs/werkzeug/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && pip install . diff --git a/build/pkgs/werkzeug/type b/build/pkgs/werkzeug/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/werkzeug/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/zope_interface/SPKG.txt b/build/pkgs/zope_interface/SPKG.txt new file mode 100644 index 00000000000..ca529666746 --- /dev/null +++ b/build/pkgs/zope_interface/SPKG.txt @@ -0,0 +1,13 @@ += zope.interface = + +== Description == + +This package is intended to be independently reusable in any Python project. +It is maintained by the Zope Toolkit project. + +This package provides an implementation of "object interfaces" for Python. +Interfaces are a mechanism for labeling objects as conforming to a given API +or contract. So, this package can be considered as implementation of the +Design By Contract methodology support in Python. + +For detailed documentation, please see http://docs.zope.org/zope.interface diff --git a/build/pkgs/zope_interface/checksums.ini b/build/pkgs/zope_interface/checksums.ini new file mode 100644 index 00000000000..f552e0133dc --- /dev/null +++ b/build/pkgs/zope_interface/checksums.ini @@ -0,0 +1,4 @@ +tarball=zope.interface-VERSION.tar.gz +sha1=207161e27880d07679aff6d712ed12f55e3d91b6 +md5=9ae3d24c0c7415deb249dd1a132f0f79 +cksum=3834987228 diff --git a/build/pkgs/zope_interface/dependencies b/build/pkgs/zope_interface/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/zope_interface/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/zope_interface/package-version.txt b/build/pkgs/zope_interface/package-version.txt new file mode 100644 index 00000000000..de197cc337f --- /dev/null +++ b/build/pkgs/zope_interface/package-version.txt @@ -0,0 +1 @@ +4.1.3 diff --git a/build/pkgs/zope_interface/spkg-install b/build/pkgs/zope_interface/spkg-install new file mode 100755 index 00000000000..b28ab85c90c --- /dev/null +++ b/build/pkgs/zope_interface/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && pip install . diff --git a/build/pkgs/zope_interface/type b/build/pkgs/zope_interface/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/zope_interface/type @@ -0,0 +1 @@ +standard From 39558b6299664e10bd008e2f97bb9c64e62096b3 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 7 Apr 2016 17:11:05 -0300 Subject: [PATCH 060/163] Trac 19964: extend doctest --- src/sage/rings/complex_interval.pyx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx index 5ce0b816b0b..af4e8dbb03f 100644 --- a/src/sage/rings/complex_interval.pyx +++ b/src/sage/rings/complex_interval.pyx @@ -1127,15 +1127,15 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): TESTS: - Check that the code is valid in all regions of the complex plane:: - - sage: rats = [15423/906, 337/59976, 145151/145112] - sage: for a in rats: - ....: for b in rats: - ....: x = CIF(a, b) - ....: assert (x * (~x) - 1).contains_zero() - ....: x = -CIF(a,b) - ....: assert (x * (~x) - 1).contains_zero() + Check that the code is valid in all kind of complex intervals:: + + sage: rpts = [0, -194323/42, -110/439423, -411923/122212, \ + ....: 15423/906, 337/59976, 145151/145112] + sage: rpts = [RIF(a, b) if a <= b else RIF(b,a) \ + ....: for a in rpts for b in rpts] + sage: cpts = [CIF(a, b) for a in rpts for b in rpts if not CIF(a, b).contains_zero()] + sage: for x in cpts: + ....: assert (x * (~x) - 1).contains_zero() REFERENCES: From 8cf2e2a12bd95413c4ea44d042f40261cf10b567 Mon Sep 17 00:00:00 2001 From: Marc Masdeu Date: Fri, 8 Apr 2016 09:10:05 +0100 Subject: [PATCH 061/163] Fixed Magma interface to work with remote server. We should not assume that Sage is installed in the remote server. Also we allow the user to specify the command in remote to use to start up Magma, which allows the user to call different installed versions, for instance. --- src/sage/interfaces/magma.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index ebdd76e53cc..4ab1383b372 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -230,7 +230,7 @@ EXTCODE_DIR = None -def extcode_dir(): +def extcode_dir(iface = None): """ Return directory that contains all the Magma extcode. This is put in a writable directory owned by the user, since when attached, @@ -243,10 +243,16 @@ def extcode_dir(): """ global EXTCODE_DIR if not EXTCODE_DIR: - import shutil - tmp = sage.misc.temporary_file.tmp_dir() - shutil.copytree('%s/magma/'%SAGE_EXTCODE, tmp + '/data') - EXTCODE_DIR = "%s/data/"%tmp + if iface is None or iface._server is None: + import shutil + tmp = sage.misc.temporary_file.tmp_dir() + shutil.copytree('%s/magma/'%SAGE_EXTCODE, tmp + '/data') + EXTCODE_DIR = "%s/data/"%tmp + else: + import os + tmp = iface._remote_tmpdir() + os.system('scp -q -r %s/magma/ %s:%s/data'%(SAGE_EXTCODE,iface._server,tmp)) + EXTCODE_DIR = "%s/data/"%tmp return EXTCODE_DIR @@ -282,7 +288,7 @@ class Magma(ExtraTabCompletion, Expect): """ def __init__(self, maxread=None, script_subdirectory=None, logfile=None, server=None, server_tmpdir=None, - user_config=False, seed=None): + user_config=False, seed=None, command='magma'): """ INPUT: @@ -297,18 +303,13 @@ def __init__(self, maxread=None, script_subdirectory=None, configuration files will be read by Magma. If False (the default), then Magma is started with the -n option which suppresses user configuration files. - + - ``command`` - (Default: 'magma') The command to execute to start Magma. EXAMPLES:: sage: Magma(logfile=tmp_filename()) Magma """ - # If the -b argument is given to Magma, the opening banner and all other - # introductory messages are suppressed. The final "total time" message is - # also suppressed. - #command = 'sage-native-execute magma -b ' - command = 'sage-native-execute magma' if not user_config: command += ' -n' Expect.__init__(self, @@ -567,7 +568,7 @@ def _start(self): self.expect().expect(PROMPT) self.expect().expect(PROMPT) self.expect().expect(PROMPT) - self.attach_spec(extcode_dir() + '/spec') + self.attach_spec(extcode_dir(self) + '/spec') # set random seed self.set_seed(self._seed) @@ -1380,7 +1381,8 @@ def version(self): sage: magma.version() # random, optional - magma ((2, 14, 9), 'V2.14-9') """ - return magma_version() + t = tuple([int(n) for n in self.eval('GetVersion()').split()]) + return t, 'V%s.%s-%s'%t def help(self, s): """ @@ -2757,6 +2759,7 @@ def magma_version(): sage: magma_version() # random, optional - magma ((2, 14, 9), 'V2.14-9') """ + global magma t = tuple([int(n) for n in magma.eval('GetVersion()').split()]) return t, 'V%s.%s-%s'%t From 5fb6df4fb2bae17c681c34aac2d15a66f540f006 Mon Sep 17 00:00:00 2001 From: Marc Masdeu Date: Fri, 8 Apr 2016 12:45:24 +0100 Subject: [PATCH 062/163] Fixed spacing. --- src/sage/interfaces/magma.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index 4ab1383b372..a44e7cd0cb1 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -303,6 +303,7 @@ def __init__(self, maxread=None, script_subdirectory=None, configuration files will be read by Magma. If False (the default), then Magma is started with the -n option which suppresses user configuration files. + - ``command`` - (Default: 'magma') The command to execute to start Magma. EXAMPLES:: From ec0a724df6548f328d0947ad166c12d96392d74e Mon Sep 17 00:00:00 2001 From: Marc Masdeu Date: Fri, 8 Apr 2016 12:51:58 +0100 Subject: [PATCH 063/163] Added message to help in discovering the feature. --- src/sage/interfaces/magma.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index a44e7cd0cb1..e36547419d4 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -273,6 +273,9 @@ class Magma(ExtraTabCompletion, Expect): ``magma_free`` command instead, which uses the free demo web interface to Magma. + If you have ssh access to a remote installation of Magma, you can + also set the ``server`` parameter to use it. + EXAMPLES: You must use nvals = 0 to call a function that doesn't return From ee0536e6528ab48d3e146277a9e15bb85cbb0672 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 9 Apr 2016 15:12:29 -0500 Subject: [PATCH 064/163] Added (indexed) monoids to finitely generated monoids category. --- src/sage/monoids/indexed_free_monoid.py | 3 +++ src/sage/monoids/monoid.py | 19 +++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/sage/monoids/indexed_free_monoid.py b/src/sage/monoids/indexed_free_monoid.py index a6e3d7e84a1..274b373e9f8 100644 --- a/src/sage/monoids/indexed_free_monoid.py +++ b/src/sage/monoids/indexed_free_monoid.py @@ -25,6 +25,7 @@ from sage.categories.monoids import Monoids from sage.categories.poor_man_map import PoorManMap +from sage.categories.sets_cat import Sets from sage.rings.integer import Integer from sage.rings.infinity import infinity from sage.rings.all import ZZ @@ -688,6 +689,8 @@ def __init__(self, indices, prefix, category=None, names=None, **kwds): category = category.Finite() else: category = category.Infinite() + if indices in Sets().Finite(): + category = category.FinitelyGeneratedAsMagma() Parent.__init__(self, names=names, category=category) # ignore the optional 'key' since it only affects CachedRepresentation diff --git a/src/sage/monoids/monoid.py b/src/sage/monoids/monoid.py index f34fe2ee22f..e4d66562375 100644 --- a/src/sage/monoids/monoid.py +++ b/src/sage/monoids/monoid.py @@ -28,7 +28,7 @@ def is_Monoid(x): return isinstance(x, Monoid_class) class Monoid_class(Parent): - def __init__(self,names): + def __init__(self, names): r""" EXAMPLES:: @@ -42,7 +42,8 @@ def __init__(self,names): sage: TestSuite(F).run() """ from sage.categories.monoids import Monoids - Parent.__init__(self, base=self,names=names,category=Monoids()) + category = Monoids().FinitelyGeneratedAsMagma() + Parent.__init__(self, base=self, names=names, category=category) @cached_method def gens(self): @@ -56,3 +57,17 @@ def gens(self): (a, b, c, d, e) """ return tuple(self.gen(i) for i in range(self.ngens())) + + def monoid_generators(self): + r""" + Returns the generators for ``self``. + + EXAMPLES:: + + sage: F. = FreeMonoid(5) + sage: F.monoid_generators() + Family (a, b, c, d, e) + """ + from sage.sets.family import Family + return Family(self.gens()) + From fd09f2ae8d00810d91810bb3a0987d870db41e19 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 10 Apr 2016 01:49:43 -0500 Subject: [PATCH 065/163] Some last little fixes for the global_options option. --- .../thematic_tutorials/lie/infinity_crystals.rst | 14 ++++++++------ src/sage/doctest/sources.py | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst b/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst index 1b822dc51d0..e3d970af02f 100644 --- a/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst +++ b/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst @@ -242,16 +242,18 @@ containing the element `\boldsymbol{1}`, which we denote by sage: m.weight_in_root_lattice() -2*alpha[0] - 2*alpha[1] - 2*alpha[2] - alpha[3] -We can also model `B(\infty)` using the monomials `A_{i,k}` instead:: +We can also model `B(\infty)` using the monomials `A_{i,k}` instead: - sage: Ninf = crystals.infinity.NakajimaMonomials(['C',3,1], use_Y = False) - sage: ninf = Ninf.highest_weight_vector() - sage: n = ninf.f_string([0,1,2,3,2,1,0]); n +.. link:: + + sage: Minf.global_options(monomial='A') + sage: m A(0,0)^-1 A(0,3)^-1 A(1,0)^-1 A(1,2)^-1 A(2,0)^-1 A(2,1)^-1 A(3,0)^-1 - sage: n.weight() + sage: m.weight() -2*Lambda[0] + 2*Lambda[1] - 2*delta - sage: n.weight_in_root_lattice() + sage: m.weight_in_root_lattice() -2*alpha[0] - 2*alpha[1] - 2*alpha[2] - alpha[3] + sage: Minf.global_options.reset() Building the crystal graph output for these monomial crystals is the same as the constructions above:: diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index afd256279e9..4b07634f0cb 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -710,6 +710,7 @@ def _test_enough_doctests(self, check_extras=True, verbose=True): There are 15 tests in sage/combinat/permutation.py that are not being run There are 14 tests in sage/combinat/skew_partition.py that are not being run There are 18 tests in sage/combinat/tableau.py that are not being run + There are 1 tests in sage/combinat/crystals/monomial_crystals.py that are not being run There are 8 tests in sage/combinat/crystals/tensor_product.py that are not being run There are 11 tests in sage/combinat/rigged_configurations/rigged_configurations.py that are not being run There are 15 tests in sage/combinat/root_system/cartan_type.py that are not being run From 58ffd2c7cc9aece5194d47b4bf16b6131090fbe0 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 10 Apr 2016 02:10:06 -0500 Subject: [PATCH 066/163] A variant using an instance option. --- .../lie/infinity_crystals.rst | 4 +- .../combinat/crystals/monomial_crystals.py | 120 +++++++++++------- src/sage/doctest/sources.py | 1 - 3 files changed, 75 insertions(+), 50 deletions(-) diff --git a/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst b/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst index e3d970af02f..c307d91e6f8 100644 --- a/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst +++ b/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst @@ -246,14 +246,14 @@ We can also model `B(\infty)` using the monomials `A_{i,k}` instead: .. link:: - sage: Minf.global_options(monomial='A') + sage: Minf.set_monomials('A') sage: m A(0,0)^-1 A(0,3)^-1 A(1,0)^-1 A(1,2)^-1 A(2,0)^-1 A(2,1)^-1 A(3,0)^-1 sage: m.weight() -2*Lambda[0] + 2*Lambda[1] - 2*delta sage: m.weight_in_root_lattice() -2*alpha[0] - 2*alpha[1] - 2*alpha[2] - alpha[3] - sage: Minf.global_options.reset() + sage: Minf.set_monomials('Y') Building the crystal graph output for these monomial crystals is the same as the constructions above:: diff --git a/src/sage/combinat/crystals/monomial_crystals.py b/src/sage/combinat/crystals/monomial_crystals.py index 18d608600c3..1c39fcce0ab 100644 --- a/src/sage/combinat/crystals/monomial_crystals.py +++ b/src/sage/combinat/crystals/monomial_crystals.py @@ -100,7 +100,6 @@ from sage.structure.element import Element from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.global_options import GlobalOptions from sage.categories.classical_crystals import ClassicalCrystals from sage.categories.highest_weight_crystals import HighestWeightCrystals from sage.categories.regular_crystals import RegularCrystals @@ -113,37 +112,6 @@ from sage.matrix.matrix import is_Matrix from sage.matrix.matrix_space import MatrixSpace - -NakajimaMonomialGlobalOptions=GlobalOptions(name='Nakajima_monomials', - doc=r""" - Sets the global options for Nakajima monomials. The default is to - use the `Y` variables. - - There are the following variables: - - - `Y` -- corresponds to fundamental weights - - `A` -- corresponds to simple roots - - If the `A` variables are used, the output is written as - `\prod_i Y_{i0}^{\lambda_i} \prod_{ik} A_{ik}^c_{ik}`, where - `\sum_{i \in I} \lambda_i \Lambda_i` is the corresponding - dominant weight. - """, - end_doc=r""" - If no parameters are set, then the function returns a copy of the - options dictionary. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['A',2]) - """, - monomial=dict(default="Y", - description='Sets which variables to use for output', - values=dict(Y='use the Y variables', - A='use the A variables'), - case_sensitive=True) -) - class NakajimaMonomial(Element): r""" An element of the monomial crystal. @@ -165,13 +133,13 @@ class NakajimaMonomial(Element): An example using the `A` variables:: sage: M = crystals.infinity.NakajimaMonomials("A3") - sage: M.global_options(monomial='A') + sage: M.set_monomials('A') sage: mg = M.module_generators[0] sage: mg.f_string([1,2,3,2,1]) A(1,0)^-1 A(1,1)^-1 A(2,0)^-2 A(3,0)^-1 sage: mg.f_string([3,2,1]) A(1,2)^-1 A(2,1)^-1 A(3,0)^-1 - sage: M.global_options.reset() + sage: M.set_monomials('Y') """ def __init__(self, parent, Y, A): @@ -199,12 +167,12 @@ def _repr_(self): sage: M = crystals.infinity.NakajimaMonomials(['A',5,2]) sage: x = M({(1,0):1, (2,2):-2, (0,5):10}); x Y(0,5)^10 Y(1,0) Y(2,2)^-2 - sage: M.global_options(monomial='A') + sage: M.set_monomials('A') sage: x A(1,0)^-2 A(1,1)^-2 A(2,0)^-4 A(2,1)^-2 A(3,0)^-2 - sage: M.global_options.reset() + sage: M.set_monomials('Y') """ - return self.parent().global_options.dispatch(self, '_repr_', 'monomial') + return getattr(self, '_repr_' + self.parent()._monomial)() def _repr_Y(self): r""" @@ -322,12 +290,12 @@ def _latex_(self): sage: x = M.module_generators[0].f_string([1,0,2]) sage: latex(x) Y_{0,0}^{-1} Y_{1,0}^{-1} Y_{1,1}^{2} Y_{2,0} Y_{2,1}^{-1} - sage: M.global_options(monomial='A') + sage: M.set_monomials('A') sage: latex(x) A_{0,0}^{-1} A_{1,0}^{-1} A_{2,0}^{-1} - sage: M.global_options.reset() + sage: M.set_monomials('Y') """ - return self.parent().global_options.dispatch(self, '_latex_', 'monomial') + return getattr(self, '_latex_' + self.parent()._monomial)() def _latex_Y(self): r""" @@ -613,7 +581,7 @@ def e(self, i): None] sage: M = crystals.infinity.NakajimaMonomials(['D',4,1]) - sage: M.global_options(monomial='A') + sage: M.set_monomials('A') sage: m = M.module_generators[0].f_string([4,2,3,0]) sage: [m.e(i) for i in M.index_set()] [A(2,1)^-1 A(3,1)^-1 A(4,0)^-1, @@ -621,7 +589,7 @@ def e(self, i): None, A(0,2)^-1 A(2,1)^-1 A(4,0)^-1, None] - sage: M.global_options.reset() + sage: M.set_monomials('Y') """ if i not in self.parent().index_set(): raise ValueError("i must be an element of the index set") @@ -879,12 +847,15 @@ def __classcall_private__(cls, ct, c=None, use_Y=None): """ if use_Y is not None: from sage.misc.superseded import deprecation - deprecation(18895, 'use_Y is deprecated; use the global_options variable "monomial" instead.') + deprecation(18895, 'use_Y is deprecated; use the set_monomials() method instead.') cartan_type = CartanType(ct) n = len(cartan_type.index_set()) c = InfinityCrystalOfNakajimaMonomials._normalize_c(c, n) - return super(InfinityCrystalOfNakajimaMonomials, cls).__classcall__(cls, cartan_type, c) + M = super(InfinityCrystalOfNakajimaMonomials, cls).__classcall__(cls, cartan_type, c) + if use_Y: + M.set_monomials('A') + return M def __init__(self, ct, c, category=None): r""" @@ -895,6 +866,7 @@ def __init__(self, ct, c, category=None): """ self._cartan_type = ct self._c = c + self._monomial = 'Y' if category is None: category = (HighestWeightCrystals(), InfiniteEnumeratedSets()) @@ -1027,8 +999,62 @@ def weight_lattice_realization(self): return F.weight_lattice(extended=True) return F.weight_lattice() + def set_monomials(self, letter): + r""" + Set the type of monomials to use for the element output. + + If the `A` variables are used, the output is written as + `\prod_i Y_{i0}^{\lambda_i} \prod_{ik} A_{ik}^c_{ik}`, where + `\sum_{i \in I} \lambda_i \Lambda_i` is the corresponding + dominant weight. + + INPUT: + + - ``letter`` -- can be one of the following: + + * ``'Y'`` - use `Y_{ik}`, corresponds to fundamental weights + * ``'A'`` - use `A_{ik}`, corresponds to simple roots + + EXAMPLES:: + + sage: M = crystals.infinity.NakajimaMonomials(['A', 4]) + sage: elt = M.highest_weight_vector().f_string([2,1,3,2,3,2,4,3]) + sage: elt + Y(1,2) Y(2,0)^-1 Y(2,2)^-1 Y(3,0)^-1 Y(3,2)^-1 Y(4,0) + sage: M.set_monomials('A') + sage: elt + A(1,1)^-1 A(2,0)^-1 A(2,1)^-2 A(3,0)^-2 A(3,1)^-1 A(4,0)^-1 + sage: M.set_monomials('Y') + + :: + + sage: La = RootSystem(['A',2]).weight_lattice().fundamental_weights() + sage: M = crystals.NakajimaMonomials(La[1]+La[2]) + sage: lw = M.lowest_weight_vectors()[0] + sage: lw + Y(1,2)^-1 Y(2,1)^-1 + sage: M.set_monomials('A') + sage: lw + Y(1,0) Y(2,0) A(1,0)^-1 A(1,1)^-1 A(2,0)^-2 + sage: M.set_monomials('Y') + """ + if letter not in ['Y', 'A']: + raise ValueError("invalid monomial type") + self._monomial = letter + + def get_monomials(self): + """ + Return the type of monomials to use for the element output. + + EXAMPLES:: + + sage: M = crystals.infinity.NakajimaMonomials(['A', 4]) + sage: M.get_monomials() + 'Y' + """ + return self._monomial + Element = NakajimaMonomial - global_options = NakajimaMonomialGlobalOptions class CrystalOfNakajimaMonomialsElement(NakajimaMonomial): r""" @@ -1072,7 +1098,7 @@ def f(self, i): :: sage: M = crystals.infinity.NakajimaMonomials("E8") - sage: M.global_options(monomial='A') + sage: M.set_monomials('A') sage: m = M.module_generators[0].f_string([4,2,3,8]) sage: m A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1 @@ -1085,7 +1111,7 @@ def f(self, i): A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(6,0)^-1 A(8,0)^-1, A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(7,1)^-1 A(8,0)^-1, A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-2] - sage: M.global_options.reset() + sage: M.set_monomials('Y') """ if self.phi(i) == 0: return None diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index 4b07634f0cb..afd256279e9 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -710,7 +710,6 @@ def _test_enough_doctests(self, check_extras=True, verbose=True): There are 15 tests in sage/combinat/permutation.py that are not being run There are 14 tests in sage/combinat/skew_partition.py that are not being run There are 18 tests in sage/combinat/tableau.py that are not being run - There are 1 tests in sage/combinat/crystals/monomial_crystals.py that are not being run There are 8 tests in sage/combinat/crystals/tensor_product.py that are not being run There are 11 tests in sage/combinat/rigged_configurations/rigged_configurations.py that are not being run There are 15 tests in sage/combinat/root_system/cartan_type.py that are not being run From c95e024b950cf660c5a13ddbfc6793167f01b62c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 10 Apr 2016 03:32:16 -0500 Subject: [PATCH 067/163] Fixing trivial failing doctests due to new iterator. --- src/sage/algebras/associated_graded.py | 8 +++++--- src/sage/algebras/jordan_algebra.py | 2 +- .../examples/filtered_algebras_with_basis.py | 6 +++++- .../filtered_algebras_with_basis.py | 9 +++++---- .../categories/filtered_modules_with_basis.py | 19 ++++++++++++------- 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/sage/algebras/associated_graded.py b/src/sage/algebras/associated_graded.py index 750cbe33df1..d6f6a88b014 100644 --- a/src/sage/algebras/associated_graded.py +++ b/src/sage/algebras/associated_graded.py @@ -139,10 +139,10 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): ``grA`` are isomorphic:: sage: grA(A.an_element()) - bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']) + 2*bar(U['z']) + 3*bar(1) sage: elt = A.an_element() + A.algebra_generators()['x'] + 2 sage: grelt = grA(elt); grelt - bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']) + 2*bar(1) + bar(U['x']^2*U['y']^2*U['z']^3) + 2*bar(U['x']) + 2*bar(U['z']) + 5*bar(1) sage: A(grelt) == elt True @@ -241,8 +241,10 @@ def _element_constructor_(self, x): sage: grA = A.graded_algebra() sage: grA(A.an_element()) bar(U['x']^2*U['y']^2*U['z']^3) + + bar(U['x']) + 2*bar(U['z']) + 3*bar(1) sage: grA(A.an_element() + A.algebra_generators()['x'] + 2) - bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']) + 2*bar(1) + bar(U['x']^2*U['y']^2*U['z']^3) + + 2*bar(U['x']) + 2*bar(U['z']) + 5*bar(1) """ if isinstance(x, CombinatorialFreeModule.Element): if x.parent() is self._A: diff --git a/src/sage/algebras/jordan_algebra.py b/src/sage/algebras/jordan_algebra.py index e9cc4d0f76d..faa4c25210f 100644 --- a/src/sage/algebras/jordan_algebra.py +++ b/src/sage/algebras/jordan_algebra.py @@ -287,7 +287,7 @@ def _an_element_(self): sage: F. = FreeAlgebra(QQ) sage: J = JordanAlgebra(F) sage: J.an_element() - x + 2*y + 2*y^2 + 3*y^2*z """ return self.element_class(self, self._A.an_element()) diff --git a/src/sage/categories/examples/filtered_algebras_with_basis.py b/src/sage/categories/examples/filtered_algebras_with_basis.py index 9e7765aefa8..f5160a97a4c 100644 --- a/src/sage/categories/examples/filtered_algebras_with_basis.py +++ b/src/sage/categories/examples/filtered_algebras_with_basis.py @@ -114,8 +114,12 @@ def degree_on_basis(self, m): sage: A.degree_on_basis((x^4).leading_support()) 4 sage: a = A.an_element(); a + U['x']^2*U['y']^2*U['z']^3 + U['x'] + 2*U['z'] + 3 + sage: A.degree_on_basis(a.trailing_support()) + 1 + sage: s = sorted(a.support(), key=str)[2]; s U['x']^2*U['y']^2*U['z']^3 - sage: A.degree_on_basis(a.leading_support()) + sage: A.degree_on_basis(s) 7 """ return len(m) diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index 053d119a040..c503cdb4316 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -131,9 +131,10 @@ def to_graded_conversion(self): sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p - U['x']^2*U['y']^2*U['z']^3 + U['x'] + 2 + U['x']^2*U['y']^2*U['z']^3 + 2*U['x'] + 2*U['z'] + 5 sage: q = A.to_graded_conversion()(p); q - bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']) + 2*bar(1) + bar(U['x']^2*U['y']^2*U['z']^3) + + 2*bar(U['x']) + 2*bar(U['z']) + 5*bar(1) sage: q.parent() is A.graded_algebra() True """ @@ -159,7 +160,7 @@ def from_graded_conversion(self): sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p - U['x']^2*U['y']^2*U['z']^3 + U['x'] + 2 + U['x']^2*U['y']^2*U['z']^3 + 2*U['x'] + 2*U['z'] + 5 sage: q = A.to_graded_conversion()(p) sage: A.from_graded_conversion()(q) == p True @@ -190,7 +191,7 @@ def projection(self, i): sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p - U['x']^2*U['y']^2*U['z']^3 + U['x'] + 2 + U['x']^2*U['y']^2*U['z']^3 + 2*U['x'] + 2*U['z'] + 5 sage: q = A.projection(7)(p); q bar(U['x']^2*U['y']^2*U['z']^3) sage: q.parent() is A.graded_algebra() diff --git a/src/sage/categories/filtered_modules_with_basis.py b/src/sage/categories/filtered_modules_with_basis.py index 7f2320f7f19..9e870db932d 100644 --- a/src/sage/categories/filtered_modules_with_basis.py +++ b/src/sage/categories/filtered_modules_with_basis.py @@ -832,12 +832,14 @@ def homogeneous_component(self, n): 0 sage: A = AlgebrasWithBasis(ZZ).Filtered().example() - sage: g = A.an_element() - 2 * A.algebra_generators()['x'] * A.algebra_generators()['y']; g + sage: G = A.algebra_generators() + sage: g = A.an_element() - 2 * G['x'] * G['y']; g U['x']^2*U['y']^2*U['z']^3 - 2*U['x']*U['y'] + + U['x'] + 2*U['z'] + 3 sage: g.homogeneous_component(-1) 0 sage: g.homogeneous_component(0) - 0 + 3 sage: g.homogeneous_component(2) -2*U['x']*U['y'] sage: g.homogeneous_component(5) @@ -896,22 +898,25 @@ def truncate(self, n): 2*P[] + 2*P[1] + 3*P[2] sage: A = AlgebrasWithBasis(ZZ).Filtered().example() - sage: g = A.an_element() - 2 * A.algebra_generators()['x'] * A.algebra_generators()['y']; g + sage: G = A.algebra_generators() + sage: g = A.an_element() - 2 * G['x'] * G['y']; g U['x']^2*U['y']^2*U['z']^3 - 2*U['x']*U['y'] + + U['x'] + 2*U['z'] + 3 sage: g.truncate(-1) 0 sage: g.truncate(0) 0 sage: g.truncate(2) - 0 + U['x'] + 2*U['z'] + 3 sage: g.truncate(3) - -2*U['x']*U['y'] + -2*U['x']*U['y'] + U['x'] + 2*U['z'] + 3 sage: g.truncate(5) - -2*U['x']*U['y'] + -2*U['x']*U['y'] + U['x'] + 2*U['z'] + 3 sage: g.truncate(7) - -2*U['x']*U['y'] + -2*U['x']*U['y'] + U['x'] + 2*U['z'] + 3 sage: g.truncate(8) U['x']^2*U['y']^2*U['z']^3 - 2*U['x']*U['y'] + + U['x'] + 2*U['z'] + 3 TESTS: From 36222eb9333ea8b7ba3266b5dc26e54511074c37 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 10 Apr 2016 03:57:01 -0500 Subject: [PATCH 068/163] If checking mod == 0, then make sure to do the division. --- src/sage/combinat/integer_vector_weighted.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/integer_vector_weighted.py b/src/sage/combinat/integer_vector_weighted.py index 2081dd32902..58fca1f4046 100644 --- a/src/sage/combinat/integer_vector_weighted.py +++ b/src/sage/combinat/integer_vector_weighted.py @@ -23,6 +23,7 @@ from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.sets_with_grading import SetsWithGrading from __builtin__ import list as builtinlist +from sage.rings.integer_ring import ZZ from sage.rings.integer import Integer from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent @@ -155,9 +156,9 @@ def __contains__(self, x): sage: [3,-1,0] in WeightedIntegerVectors([2,1,1]) False """ - return isinstance(x, (builtinlist, Permutation)) and \ - len(x) == len(self._weights) and \ - all(isinstance(i, (int, Integer)) and i>=0 for i in x) + return (isinstance(x, (builtinlist, Permutation)) + and len(x) == len(self._weights) + and all(isinstance(i, (int, Integer)) and i >= 0 for i in x)) def subset(self, size = None): """ @@ -282,11 +283,18 @@ def iterator_fast(n, l): """ Iterate over all ``l`` weighted integer vectors with total weight ``n``. + INPUT: + + - ``n`` -- an integer + - ``l`` -- the weights in weakly increasing order + EXAMPLES:: sage: from sage.combinat.integer_vector_weighted import iterator_fast sage: list(iterator_fast(3, [1,1,2])) [[0, 1, 1], [1, 0, 1], [0, 3, 0], [1, 2, 0], [2, 1, 0], [3, 0, 0]] + sage: list(iterator_fast(2, [2])) + [[1]] """ if n < 0: return @@ -297,7 +305,7 @@ def iterator_fast(n, l): return if len(l) == 1: if n % l[-1] == 0: - yield [n] + yield [n / l[-1]] return k = -1 @@ -307,7 +315,7 @@ def iterator_fast(n, l): cur[0] -= 1 rem += l[k] if rem == 0: - yield [Integer(0)] * (len(l) - len(cur)) + cur + yield [ZZ.zero()] * (len(l) - len(cur)) + cur elif cur[0] < 0 or rem < 0: rem += cur.pop(0) * l[k] k += 1 From fcabf36196a9b13be52074b40ca71882160a60cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= Date: Sun, 10 Apr 2016 11:21:52 +0200 Subject: [PATCH 069/163] 20410: implement tab completion on 'units' and 'units.*' --- src/sage/symbolic/units.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/sage/symbolic/units.py b/src/sage/symbolic/units.py index e8c4a7ba30c..8bdd8747e73 100644 --- a/src/sage/symbolic/units.py +++ b/src/sage/symbolic/units.py @@ -1034,7 +1034,7 @@ class that derives from symbolic expression, and has a specialized class Units: """ - A collection of units of a some type. + A collection of units of some type. EXAMPLES:: @@ -1114,6 +1114,25 @@ def trait_names(self): """ return sorted([x for x in self.__data.keys() if '/' not in x]) + def __dir__(self): + """ + Return the list of names of the attributes of ``self``. + + This complements the usual content of ``dir``, with the names + of the units (or unit collections) of ``self``. + + This enables tab completion on ``units`` and its + subcollections. + + EXAMPLES:: + + sage: dir(units) + ['_Units__data', '_Units__name', '_Units__units', 'acceleration', ..., 'volume'] + sage: dir(units.force) + ['_Units__data', '_Units__name', '_Units__units', 'dyne', ..., 'ton_force'] + """ + return self.__dict__.keys() + self.__data.keys() + def __getattr__(self, name): """ Return the unit with the given name. From e6402496bad187ba92f21d3f3bd46f74e488908f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 10 Apr 2016 07:35:31 -0500 Subject: [PATCH 070/163] Last fix of doctests. --- src/sage/categories/examples/filtered_algebras_with_basis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/examples/filtered_algebras_with_basis.py b/src/sage/categories/examples/filtered_algebras_with_basis.py index f5160a97a4c..2738c29cfa4 100644 --- a/src/sage/categories/examples/filtered_algebras_with_basis.py +++ b/src/sage/categories/examples/filtered_algebras_with_basis.py @@ -115,7 +115,7 @@ def degree_on_basis(self, m): 4 sage: a = A.an_element(); a U['x']^2*U['y']^2*U['z']^3 + U['x'] + 2*U['z'] + 3 - sage: A.degree_on_basis(a.trailing_support()) + sage: A.degree_on_basis(a.leading_support()) 1 sage: s = sorted(a.support(), key=str)[2]; s U['x']^2*U['y']^2*U['z']^3 From e7914d4060cb7b1cb7429a09654bb4eb34f64adc Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 10 Apr 2016 07:48:09 -0500 Subject: [PATCH 071/163] Pulling changes from #17039 to fix doctest with this (#20405) + #20403. --- src/sage/algebras/associated_graded.py | 8 ++++---- src/sage/algebras/jordan_algebra.py | 2 +- .../examples/filtered_algebras_with_basis.py | 2 +- .../categories/filtered_algebras_with_basis.py | 10 +++++----- .../categories/filtered_modules_with_basis.py | 16 ++++++++-------- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/sage/algebras/associated_graded.py b/src/sage/algebras/associated_graded.py index d6f6a88b014..0f7c76c4663 100644 --- a/src/sage/algebras/associated_graded.py +++ b/src/sage/algebras/associated_graded.py @@ -139,10 +139,10 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): ``grA`` are isomorphic:: sage: grA(A.an_element()) - bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']) + 2*bar(U['z']) + 3*bar(1) + bar(U['x']^2*U['y']^2*U['z']^3) + 2*bar(U['x']) + 3*bar(U['y']) + bar(1) sage: elt = A.an_element() + A.algebra_generators()['x'] + 2 sage: grelt = grA(elt); grelt - bar(U['x']^2*U['y']^2*U['z']^3) + 2*bar(U['x']) + 2*bar(U['z']) + 5*bar(1) + bar(U['x']^2*U['y']^2*U['z']^3) + 3*bar(U['x']) + 3*bar(U['y']) + 3*bar(1) sage: A(grelt) == elt True @@ -241,10 +241,10 @@ def _element_constructor_(self, x): sage: grA = A.graded_algebra() sage: grA(A.an_element()) bar(U['x']^2*U['y']^2*U['z']^3) - + bar(U['x']) + 2*bar(U['z']) + 3*bar(1) + + 2*bar(U['x']) + 3*bar(U['y']) + bar(1) sage: grA(A.an_element() + A.algebra_generators()['x'] + 2) bar(U['x']^2*U['y']^2*U['z']^3) - + 2*bar(U['x']) + 2*bar(U['z']) + 5*bar(1) + + 3*bar(U['x']) + 3*bar(U['y']) + 3*bar(1) """ if isinstance(x, CombinatorialFreeModule.Element): if x.parent() is self._A: diff --git a/src/sage/algebras/jordan_algebra.py b/src/sage/algebras/jordan_algebra.py index faa4c25210f..c2fe2128fb7 100644 --- a/src/sage/algebras/jordan_algebra.py +++ b/src/sage/algebras/jordan_algebra.py @@ -287,7 +287,7 @@ def _an_element_(self): sage: F. = FreeAlgebra(QQ) sage: J = JordanAlgebra(F) sage: J.an_element() - 2*y + 2*y^2 + 3*y^2*z + 2 + 2*x + 3*y """ return self.element_class(self, self._A.an_element()) diff --git a/src/sage/categories/examples/filtered_algebras_with_basis.py b/src/sage/categories/examples/filtered_algebras_with_basis.py index 2738c29cfa4..3de3456a2b6 100644 --- a/src/sage/categories/examples/filtered_algebras_with_basis.py +++ b/src/sage/categories/examples/filtered_algebras_with_basis.py @@ -114,7 +114,7 @@ def degree_on_basis(self, m): sage: A.degree_on_basis((x^4).leading_support()) 4 sage: a = A.an_element(); a - U['x']^2*U['y']^2*U['z']^3 + U['x'] + 2*U['z'] + 3 + U['x']^2*U['y']^2*U['z']^3 + 2*U['x'] + 3*U['y'] + 1 sage: A.degree_on_basis(a.leading_support()) 1 sage: s = sorted(a.support(), key=str)[2]; s diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index c503cdb4316..f8cdfbadfe2 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -131,10 +131,10 @@ def to_graded_conversion(self): sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p - U['x']^2*U['y']^2*U['z']^3 + 2*U['x'] + 2*U['z'] + 5 + U['x']^2*U['y']^2*U['z']^3 + 3*U['x'] + 3*U['y'] + 3 sage: q = A.to_graded_conversion()(p); q - bar(U['x']^2*U['y']^2*U['z']^3) - + 2*bar(U['x']) + 2*bar(U['z']) + 5*bar(1) + bar(U['x']^2*U['y']^2*U['z']^3) + 3*bar(U['x']) + + 3*bar(U['y']) + 3*bar(1) sage: q.parent() is A.graded_algebra() True """ @@ -160,7 +160,7 @@ def from_graded_conversion(self): sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p - U['x']^2*U['y']^2*U['z']^3 + 2*U['x'] + 2*U['z'] + 5 + U['x']^2*U['y']^2*U['z']^3 + 3*U['x'] + 3*U['y'] + 3 sage: q = A.to_graded_conversion()(p) sage: A.from_graded_conversion()(q) == p True @@ -191,7 +191,7 @@ def projection(self, i): sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p - U['x']^2*U['y']^2*U['z']^3 + 2*U['x'] + 2*U['z'] + 5 + U['x']^2*U['y']^2*U['z']^3 + 3*U['x'] + 3*U['y'] + 3 sage: q = A.projection(7)(p); q bar(U['x']^2*U['y']^2*U['z']^3) sage: q.parent() is A.graded_algebra() diff --git a/src/sage/categories/filtered_modules_with_basis.py b/src/sage/categories/filtered_modules_with_basis.py index 9e870db932d..29b372f81be 100644 --- a/src/sage/categories/filtered_modules_with_basis.py +++ b/src/sage/categories/filtered_modules_with_basis.py @@ -835,11 +835,11 @@ def homogeneous_component(self, n): sage: G = A.algebra_generators() sage: g = A.an_element() - 2 * G['x'] * G['y']; g U['x']^2*U['y']^2*U['z']^3 - 2*U['x']*U['y'] - + U['x'] + 2*U['z'] + 3 + + 2*U['x'] + 3*U['y'] + 1 sage: g.homogeneous_component(-1) 0 sage: g.homogeneous_component(0) - 3 + 1 sage: g.homogeneous_component(2) -2*U['x']*U['y'] sage: g.homogeneous_component(5) @@ -901,22 +901,22 @@ def truncate(self, n): sage: G = A.algebra_generators() sage: g = A.an_element() - 2 * G['x'] * G['y']; g U['x']^2*U['y']^2*U['z']^3 - 2*U['x']*U['y'] - + U['x'] + 2*U['z'] + 3 + + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(-1) 0 sage: g.truncate(0) 0 sage: g.truncate(2) - U['x'] + 2*U['z'] + 3 + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(3) - -2*U['x']*U['y'] + U['x'] + 2*U['z'] + 3 + -2*U['x']*U['y'] + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(5) - -2*U['x']*U['y'] + U['x'] + 2*U['z'] + 3 + -2*U['x']*U['y'] + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(7) - -2*U['x']*U['y'] + U['x'] + 2*U['z'] + 3 + -2*U['x']*U['y'] + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(8) U['x']^2*U['y']^2*U['z']^3 - 2*U['x']*U['y'] - + U['x'] + 2*U['z'] + 3 + + 2*U['x'] + 3*U['y'] + 1 TESTS: From b4b3254690dde8252dd78a2dd37d83a884470886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Mon, 11 Apr 2016 09:30:54 +0300 Subject: [PATCH 072/163] Added is_series_parallel(). --- src/sage/combinat/posets/posets.py | 34 +++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index f4db6431e71..9428f328623 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -71,6 +71,7 @@ :meth:`~FinitePoset.is_graded` | Return ``True`` if all maximal chains of the poset has same length. :meth:`~FinitePoset.is_ranked` | Return ``True`` if the poset has a rank function. :meth:`~FinitePoset.is_rank_symmetric` | Return ``True`` if the poset is rank symmetric. + :meth:`~FinitePoset.is_series_parallel` | Return ``True`` if the poset can be built by ordinal sums and disjoint unions. :meth:`~FinitePoset.is_eulerian` | Return ``True`` if the poset is Eulerian. :meth:`~FinitePoset.is_incomparable_chain_free` | Return ``True`` if the poset is (m+n)-free. :meth:`~FinitePoset.is_slender` | Return ``True`` if the poset is slender. @@ -2549,9 +2550,40 @@ def is_connected(self): """ return self._hasse_diagram.is_connected() + def is_series_parallel(self): + """ + Return ``True`` if the poset is series-parallel, and ``False`` + otherwise. + + A poset is *series-parallel* if it can be built up from one-element + posets using the operations of disjoint union and ordinal + sum. This is also called *N-free* property: every poset that is not + series-parallel contains a subposet isomorphic to the 4-element + N-shaped poset where `a > c, d` and `b > d`. + + See :wikipedia:`Series-parallel partial order`. + + EXAMPLES:: + + sage: VA = Poset({1: [2, 3], 4: [5], 6: [5]}) + sage: VA.is_series_parallel() + True + sage: big_N = Poset({1: [2, 4], 2: [3], 4:[7], 5:[6], 6:[7]}) + sage: big_N.is_series_parallel() + False + + TESTS:: + + sage: Poset().is_series_parallel() + True + """ + # TODO: Add series-parallel decomposition later. + N = Poset({0: [2, 3], 1: [3]}) + return not self.has_isomorphic_subposet(N) + def is_EL_labelling(self, f, return_raising_chains=False): r""" - Returns ``True`` if ``f`` is an EL labelling of ``self``. + Return ``True`` if ``f`` is an EL labelling of ``self``. A labelling `f` of the edges of the Hasse diagram of a poset is called an EL labelling (edge lexicographic labelling) if From f29d68508ffcaa40aadbfa1a6594633fdf388094 Mon Sep 17 00:00:00 2001 From: Marc Masdeu Date: Mon, 11 Apr 2016 09:31:39 +0100 Subject: [PATCH 073/163] Trac #20254: Fixed old style doctests. --- .../elliptic_curves/ell_modular_symbols.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py index 9f2001c4a22..d1ac3d4ef91 100644 --- a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py +++ b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py @@ -254,10 +254,10 @@ def _find_scaling_L_ratio(self): sage: rk0 = ['11a1', '11a2', '15a1', '27a1', '37b1'] sage: for la in rk0: # long time (3s on sage.math, 2011) - ... E = EllipticCurve(la) - ... me = E.modular_symbol(use_eclib = True) - ... ms = E.modular_symbol(use_eclib = False) - ... print E.lseries().L_ratio()*E.real_components(), me(0), ms(0) + ....: E = EllipticCurve(la) + ....: me = E.modular_symbol(use_eclib = True) + ....: ms = E.modular_symbol(use_eclib = False) + ....: print E.lseries().L_ratio()*E.real_components(), me(0), ms(0) 1/5 1/5 1/5 1 1 1 1/4 1/4 1/4 @@ -268,14 +268,14 @@ def _find_scaling_L_ratio(self): sage: [EllipticCurve(la).modular_symbol(use_eclib=True)(0) for la in rk1] # long time (1s on sage.math, 2011) [0, 0, 0, 0, 0, 0] sage: for la in rk1: # long time (8s on sage.math, 2011) - ... E = EllipticCurve(la) - ... m = E.modular_symbol(use_eclib = True) - ... lp = E.padic_lseries(5) - ... for D in [5,17,12,8]: - ... ED = E.quadratic_twist(D) - ... md = sum([kronecker(D,u)*m(ZZ(u)/D) for u in range(D)]) - ... etaD = lp._quotient_of_periods_to_twist(D) - ... assert ED.lseries().L_ratio()*ED.real_components() * etaD == md + ....: E = EllipticCurve(la) + ....: m = E.modular_symbol(use_eclib = True) + ....: lp = E.padic_lseries(5) + ....: for D in [5,17,12,8]: + ....: ED = E.quadratic_twist(D) + ....: md = sum([kronecker(D,u)*m(ZZ(u)/D) for u in range(D)]) + ....: etaD = lp._quotient_of_periods_to_twist(D) + ....: assert ED.lseries().L_ratio()*ED.real_components() * etaD == md """ E = self._E From d9f8bdee1447c80a0ecda4e31ee7675d3832775e Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 23 Mar 2016 18:45:57 +0100 Subject: [PATCH 074/163] Fix conversions matrix -> AffineGroup --- src/sage/groups/affine_gps/group_element.py | 28 ++++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/sage/groups/affine_gps/group_element.py b/src/sage/groups/affine_gps/group_element.py index 8a84ab5aafc..b9074c61238 100644 --- a/src/sage/groups/affine_gps/group_element.py +++ b/src/sage/groups/affine_gps/group_element.py @@ -31,10 +31,12 @@ """ #***************************************************************************** -# Copyright (C) 2006 David Joyner and William Stein -# -# Distributed under the terms of the GNU General Public License (GPL) +# Copyright (C) 2013 Volker Braun # +# 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 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** @@ -84,6 +86,20 @@ class AffineGroupElement(MultiplicativeGroupElement): sage: G(2) [2 0] [0] x |-> [0 2] x + [0] + + Conversion from a matrix and a matrix group element:: + + sage: M = Matrix(4, 4, [0, 0, -1, 1, 0, -1, 0, 1, -1, 0, 0, 1, 0, 0, 0, 1]) + sage: A = AffineGroup(3, ZZ) + sage: A(M) + [ 0 0 -1] [1] + x |-> [ 0 -1 0] x + [1] + [-1 0 0] [1] + sage: G = MatrixGroup([M]) + sage: A(G.0) + [ 0 0 -1] [1] + x |-> [ 0 -1 0] x + [1] + [-1 0 0] [1] """ def __init__(self, parent, A, b=0, convert=True, check=True): r""" @@ -95,10 +111,14 @@ def __init__(self, parent, A, b=0, convert=True, check=True): sage: g = G.random_element() sage: TestSuite(g).run() """ + try: + A = A.matrix() + except AttributeError: + pass if is_Matrix(A) and A.nrows() == A.ncols() == parent.degree()+1: g = A - A = g.submatrix(0,0,2,2) d = parent.degree() + A = g.submatrix(0, 0, d, d) b = [ g[i,d] for i in range(d) ] convert = True if convert: From cb93656f7f8b50a380a55b59b743dbfbaa6df99e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 11 Apr 2016 17:04:32 +0200 Subject: [PATCH 075/163] trac #19446 a few doc and code cleanup changes --- src/sage/combinat/binary_tree.py | 76 +++++++++++++++++--------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/src/sage/combinat/binary_tree.py b/src/sage/combinat/binary_tree.py index b0ce9d72dd8..0de58044d96 100644 --- a/src/sage/combinat/binary_tree.py +++ b/src/sage/combinat/binary_tree.py @@ -2041,14 +2041,15 @@ def comb(self, side='left'): Return the comb of a tree. There are two combs in a binary tree : a left comb and a right comb. - Consider all the vertices of the leftmost (resp. rightmost) branch of - the root. The left (resp. right) comb is the list of right (resp. left) - subtree of each of these vertices. + + Consider all the vertices of the leftmost (resp. rightmost) branch of + the root. The left (resp. right) comb is the list of right (resp. left) + subtrees of each of these vertices. INPUT: - - ``side`` -- (default: 'left') set to 'left' to obtain a left comb, and to 'right' to - obtain a right comb. + - ``side`` -- (default: 'left') set to 'left' to obtain a left + comb, and to 'right' to obtain a right comb. OUTPUT: @@ -2081,7 +2082,7 @@ def comb(self, side='left'): / \ / o o o / \ - o o + o o sage: BT.comb('left') [[[., .], [[[., .], [., .]], [., .]]], ., [., .]] sage: ascii_art(BT.comb('left')) @@ -2103,13 +2104,14 @@ def comb(self, side='left'): [ / \ ] [ o o ] """ + def _comb(side): if self.is_empty(): return [] tree = self[side] res = [] while not tree.is_empty(): - res.append(tree[1-side]) + res.append(tree[1 - side]) tree = tree[side] return res if side == 'left': @@ -2121,13 +2123,13 @@ def hook_number(self): r""" Return the number of hooks. - The hook of a vertex v is the union of {v}, its leftmost and - rightmost branches. + The hook of a vertex `v` is a set of vertices formed by the + union of `{v}`, its leftmost and rightmost branches. - There is a unique way to partition the vertices in hooks. + There is a unique way to partition the set of vertices in hooks. The number of hooks in such a partition is the hook number of the tree. - We can obtain this partition recursively by extracting the root's hook + We can obtain this partition recursively by extracting the root's hook and iterating the processus on each tree of the remaining forest. EXAMPLES:: @@ -2142,8 +2144,8 @@ def hook_number(self): o / \ o o - / - o + / + o sage: BT.hook_number() 1 sage: BT = BinaryTree( '[[[[., [., .]], .], [[., .], [[[., .], [., .]], [., .]]]], [., [[[., .], [[[., .], [., .]], .]], .]]]' ) @@ -2166,19 +2168,20 @@ def hook_number(self): """ if self.is_empty(): return 0 - return 1 + sum(t.hook_number() for t in self.comb('left') + self.comb('right')) + return 1 + sum(t.hook_number() + for t in self.comb('left') + self.comb('right')) def twisting_number(self): r""" - Return a 2-tuple where the first element of the tuple is the number - of straight left branches in the binary tree and the second one is - the number of straight right branches in the binary tree. + Return a pair (number of straight left branches, number of straight + right branches). - OUTPUT : + OUTPUT : - A list of size 2 of non negative integers. + A list of two integers. EXAMPLES:: + sage: BT = BinaryTree( '.' ) sage: BT.twisting_number() [0, 0] @@ -2189,8 +2192,8 @@ def twisting_number(self): o / \ o o - / - o + / + o sage: BT.twisting_number() [1, 1] sage: BT = BinaryTree( '[[[[., [., .]], .], [[., .], [[[., .], [., .]], [., .]]]], [., [[[., .], [[[., .], [., .]], .]], .]]]' ) @@ -2221,26 +2224,27 @@ def twisting_number(self): sage: BT.twisting_number() [1, 1] """ - tn=[0,0] - if self.node_number()<=1: + tn = [0, 0] + if self.node_number() <= 1: return tn - L=self.comb('left') - if len(L)>0: - tn[0]=tn[0]+1 + + L = self.comb('left') + if len(L): + tn[0] += 1 for h in L: - tw=BinaryTree([None,h]).twisting_number() - tn[0]=tn[0]+tw[0] - tn[1]=tn[1]+tw[1] - R=self.comb('right') - if len(R)>0: - tn[1]=tn[1]+1 + tw = BinaryTree([None, h]).twisting_number() + tn[0] += tw[0] + tn[1] += tw[1] + + R = self.comb('right') + if len(R): + tn[1] += 1 for l in R: - tw=BinaryTree([l,None]).twisting_number() - tn[0]=tn[0]+tw[0] - tn[1]=tn[1]+tw[1] + tw = BinaryTree([l, None]).twisting_number() + tn[0] += tw[0] + tn[1] += tw[1] return tn - def q_hook_length_fraction(self, q=None, q_factor=False): r""" Compute the ``q``-hook length fraction of the binary tree ``self``, From ed7b38b8ff391529672d7081a6999ff1d22d0eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 12 Apr 2016 10:11:34 +0200 Subject: [PATCH 076/163] trac #16075 .next into python3 format --- src/sage/categories/enumerated_sets.py | 2 +- src/sage/combinat/combinat.py | 6 +-- src/sage/combinat/designs/block_design.py | 2 +- src/sage/combinat/partition.py | 10 ++-- src/sage/combinat/posets/posets.py | 6 +-- .../rigged_configurations/kleber_tree.py | 2 +- .../combinat/root_system/coxeter_matrix.py | 2 +- .../combinat/species/generating_series.py | 4 +- src/sage/databases/findstat.py | 4 +- src/sage/knots/link.py | 2 +- src/sage/libs/giac.py | 2 +- src/sage/manifolds/manifold.py | 2 +- .../modular/arithgroup/arithgroup_perm.py | 2 +- .../asymptotic/growth_group_cartesian.py | 15 +++--- src/sage/sandpiles/sandpile.py | 50 +++++++++---------- 15 files changed, 57 insertions(+), 54 deletions(-) diff --git a/src/sage/categories/enumerated_sets.py b/src/sage/categories/enumerated_sets.py index 72b937db7aa..c5ef0e328c1 100644 --- a/src/sage/categories/enumerated_sets.py +++ b/src/sage/categories/enumerated_sets.py @@ -217,7 +217,7 @@ def __iter__(self): [5, 6, 7] """ - #Check to see if .first() and .next() are overridden in the subclass + # Check if .first(x) and .next(x) are overridden in the subclass if ( self.first != self._first_from_iterator and self.next != self._next_from_iterator ): return self._iterator_from_next() diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index 403121dbe4e..a8897e09f2c 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -1553,7 +1553,7 @@ def __list_from_iterator(self): def __iterator_from_next(self): """ - An iterator to use when .first() and .next() are provided. + An iterator to use when .first(x) and .next(x) are provided. EXAMPLES:: @@ -1569,7 +1569,7 @@ def __iterator_from_next(self): while True: try: f = self.next(f) - except (TypeError, ValueError ): + except (TypeError, ValueError): break if f is None or f is False : @@ -1670,7 +1670,7 @@ def __iter__(self): ... NotImplementedError: iterator called but not implemented """ - #Check to see if .first() and .next() are overridden in the subclass + #Check if .first(x) and .next(x) are overridden in the subclass if ( self.first != self.__first_from_iterator and self.next != self.__next_from_iterator ): return self.__iterator_from_next() diff --git a/src/sage/combinat/designs/block_design.py b/src/sage/combinat/designs/block_design.py index 7373d1889f3..a847d7d8714 100644 --- a/src/sage/combinat/designs/block_design.py +++ b/src/sage/combinat/designs/block_design.py @@ -499,7 +499,7 @@ def HughesPlane(q2, check=True): while `D_{0,70}`, `D_{1,59}` and `D_{10,57}` are not concurrent:: sage: blocks = H.blocks() - sage: line = lambda p,q: (b for b in blocks if p in b and q in b).next() + sage: line = lambda p,q: next(b for b in blocks if p in b and q in b) sage: b_0_1 = line(0, 1) sage: b_1_10 = line(1, 10) diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index a92c09d4d22..50393045e38 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -76,8 +76,8 @@ sage: Partitions(4).first() [4] -Using the method ``.next()``, we can calculate the 'next' partition. -When we are at the last partition, ``None`` will be returned:: +Using the method ``.next(p)``, we can calculate the 'next' partition +after `p`. When we are at the last partition, ``None`` will be returned:: sage: Partitions(4).next([4]) [3, 1] @@ -1201,6 +1201,8 @@ def next(self): return self.parent()(next_p[:m]) + __next__ = next + def size(self): """ Return the size of ``self``. @@ -6818,7 +6820,7 @@ def __iter__(self): sage: P = Partitions(regular=3) sage: it = P.__iter__() - sage: [it.next() for x in range(10)] + sage: [next(it) for x in range(10)] [[], [1], [2], [1, 1], [3], [2, 1], [4], [3, 1], [2, 2], [2, 1, 1]] """ n = 0 @@ -6896,7 +6898,7 @@ def __iter__(self): sage: P = Partitions(regular=3, max_length=2) sage: it = P.__iter__() - sage: [it.next() for x in range(10)] + sage: [next(it) for x in range(10)] [[], [1], [2], [1, 1], [3], [2, 1], [4], [3, 1], [2, 2], [5]] """ n = 0 diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index f4db6431e71..5cb6e9ea8e7 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -1913,7 +1913,7 @@ def relations_iterator(self, strict=False): sage: it = P.relations_iterator() sage: type(it) - sage: it.next(), it.next() + sage: next(it), next(it) ([1, 1], [1, 2]) sage: P = posets.PentagonPoset() @@ -3017,7 +3017,7 @@ def lower_covers_iterator(self, x): sage: l0 = P.lower_covers_iterator(3) sage: type(l0) - sage: l0.next() + sage: next(l0) 2 """ for e in self._hasse_diagram.neighbor_in_iterator(self._element_to_vertex(x)): @@ -3493,7 +3493,7 @@ def antichains_iterator(self): sage: it = Posets.PentagonPoset().antichains_iterator(); it - sage: it.next(), it.next() + sage: next(it), next(it) ([], [4]) .. SEEALSO:: :meth:`antichains` diff --git a/src/sage/combinat/rigged_configurations/kleber_tree.py b/src/sage/combinat/rigged_configurations/kleber_tree.py index f9a8d288ec4..deab2d46083 100644 --- a/src/sage/combinat/rigged_configurations/kleber_tree.py +++ b/src/sage/combinat/rigged_configurations/kleber_tree.py @@ -847,7 +847,7 @@ def _children_iter(self, node): L = [range(val + 1) for val in node.up_root.to_vector()] it = itertools.product(*L) - it.next() # First element is the zero element + next(it) # First element is the zero element for root in it: # Convert the list to an honest root in the root space converted_root = RS.sum_of_terms([[I[i], val] for i, val in enumerate(root)]) diff --git a/src/sage/combinat/root_system/coxeter_matrix.py b/src/sage/combinat/root_system/coxeter_matrix.py index 4e50cf135e2..474dd806460 100644 --- a/src/sage/combinat/root_system/coxeter_matrix.py +++ b/src/sage/combinat/root_system/coxeter_matrix.py @@ -678,7 +678,7 @@ def __iter__(self): EXAMPLES:: sage: CM = CoxeterMatrix([[1,8],[8,1]]) - sage: CM.__iter__().next() + sage: next(CM.__iter__()) (1, 8) """ return iter(self._matrix) diff --git a/src/sage/combinat/species/generating_series.py b/src/sage/combinat/species/generating_series.py index b2a16703adc..acd07348528 100644 --- a/src/sage/combinat/species/generating_series.py +++ b/src/sage/combinat/species/generating_series.py @@ -1264,7 +1264,7 @@ def _exp_gen(R = RationalField()): sage: from sage.combinat.species.generating_series import _exp_gen sage: g = _exp_gen() - sage: [g.next() for i in range(4)] + sage: [next(g) for i in range(4)] [p[], p[1], 1/2*p[1, 1] + 1/2*p[2], 1/6*p[1, 1, 1] + 1/2*p[2, 1] + 1/3*p[3]] """ return (_exp_term(i, R) for i in _integers_from(0)) @@ -1323,7 +1323,7 @@ def _cl_gen (R = RationalField()): sage: from sage.combinat.species.generating_series import _cl_gen sage: g = _cl_gen() - sage: [g.next() for i in range(4)] + sage: [next(g) for i in range(4)] [0, p[1], -1/2*p[1, 1] - 1/2*p[2], 1/3*p[1, 1, 1] - 1/3*p[3]] """ return (_cl_term(i, R) for i in _integers_from(0)) diff --git a/src/sage/databases/findstat.py b/src/sage/databases/findstat.py index 2f0c29fe13e..929d77cd60c 100644 --- a/src/sage/databases/findstat.py +++ b/src/sage/databases/findstat.py @@ -527,7 +527,7 @@ def query_by_dict(query, collection=None): # we expect a dictionary from objects or strings to # integers l = query.iteritems() - (key, value) = l.next() + (key, value) = next(l) (collection, to_str) = get_collection(collection, key) @@ -2475,7 +2475,7 @@ def _element_constructor_(self, entry): # check whether entry is iterable (it's not a string!) try: - obj = iter(entry).next() + obj = next(iter(entry)) for (id, c) in self._findstat_collections.iteritems(): if isinstance(obj, c[3]): return self.element_class(self, id, c, entry) diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index 2c952aa3cb8..4b174510856 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -531,7 +531,7 @@ def braid(self): # Get a simple path from a source to a sink in the digraph it = G.all_paths_iterator(starting_vertices=G.sources(), ending_vertices=G.sinks(), simple=True) - ordered_cycles = it.next() + ordered_cycles = next(it) B = BraidGroup(len(ordered_cycles)) available_crossings = copy(pd_code) diff --git a/src/sage/libs/giac.py b/src/sage/libs/giac.py index acf4c8a3189..0baf3d6aafd 100644 --- a/src/sage/libs/giac.py +++ b/src/sage/libs/giac.py @@ -255,7 +255,7 @@ def groebner_basis(gens, proba_epsilon=None, threads=None, prot=False, *args, ** gens = gens.gens() # get the ring from gens - P = iter(gens).next().parent() + P = next(iter(gens)).parent() K = P.base_ring() p = K.characteristic() diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index 218c54fba75..1f12a2f79fa 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -1028,7 +1028,7 @@ def irange(self, start=None): In general, one has always:: - sage: M.irange().next() == M.start_index() + sage: next(M.irange()) == M.start_index() True """ diff --git a/src/sage/modular/arithgroup/arithgroup_perm.py b/src/sage/modular/arithgroup/arithgroup_perm.py index efa7a120239..657b7523554 100644 --- a/src/sage/modular/arithgroup/arithgroup_perm.py +++ b/src/sage/modular/arithgroup/arithgroup_perm.py @@ -1531,7 +1531,7 @@ def surgroups(self): EXAMPLES:: sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)(5,6)", S3="(1,2,3)(4,5,6)") - sage: H = G.surgroups().next() + sage: H = next(G.surgroups()) sage: H Arithmetic subgroup with permutations of right cosets S2=(1,2) diff --git a/src/sage/rings/asymptotic/growth_group_cartesian.py b/src/sage/rings/asymptotic/growth_group_cartesian.py index d3fa97482a2..03de5dc5f43 100644 --- a/src/sage/rings/asymptotic/growth_group_cartesian.py +++ b/src/sage/rings/asymptotic/growth_group_cartesian.py @@ -757,7 +757,8 @@ def __init__(self, it): self.it = it self.var = None self.factors = None - def next(self): + + def custom_next(self): try: self.var, factors = next(self.it) self.factors = tuple(factors) @@ -772,24 +773,24 @@ def next(self): newS = [] newO = [] - S.next() - O.next() + S.custom_next() + O.custom_next() while S.var is not None or O.var is not None: if S.var is not None and S.var < O.var: newS.extend(S.factors) newO.extend(S.factors) - S.next() + S.custom_next() elif O.var is not None and S.var > O.var: newS.extend(O.factors) newO.extend(O.factors) - O.next() + O.custom_next() else: SL, OL = pushout_univariate_factors(self, other, S.var, S.factors, O.factors) newS.extend(SL) newO.extend(OL) - S.next() - O.next() + S.custom_next() + O.custom_next() assert(len(newS) == len(newO)) diff --git a/src/sage/sandpiles/sandpile.py b/src/sage/sandpiles/sandpile.py index 6f33c08c6a2..bf007bfb92d 100644 --- a/src/sage/sandpiles/sandpile.py +++ b/src/sage/sandpiles/sandpile.py @@ -607,7 +607,7 @@ def __init__(self, g, sink=None): # create digraph and initialize some variables DiGraph.__init__(self,g,weighted=True) self._dict = deepcopy(g) - if sink==None: + if sink is None: sink = self.vertices()[0] self._sink = sink # key for sink self._sink_ind = self.vertices().index(sink) @@ -2136,7 +2136,7 @@ def stable_configs(self, smax=None): sage: s = sandpiles.Complete(3) sage: a = s.stable_configs() - sage: a.next() + sage: next(a) {1: 0, 2: 0} sage: [i.values() for i in a] [[0, 1], [1, 0], [1, 1]] @@ -2144,7 +2144,7 @@ def stable_configs(self, smax=None): sage: list(b) [{1: 0, 2: 0}, {1: 1, 2: 0}] """ - if smax==None: + if smax is None: smax = self.max_stable().values() else: c = SandpileConfig(self,smax) @@ -2174,42 +2174,42 @@ def markov_chain(self,state, distrib=None): sage: s = sandpiles.Complete(4) sage: m = s.markov_chain([0,0,0]) - sage: m.next() # random + sage: next(m) # random {1: 0, 2: 0, 3: 0} - sage: m.next().values() # random + sage: next(m).values() # random [0, 0, 0] - sage: m.next().values() # random + sage: next(m).values() # random [0, 0, 0] - sage: m.next().values() # random + sage: next(m).values() # random [0, 0, 0] - sage: m.next().values() # random + sage: next(m).values() # random [0, 1, 0] - sage: m.next().values() # random + sage: next(m).values() # random [0, 2, 0] - sage: m.next().values() # random + sage: next(m).values() # random [0, 2, 1] - sage: m.next().values() # random + sage: next(m).values() # random [1, 2, 1] - sage: m.next().values() # random + sage: next(m).values() # random [2, 2, 1] sage: m = s.markov_chain(s.zero_div(), [0.1,0.1,0.1,0.7]) - sage: m.next().values() # random + sage: next(m).values() # random [0, 0, 0, 1] - sage: m.next().values() # random + sage: next(m).values() # random [0, 0, 1, 1] - sage: m.next().values() # random + sage: next(m).values() # random [0, 0, 1, 2] - sage: m.next().values() # random + sage: next(m).values() # random [1, 1, 2, 0] - sage: m.next().values() # random + sage: next(m).values() # random [1, 1, 2, 1] - sage: m.next().values() # random + sage: next(m).values() # random [1, 1, 2, 2] - sage: m.next().values() # random + sage: next(m).values() # random [1, 1, 2, 3] - sage: m.next().values() # random + sage: next(m).values() # random [1, 1, 2, 4] - sage: m.next().values() # random + sage: next(m).values() # random [1, 1, 3, 4] .. NOTE:: @@ -2244,7 +2244,7 @@ def markov_chain(self,state, distrib=None): st = SandpileDivisor(self,st) else: raise SyntaxError(state) - if distrib==None: # default = uniform distribution + if distrib is None: # default = uniform distribution distrib = [1/n]*n X = GeneralDiscreteDistribution(distrib) if isinstance(st,SandpileConfig): @@ -3822,7 +3822,7 @@ def add_random(self, distrib=None): c = deepcopy(self) ind = self._sandpile._sink_ind n = self._sandpile.num_verts() - if distrib==None: # default = uniform distribution on nonsink vertices + if distrib is None: # default = uniform distribution on nonsink vertices distrib = [1/(n-1)]*(n-1) if len(distrib)==n-1: # prob. dist. on nonsink vertices X = GeneralDiscreteDistribution(distrib) @@ -5148,7 +5148,7 @@ def simulate_threshold(self, distrib=None): S = E.sandpile() V = S.vertices() n = S.num_verts() - if distrib==None: # default = uniform distribution + if distrib is None: # default = uniform distribution distrib = [1/n]*n X = GeneralDiscreteDistribution(distrib) while not E.is_alive(): @@ -6032,7 +6032,7 @@ def add_random(self, distrib=None): D = deepcopy(self) S = self.sandpile() V = S.vertices() - if distrib==None: # default = uniform distribution + if distrib is None: # default = uniform distribution n = S.num_verts() distrib = [1/n]*n X = GeneralDiscreteDistribution(distrib) From b2eddb2ad789661b3862cd4a729f72f6dcf2f1fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= Date: Tue, 12 Apr 2016 10:20:20 +0200 Subject: [PATCH 077/163] 16075: revert 5e09bde1; the next method here is not meant to implement the iterator protocol --- src/sage/categories/examples/sets_cat.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/sage/categories/examples/sets_cat.py b/src/sage/categories/examples/sets_cat.py index 7b40f116de8..943d576abb8 100644 --- a/src/sage/categories/examples/sets_cat.py +++ b/src/sage/categories/examples/sets_cat.py @@ -286,8 +286,14 @@ def next(self): EXAMPLES:: sage: P = Sets().example("inherits") - sage: next(P.an_element()) + sage: p = P.an_element(); p + sage: p.next() 53 + + .. NOTE:: + + This method is not meant to implement the protocol iterator, + and thus not subject to Python 2 vs Python 3 incompatibilities. """ return self.parent().next(self) @@ -615,9 +621,9 @@ class PrimeNumbers_Facade(PrimeNumbers_Abstract): whereas:: - sage: next(pw) + sage: pw.next() 53 - sage: next(pi) + sage: pi.next() 53 TESTS:: From d4c1650d466dd9ca5fa24769dc7450b9779ea080 Mon Sep 17 00:00:00 2001 From: Marc Masdeu Date: Tue, 12 Apr 2016 09:27:13 +0100 Subject: [PATCH 078/163] Disallowed file transfers when using remote Magma. Added error checking when calling scp. --- src/sage/interfaces/expect.py | 2 +- src/sage/interfaces/magma.py | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/sage/interfaces/expect.py b/src/sage/interfaces/expect.py index 756f7017777..216a1ffb93f 100644 --- a/src/sage/interfaces/expect.py +++ b/src/sage/interfaces/expect.py @@ -140,7 +140,7 @@ def __init__(self, name, prompt, command=None, server=None, else: command = "sage-native-execute ssh -t %s '%s'"%(server, command) self.__is_remote = True -# eval_using_file_cutoff = 0 # don't allow this! + eval_using_file_cutoff = 0 # don't allow this! if verbose_start: print "Using remote server" print command diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index e36547419d4..edd41637ab0 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -251,8 +251,17 @@ def extcode_dir(iface = None): else: import os tmp = iface._remote_tmpdir() - os.system('scp -q -r %s/magma/ %s:%s/data'%(SAGE_EXTCODE,iface._server,tmp)) - EXTCODE_DIR = "%s/data/"%tmp + command = 'scp -q -r "%s/magma/" "%s:%s/data" 1>&2 2>/dev/null'%(SAGE_EXTCODE,iface._server,tmp) + try: + ans = os.system(command) + EXTCODE_DIR = "%s/data/"%tmp + if ans != 0: + raise IOError + except (OSError,IOError): + out_str = 'Tried to copy the file structure in "%s/magma/" to "%s:%s/data" and failed (possibly because scp is not installed in the system).\nFor the remote Magma to work you should populate the remote directory by some other method, or install scp in the system and retry.'%(SAGE_EXTCODE, iface._server, tmp) + from warnings import warn, resetwarnings + resetwarnings() + warn(out_str) return EXTCODE_DIR From 650ff4013b342e52bd6c21d0dff1c26d12485790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= Date: Tue, 12 Apr 2016 10:24:22 +0200 Subject: [PATCH 079/163] Minor doc improvements in sets_cat.py --- src/sage/categories/examples/sets_cat.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/categories/examples/sets_cat.py b/src/sage/categories/examples/sets_cat.py index 7b40f116de8..7ae35e8946f 100644 --- a/src/sage/categories/examples/sets_cat.py +++ b/src/sage/categories/examples/sets_cat.py @@ -235,7 +235,7 @@ def _from_integer_(self, i): def next(self, i): """ - Returns the next prime number + Return the next prime number. EXAMPLES:: @@ -250,7 +250,7 @@ def next(self, i): def some_elements(self): """ - Returns some prime numbers + Return some prime numbers. EXAMPLES:: @@ -268,7 +268,7 @@ def some_elements(self): class Element(Element): def is_prime(self): """ - Returns if a prime number is prime = True ! + Return whether ``self`` if a prime number EXAMPLES:: @@ -281,7 +281,7 @@ def is_prime(self): def next(self): """ - Returns the next prime number + Return the next prime number. EXAMPLES:: @@ -587,16 +587,16 @@ class PrimeNumbers_Facade(PrimeNumbers_Abstract): sage: z.parent() Integer Ring - The disadvantage of this implementation is that the element doesn't know - that they are primes so that prime testing is slow:: + The disadvantage of this implementation is that the elements don't know + that they are prime, so that prime testing is slow:: sage: pf = Sets().example("facade").an_element() sage: timeit("pf.is_prime()") # random 625 loops, best of 3: 4.1 us per loop compared to the other implementations where prime testing is only done if - needed during the construction of the element. Then the elements themselve - "know" that they are prime:: + needed during the construction of the element, and later on the elements + "knows" that they are prime:: sage: pw = Sets().example("wrapper").an_element() sage: timeit("pw.is_prime()") # random @@ -606,14 +606,14 @@ class PrimeNumbers_Facade(PrimeNumbers_Abstract): sage: timeit("pw.is_prime()") # random 625 loops, best of 3: 854 ns per loop - And moreover, the next methods for the element does not exist:: + Note also that the ``next` method for the elements does not exist:: sage: pf.next() Traceback (most recent call last): ... AttributeError: 'sage.rings.integer.Integer' object has no attribute 'next' - whereas:: + unlike in the other implementations:: sage: next(pw) 53 From e1ec418a0bdcb8a6a82d872e9deef6c660b9348f Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 12 Apr 2016 13:19:28 +0200 Subject: [PATCH 080/163] Support numpy.matrix in is_numpy_type() --- src/sage/matrix/constructor.pyx | 4 ++++ src/sage/structure/coerce.pyx | 12 +++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/sage/matrix/constructor.pyx b/src/sage/matrix/constructor.pyx index 3ccf39386a8..fdf847b96c2 100644 --- a/src/sage/matrix/constructor.pyx +++ b/src/sage/matrix/constructor.pyx @@ -720,6 +720,10 @@ class MatrixFactory(object): if is_numpy_type(type(arg)): import numpy if isinstance(arg, numpy.ndarray): + # Convert to a numpy array if it was a matrix. + if type(arg) is not numpy.ndarray: + arg = numpy.array(arg) + str_dtype = str(arg.dtype) if not (arg.flags.c_contiguous is True or arg.flags.f_contiguous is True): diff --git a/src/sage/structure/coerce.pyx b/src/sage/structure/coerce.pyx index e22f3a120a3..ca4dea9887f 100644 --- a/src/sage/structure/coerce.pyx +++ b/src/sage/structure/coerce.pyx @@ -258,6 +258,10 @@ cpdef bint is_numpy_type(t): True sage: is_numpy_type(numpy.float) # Alias for Python float False + sage: is_numpy_type(numpy.ndarray) + True + sage: is_numpy_type(numpy.matrix) + True sage: is_numpy_type(int) False sage: is_numpy_type(Integer) @@ -269,7 +273,13 @@ cpdef bint is_numpy_type(t): """ if not isinstance(t, type): return False - return strncmp((t).tp_name, "numpy.", 6) == 0 + cdef PyTypeObject* T = t + if strncmp(T.tp_name, "numpy.", 6) == 0: + return True + # Check base type. This is needed to detect numpy.matrix. + if strncmp(T.tp_base.tp_name, "numpy.", 6) == 0: + return True + return False cdef object _Integer From c5cad5d6321d8755ce456370c6054f5206e64020 Mon Sep 17 00:00:00 2001 From: Christian Nassau Date: Tue, 12 Apr 2016 12:48:06 +0200 Subject: [PATCH 081/163] Doctest conversion numpy.matrix -> Sage matrix --- src/sage/matrix/constructor.pyx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/matrix/constructor.pyx b/src/sage/matrix/constructor.pyx index fdf847b96c2..ad12a5f8882 100644 --- a/src/sage/matrix/constructor.pyx +++ b/src/sage/matrix/constructor.pyx @@ -452,7 +452,7 @@ class MatrixFactory(object): [4.0 5.0 6.0] [7.0 8.0 9.0] Full MatrixSpace of 3 by 3 dense matrices over Real Double Field - sage: n = numpy.array([[1,2,3],[4,5,6],[7,8,9]],'float64') + sage: n = numpy.matrix([[1,2,3],[4,5,6],[7,8,9]],'float64') sage: m = matrix(n); m; m.parent() [1.0 2.0 3.0] [4.0 5.0 6.0] @@ -464,7 +464,7 @@ class MatrixFactory(object): [4.0 5.0 6.0] [7.0 8.0 9.0] Full MatrixSpace of 3 by 3 dense matrices over Complex Double Field - sage: n = numpy.array([[1,2,3],[4,5,6],[7,8,9]],'complex128') + sage: n = numpy.matrix([[1,2,3],[4,5,6],[7,8,9]],'complex128') sage: m = matrix(n); m; m.parent() [1.0 2.0 3.0] [4.0 5.0 6.0] @@ -481,6 +481,8 @@ class MatrixFactory(object): [3.0 4.0] sage: matrix(numpy.array([[5]])) [5] + sage: matrix(numpy.matrix([[5]])) + [5] A ring and a numpy array:: From 27940830fe66aadcc5a2c94f919606f7d6d28caf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= Date: Tue, 12 Apr 2016 14:24:34 +0200 Subject: [PATCH 082/163] 20429: fixed typo --- src/sage/categories/examples/sets_cat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/examples/sets_cat.py b/src/sage/categories/examples/sets_cat.py index 7ae35e8946f..948e01ec9b3 100644 --- a/src/sage/categories/examples/sets_cat.py +++ b/src/sage/categories/examples/sets_cat.py @@ -606,7 +606,7 @@ class PrimeNumbers_Facade(PrimeNumbers_Abstract): sage: timeit("pw.is_prime()") # random 625 loops, best of 3: 854 ns per loop - Note also that the ``next` method for the elements does not exist:: + Note also that the ``next`` method for the elements does not exist:: sage: pf.next() Traceback (most recent call last): From d85b3900ade8c0826c34632fe3d1c4e17bb2c05f Mon Sep 17 00:00:00 2001 From: Marc Masdeu Date: Tue, 12 Apr 2016 14:17:06 +0100 Subject: [PATCH 083/163] Added possibility of changing Magma defaults via environment variables. --- src/sage/interfaces/magma.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index edd41637ab0..00e36787e21 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -298,7 +298,7 @@ class Magma(ExtraTabCompletion, Expect): '1.1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' sage: magma.SetDefaultRealFieldPrecision(30, nvals=0) # optional - magma """ - def __init__(self, maxread=None, script_subdirectory=None, + def __init__(self, script_subdirectory=None, logfile=None, server=None, server_tmpdir=None, user_config=False, seed=None, command='magma'): """ @@ -311,11 +311,15 @@ def __init__(self, maxread=None, script_subdirectory=None, - ``server`` - address of remote server + - ``server_tmpdir`` - temporary directory to use in remote server + - ``user_config`` - if True, then local user configuration files will be read by Magma. If False (the default), then Magma is started with the -n option which suppresses user configuration files. + - ``seed`` - Seed to use in the random number generator. + - ``command`` - (Default: 'magma') The command to execute to start Magma. EXAMPLES:: @@ -325,6 +329,21 @@ def __init__(self, maxread=None, script_subdirectory=None, """ if not user_config: command += ' -n' + + # Obtain the parameters from the environment, to allow the magma = Magma() phrase + # to work with non-default parameters. + import os + if server is None: + server = os.getenv('SAGE_MAGMA_SERVER') + if server_tmpdir is None: + server_tmpdir = os.getenv('SAGE_MAGMA_SERVER_TMPDIR') + if command is None: + command = os.getenv('SAGE_MAGMA_COMMAND') + if script_subdirectory is None: + script_subdirectory = os.getenv('SAGE_MAGMA_SCRIPT_SUBDIRECTORY') + if seed is None: + seed = os.getenv('SAGE_MAGMA_SEED') + Expect.__init__(self, name = "magma", prompt = ">>SAGE>>", From bc6a1e30abf968e5a14c50a7fa06b1e054295adf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= Date: Tue, 12 Apr 2016 16:24:10 +0200 Subject: [PATCH 084/163] 16075: xxx.next() -> next(xxx) in boost_graph for Python3 compatibility --- src/sage/graphs/base/boost_graph.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index 894e542952e..2973d2b417f 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -841,9 +841,9 @@ cpdef shortest_paths(g, start, weight_function=None, algorithm=None): pred = {} if weight_function is not None: - correct_type = type(weight_function(g.edge_iterator().next())) + correct_type = type(weight_function(next(g.edge_iterator()))) elif g.weighted(): - correct_type = type(g.edge_iterator().next()[2]) + correct_type = type(next(g.edge_iterator())[2]) else: correct_type = int # Needed for rational curves. @@ -954,9 +954,9 @@ cpdef johnson_shortest_paths(g, weight_function = None): raise ValueError("The graph contains a negative cycle.") if weight_function is not None: - correct_type = type(weight_function(g.edge_iterator().next())) + correct_type = type(weight_function(next(g.edge_iterator()))) elif g.weighted(): - correct_type = type(g.edge_iterator().next()[2]) + correct_type = type(next(g.edge_iterator())[2]) else: correct_type = int # Needed for rational curves. From 45f6fb5dde1cb2bf60acfa06b3abdbd184eb4ac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= Date: Tue, 12 Apr 2016 10:46:14 +0200 Subject: [PATCH 085/163] 16075: review --- src/sage/combinat/combinat.py | 10 +++++----- src/sage/combinat/partition.py | 2 -- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index a8897e09f2c..9a342077220 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -1553,7 +1553,7 @@ def __list_from_iterator(self): def __iterator_from_next(self): """ - An iterator to use when .first(x) and .next(x) are provided. + An iterator to use when the .first() and .next(x) methods are provided. EXAMPLES:: @@ -1670,18 +1670,18 @@ def __iter__(self): ... NotImplementedError: iterator called but not implemented """ - #Check if .first(x) and .next(x) are overridden in the subclass + # Check whether .first() and .next(x) are overridden in the subclass if ( self.first != self.__first_from_iterator and self.next != self.__next_from_iterator ): return self.__iterator_from_next() - #Check to see if .last() and .previous() are overridden in the subclass + # Check whether .last() and .previous() are overridden in the subclass elif ( self.last != self.__last_from_iterator and self.previous != self.__previous_from_iterator): return self.__iterator_from_previous() - #Check to see if .unrank() is overridden in the subclass + # Check whether .unrank() is overridden in the subclass elif self.unrank != self.__unrank_from_iterator: return self.__iterator_from_unrank() - #Finally, check to see if .list() is overridden in the subclass + # Check whether .list() is overridden in the subclass elif self.list != self.__list_from_iterator: return self.__iterator_from_list() else: diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 50393045e38..fc4ec33b1ad 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -1201,8 +1201,6 @@ def next(self): return self.parent()(next_p[:m]) - __next__ = next - def size(self): """ Return the size of ``self``. From e68cc70a4b71667bdf3057c87a87cd01deb51dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= Date: Tue, 12 Apr 2016 10:42:01 +0200 Subject: [PATCH 086/163] 16075: method renaming + comment to highlight that this is not following Python's protocol iterator, and thus not subject to Py2 -> Py3 incompatibility --- .../asymptotic/growth_group_cartesian.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/sage/rings/asymptotic/growth_group_cartesian.py b/src/sage/rings/asymptotic/growth_group_cartesian.py index 03de5dc5f43..26372689f13 100644 --- a/src/sage/rings/asymptotic/growth_group_cartesian.py +++ b/src/sage/rings/asymptotic/growth_group_cartesian.py @@ -751,14 +751,17 @@ def subfactors(F): 'no common parent was found, and ' 'splitting the factors was unsuccessful.' % (self, other, var)) - + # A wrapper around an iterator that stores additional intermediate data. + # This deviates slightly from the iterator protocol: + # At the end of the iteration the data is reset to None instead + # of raising a StopIteration. class it: def __init__(self, it): self.it = it self.var = None self.factors = None - def custom_next(self): + def next_custom(self): try: self.var, factors = next(self.it) self.factors = tuple(factors) @@ -773,24 +776,24 @@ def custom_next(self): newS = [] newO = [] - S.custom_next() - O.custom_next() + S.next_custom() + O.next_custom() while S.var is not None or O.var is not None: if S.var is not None and S.var < O.var: newS.extend(S.factors) newO.extend(S.factors) - S.custom_next() + S.next_custom() elif O.var is not None and S.var > O.var: newS.extend(O.factors) newO.extend(O.factors) - O.custom_next() + O.next_custom() else: SL, OL = pushout_univariate_factors(self, other, S.var, S.factors, O.factors) newS.extend(SL) newO.extend(OL) - S.custom_next() - O.custom_next() + S.next_custom() + O.next_custom() assert(len(newS) == len(newO)) From 4f4db614d6fa1911b1eca63f7a4a7da062cd477f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 12 Apr 2016 20:42:57 +0200 Subject: [PATCH 087/163] trac 16075 two details --- src/sage/categories/enumerated_sets.py | 2 +- src/sage/categories/examples/sets_cat.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/categories/enumerated_sets.py b/src/sage/categories/enumerated_sets.py index c5ef0e328c1..c9ef74b9520 100644 --- a/src/sage/categories/enumerated_sets.py +++ b/src/sage/categories/enumerated_sets.py @@ -217,7 +217,7 @@ def __iter__(self): [5, 6, 7] """ - # Check if .first(x) and .next(x) are overridden in the subclass + # Check if .first() and .next(x) are overridden in the subclass if ( self.first != self._first_from_iterator and self.next != self._next_from_iterator ): return self._iterator_from_next() diff --git a/src/sage/categories/examples/sets_cat.py b/src/sage/categories/examples/sets_cat.py index 943d576abb8..25a3726c82a 100644 --- a/src/sage/categories/examples/sets_cat.py +++ b/src/sage/categories/examples/sets_cat.py @@ -287,6 +287,7 @@ def next(self): sage: P = Sets().example("inherits") sage: p = P.an_element(); p + 47 sage: p.next() 53 From e1cca76c5d861f21a309056aee9f4b09472a050d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 12 Apr 2016 20:47:35 +0200 Subject: [PATCH 088/163] trac 20429 minor tweaks --- src/sage/categories/examples/sets_cat.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/categories/examples/sets_cat.py b/src/sage/categories/examples/sets_cat.py index 948e01ec9b3..1f30a623867 100644 --- a/src/sage/categories/examples/sets_cat.py +++ b/src/sage/categories/examples/sets_cat.py @@ -268,7 +268,7 @@ def some_elements(self): class Element(Element): def is_prime(self): """ - Return whether ``self`` if a prime number + Return whether ``self`` is a prime number. EXAMPLES:: @@ -587,7 +587,7 @@ class PrimeNumbers_Facade(PrimeNumbers_Abstract): sage: z.parent() Integer Ring - The disadvantage of this implementation is that the elements don't know + The disadvantage of this implementation is that the elements do not know that they are prime, so that prime testing is slow:: sage: pf = Sets().example("facade").an_element() @@ -596,7 +596,7 @@ class PrimeNumbers_Facade(PrimeNumbers_Abstract): compared to the other implementations where prime testing is only done if needed during the construction of the element, and later on the elements - "knows" that they are prime:: + "know" that they are prime:: sage: pw = Sets().example("wrapper").an_element() sage: timeit("pw.is_prime()") # random From 2336cbb0bcf52c102c9ed4cd2a9005129df56f1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= Date: Tue, 12 Apr 2016 21:33:19 +0200 Subject: [PATCH 089/163] 20410: reimplemented tab completion on top of ExtraTabCompletion, per suggestion of the reviewer --- src/sage/symbolic/units.py | 42 +++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/sage/symbolic/units.py b/src/sage/symbolic/units.py index 8bdd8747e73..a0e928f4085 100644 --- a/src/sage/symbolic/units.py +++ b/src/sage/symbolic/units.py @@ -92,6 +92,7 @@ # Sage library from ring import SR from expression import Expression +from sage.interfaces.tab_completion import ExtraTabCompletion ############################################################################### # Unit conversions dictionary. @@ -1032,7 +1033,7 @@ class that derives from symbolic expression, and has a specialized """ return UnitExpression(SR, SR.var(name)) -class Units: +class Units(ExtraTabCompletion): """ A collection of units of some type. @@ -1101,37 +1102,32 @@ def __cmp__(self, other): return cmp(type(self), type(other)) return cmp((self.__name, self.__data), (other.__name, other.__data)) - def trait_names(self): + def _tab_completion(self): """ - Return completions of this unit objects. This is used by the - Sage command line and notebook to create the list of method - names. + Return tab completions. - EXAMPLES:: - - sage: units.area.trait_names() - ['acre', 'are', 'barn', 'hectare', 'rood', 'section', 'square_chain', 'square_meter', 'township'] - """ - return sorted([x for x in self.__data.keys() if '/' not in x]) + This complements the usual content of :func:`dir`, with the + list of the names of the unit collections (resp. units) for + :obj:`units` (resp. its subcollections), in particular for tab + completion purposes. - def __dir__(self): - """ - Return the list of names of the attributes of ``self``. - - This complements the usual content of ``dir``, with the names - of the units (or unit collections) of ``self``. - - This enables tab completion on ``units`` and its - subcollections. + .. SEEALSO:: :class:`ExtraTabCompletion` EXAMPLES:: + sage: units.area._tab_completion() + ['acre', 'are', 'barn', 'hectare', 'rood', 'section', 'square_chain', 'square_meter', 'township'] + sage: units._tab_completion() + ['acceleration', ..., 'volume'] + sage: units.force._tab_completion() + ['dyne', ..., 'ton_force'] + sage: dir(units) - ['_Units__data', '_Units__name', '_Units__units', 'acceleration', ..., 'volume'] + ['_Units__data', ..., 'acceleration', ..., 'volume'] sage: dir(units.force) - ['_Units__data', '_Units__name', '_Units__units', 'dyne', ..., 'ton_force'] + ['_Units__data', ..., 'dyne', ..., 'ton_force'] """ - return self.__dict__.keys() + self.__data.keys() + return sorted([x for x in self.__data.keys() if '/' not in x]) def __getattr__(self, name): """ From e344fa54a33c1f8da38cfd021e27e7f9f3be9992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= Date: Tue, 12 Apr 2016 21:56:51 +0200 Subject: [PATCH 090/163] 20410: misc docstring syntax fixes --- src/sage/symbolic/units.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/sage/symbolic/units.py b/src/sage/symbolic/units.py index a0e928f4085..25fb4bea244 100644 --- a/src/sage/symbolic/units.py +++ b/src/sage/symbolic/units.py @@ -930,11 +930,11 @@ def vars_in_str(s): INPUT: - - `s` -- string + - ``s`` -- a string OUTPUT: - - list of strings (unit names) + - a list of strings (unit names) EXAMPLES:: @@ -951,11 +951,11 @@ def unit_derivations_expr(v): INPUT: - - `v` -- string, name of a unit type such as 'area', 'volume', etc. + - ``v`` -- a string, name of a unit type such as 'area', 'volume', etc. OUTPUT: - - symbolic expression + - a symbolic expression EXAMPLES:: @@ -1017,11 +1017,11 @@ class that derives from symbolic expression, and has a specialized INPUT: - - ``name`` -- string + - ``name`` -- a string OUTPUT: - - UnitExpression + - a :class:`UnitExpression` EXAMPLES:: @@ -1183,11 +1183,11 @@ def unitdocs(unit): INPUT: - - ``unit`` + - ``unit`` -- a unit OUTPUT: - - ``string`` + - a string EXAMPLES:: @@ -1214,11 +1214,11 @@ def is_unit(s): INPUT: - - `s` -- an object + - ``s`` -- an object OUTPUT: - - ``bool`` + - a boolean EXAMPLES:: @@ -1246,13 +1246,13 @@ def convert(expr, target): INPUT: - - `expr` -- the symbolic expression converting from + - ``expr`` -- the symbolic expression converting from - - `target` -- (default None) the symbolic expression converting to + - ``target`` -- (default None) the symbolic expression converting to OUTPUT: - - `symbolic expression` + - a symbolic expression EXAMPLES:: @@ -1338,11 +1338,11 @@ def base_units(unit): INPUT: - - ``unit`` + - ``unit`` -- a unit OUTPUT: - - `symbolic expression` + - a symbolic expression EXAMPLES:: @@ -1388,12 +1388,12 @@ def convert_temperature(expr, target): INPUT: - - `expr` -- a unit of temperature - - `target` -- a units of temperature + - ``expr`` -- a unit of temperature + - ``target`` -- a units of temperature OUTPUT: - - `symbolic expression` + - a symbolic expression EXAMPLES:: From de898ac2dcb90fa40b426089477df41def96e445 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 27 Mar 2016 13:15:57 -0700 Subject: [PATCH 091/163] InteractiveLPBackend: Use standard form transformation on optimal solution --- .../backends/interactivelp_backend.pxd | 1 + .../backends/interactivelp_backend.pyx | 21 +++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/sage/numerical/backends/interactivelp_backend.pxd b/src/sage/numerical/backends/interactivelp_backend.pxd index d3a8f2a0e49..7347e2ef7e7 100644 --- a/src/sage/numerical/backends/interactivelp_backend.pxd +++ b/src/sage/numerical/backends/interactivelp_backend.pxd @@ -15,6 +15,7 @@ cdef class InteractiveLPBackend(GenericBackend): cdef object prob_name cdef object lp_std_form + cdef object std_form_transformation cdef object final_dictionary cdef int verbosity diff --git a/src/sage/numerical/backends/interactivelp_backend.pyx b/src/sage/numerical/backends/interactivelp_backend.pyx index 578c77d328f..1b5a6e3e6b6 100644 --- a/src/sage/numerical/backends/interactivelp_backend.pyx +++ b/src/sage/numerical/backends/interactivelp_backend.pyx @@ -52,14 +52,17 @@ cdef class InteractiveLPBackend: This backend can work with irrational algebraic numbers:: sage: poly = polytopes.dodecahedron(base_ring=AA) - sage: lp = poly.to_linear_program(solver='InteractiveLP') - sage: b = lp.get_backend() - sage: for k in range(3): b.variable_lower_bound(k, 0) - sage: b.set_objective([1, 1, 1]) + sage: lp, x = poly.to_linear_program(solver='InteractiveLP', return_variable=True) + sage: lp.set_objective(x[0] + x[1] + x[2]) sage: lp.solve() 2.291796067500631? - sage: [b.get_variable_value(k) for k in range(3)] + sage: lp.get_values(x[0], x[1], x[2]) [0.763932022500211?, 0.763932022500211?, 0.763932022500211?] + sage: lp.set_objective(x[0] - x[1] - x[2]) + sage: lp.solve() + 2.291796067500631? + sage: lp.get_values(x[0], x[1], x[2]) + [0.763932022500211?, -0.763932022500211?, -0.763932022500211?] """ if base_ring is None: @@ -633,7 +636,8 @@ cdef class InteractiveLPBackend: """ ## FIXME: standard_form should allow to pass slack names (which we would take from row_names). ## FIXME: Perhaps also pass the problem name as objective name - lp_std_form = self.lp_std_form = self.lp.standard_form() + lp_std_form, transformation = self.lp.standard_form(transformation=True) + self.lp_std_form, self.std_form_transformation = lp_std_form, transformation output = lp_std_form.run_revised_simplex_method() ## FIXME: Display output as a side effect if verbosity is high enough d = self.final_dictionary = lp_std_form.final_revised_dictionary() @@ -701,9 +705,8 @@ cdef class InteractiveLPBackend: sage: p.get_variable_value(1) 3/2 """ - if str(self.lp.decision_variables()[variable]) != str(self.lp_std_form.decision_variables()[variable]): - raise NotImplementedError("Undoing the standard-form transformation is not implemented") - return self.final_dictionary.basic_solution()[variable] + solution = self.std_form_transformation(self.final_dictionary.basic_solution()) + return solution[variable] cpdef int ncols(self): """ From b8ea5a285c9890ff3d1c992576da9f23d9f94d81 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 29 Mar 2016 20:31:44 -0700 Subject: [PATCH 092/163] InteractiveLPBackend: Use InteractiveLPProblem's objective_constant_term --- .../backends/interactivelp_backend.pyx | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/sage/numerical/backends/interactivelp_backend.pyx b/src/sage/numerical/backends/interactivelp_backend.pyx index 1b5a6e3e6b6..a8a322aa697 100644 --- a/src/sage/numerical/backends/interactivelp_backend.pyx +++ b/src/sage/numerical/backends/interactivelp_backend.pyx @@ -77,8 +77,6 @@ cdef class InteractiveLPBackend: else: self.set_sense(-1) - self.obj_constant_term = 0 - self.row_names = [] cpdef base_ring(self): @@ -199,7 +197,7 @@ cdef class InteractiveLPBackend: sage: p.objective_coefficient(1) 1 """ - A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR() + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() cdef int vtype = int(binary) + int(continuous) + int(integer) if vtype == 0: continuous = True @@ -222,7 +220,7 @@ cdef class InteractiveLPBackend: x = tuple(x) + (name,) self.lp = InteractiveLPProblem(A, b, c, x, constraint_types, variable_types, - problem_type, ring) + problem_type, ring, objective_constant_term=d) return self.ncols() - 1 cpdef int add_variables(self, int n, lower_bound=0, upper_bound=None, binary=False, continuous=True, integer=False, obj=None, names=None) except -1: @@ -301,7 +299,7 @@ cdef class InteractiveLPBackend: else: raise NotImplementedError() - def _AbcxCVPR(self): + def _AbcxCVPRd(self): """ Retrieve all problem data from the LP. @@ -309,15 +307,16 @@ cdef class InteractiveLPBackend: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "InteractiveLP") - sage: p._AbcxCVPR() - ([], (), (), (), (), (), 'max', Algebraic Real Field) + sage: p._AbcxCVPRd() + ([], (), (), (), (), (), 'max', Algebraic Real Field, 0) """ A, b, c, x = self.lp.Abcx() constraint_types = self.lp.constraint_types() variable_types = self.lp.variable_types() problem_type = self.lp.problem_type() base_ring = self.lp.base_ring() - return A, b, c, x, constraint_types, variable_types, problem_type, base_ring + d = self.lp.objective_constant_term() + return A, b, c, x, constraint_types, variable_types, problem_type, base_ring, d cpdef set_sense(self, int sense): """ @@ -340,14 +339,14 @@ cdef class InteractiveLPBackend: sage: p.is_maximization() False """ - A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR() + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() if sense == +1: problem_type = "max" else: problem_type = "min" self.lp = InteractiveLPProblem(A, b, c, x, constraint_types, variable_types, - problem_type, ring) + problem_type, ring, objective_constant_term=d) cpdef objective_coefficient(self, int variable, coeff=None): """ @@ -375,12 +374,12 @@ cdef class InteractiveLPBackend: if coeff is None: return self.lp.objective_coefficients()[variable] else: - A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR() + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() c = list(c) c[variable] = coeff self.lp = InteractiveLPProblem(A, b, c, x, constraint_types, variable_types, - problem_type, ring) + problem_type, ring, objective_constant_term=d) cpdef set_objective(self, list coeff, d = 0): """ @@ -426,12 +425,11 @@ cdef class InteractiveLPBackend: -47/5 """ - A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR() + A, b, _, x, constraint_types, variable_types, problem_type, ring, _ = self._AbcxCVPRd() c = coeff self.lp = InteractiveLPProblem(A, b, c, x, constraint_types, variable_types, - problem_type, ring) - self.obj_constant_term = d + problem_type, ring, objective_constant_term=d) cpdef set_verbosity(self, int level): """ @@ -473,13 +471,13 @@ cdef class InteractiveLPBackend: sage: p.get_values([x,y]) [0, 3] """ - A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR() + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() A = A.delete_rows((i,)) b = list(b); del b[i] constraint_types=list(constraint_types); del constraint_types[i] self.lp = InteractiveLPProblem(A, b, c, x, constraint_types, variable_types, - problem_type, ring) + problem_type, ring, objective_constant_term=d) cpdef add_linear_constraint(self, coefficients, lower_bound, upper_bound, name=None): """ @@ -514,7 +512,7 @@ cdef class InteractiveLPBackend: sage: p.row_name(1) 'foo' """ - A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR() + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() if lower_bound is None: if upper_bound is None: raise ValueError("At least one of lower_bound and upper_bound must be provided") @@ -540,7 +538,7 @@ cdef class InteractiveLPBackend: self.lp = InteractiveLPProblem(A, b, c, x, constraint_types, variable_types, - problem_type, ring) + problem_type, ring, objective_constant_term=d) cpdef add_col(self, list indices, list coeffs): @@ -678,7 +676,7 @@ cdef class InteractiveLPBackend: v = d.objective_value() if self.lp_std_form.is_negative(): v = - v - return self.obj_constant_term + v + return v cpdef get_variable_value(self, int variable): """ @@ -1030,12 +1028,12 @@ cdef class InteractiveLPBackend: else: if value != bounds[1]: bounds = (bounds[0], value) - A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR() + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() variable_types = list(variable_types) variable_types[index] = self._variable_type_from_bounds(*bounds) self.lp = InteractiveLPProblem(A, b, c, x, constraint_types, variable_types, - problem_type, ring) + problem_type, ring, objective_constant_term=d) cpdef variable_lower_bound(self, int index, value = False): """ @@ -1074,12 +1072,12 @@ cdef class InteractiveLPBackend: else: if value != bounds[0]: bounds = (value, bounds[1]) - A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR() + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() variable_types = list(variable_types) variable_types[index] = self._variable_type_from_bounds(*bounds) self.lp = InteractiveLPProblem(A, b, c, x, constraint_types, variable_types, - problem_type, ring) + problem_type, ring, objective_constant_term=d) cpdef bint is_variable_basic(self, int index): """ From d84b5bd22120173b29d926541751f9e152a87d02 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 10 Apr 2016 20:04:37 -0700 Subject: [PATCH 093/163] InteractiveLPBackend.objective_constant_term: New --- .../backends/interactivelp_backend.pyx | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/sage/numerical/backends/interactivelp_backend.pyx b/src/sage/numerical/backends/interactivelp_backend.pyx index a8a322aa697..923574e5c4b 100644 --- a/src/sage/numerical/backends/interactivelp_backend.pyx +++ b/src/sage/numerical/backends/interactivelp_backend.pyx @@ -381,6 +381,32 @@ cdef class InteractiveLPBackend: constraint_types, variable_types, problem_type, ring, objective_constant_term=d) + cpdef objective_constant_term(self, d=None): + """ + Set or get the constant term in the objective function + + INPUT: + + - ``d`` (double) -- its coefficient. If `None` (default), return the current value. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.objective_constant_term() + 0 + sage: p.objective_constant_term(42) + sage: p.objective_constant_term() + 42 + """ + if d is None: + return self.lp.objective_constant_term() + else: + A, b, c, x, constraint_types, variable_types, problem_type, ring, _ = self._AbcxCVPRd() + self.lp = InteractiveLPProblem(A, b, c, x, + constraint_types, variable_types, + problem_type, ring, objective_constant_term=d) + cpdef set_objective(self, list coeff, d = 0): """ Set the objective function. From c8fa4b0a9a706f79786f8ee94e0c955e3cabae51 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 12 Apr 2016 17:03:54 -0700 Subject: [PATCH 094/163] InteractiveLPBackend: Change default base_ring to QQ --- src/sage/numerical/backends/generic_backend.pyx | 4 ++-- src/sage/numerical/backends/interactivelp_backend.pyx | 8 ++++---- src/sage/numerical/mip.pyx | 3 ++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 28d443647c9..aef0cb0c535 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -1169,7 +1169,7 @@ def default_mip_solver(solver = None): - ``InteractiveLPProblem`` (``solver="InteractiveLP"``). A didactical implementation of the revised simplex method in Sage. It works over - any exact ordered field, the default is ``AA``. + any exact ordered field, the default is ``QQ``. ``solver`` should then be equal to one of ``"GLPK"``, ``"Coin"``, ``"CPLEX"``, ``"CVXOPT"``, ``"Gurobi"``, ``"PPL"`, or @@ -1294,7 +1294,7 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None, ba - ``InteractiveLPProblem`` (``solver="InteractiveLP"``). A didactical implementation of the revised simplex method in Sage. It works over - any exact ordered field, the default is ``AA``. + any exact ordered field, the default is ``QQ``. ``solver`` should then be equal to one of ``"GLPK"``, ``"Coin"``, ``"CPLEX"``, ``"CVXOPT"``,``"Gurobi"``, ``"PPL"``, ``"InteractiveLP"``, diff --git a/src/sage/numerical/backends/interactivelp_backend.pyx b/src/sage/numerical/backends/interactivelp_backend.pyx index 923574e5c4b..4d6daf99e73 100644 --- a/src/sage/numerical/backends/interactivelp_backend.pyx +++ b/src/sage/numerical/backends/interactivelp_backend.pyx @@ -66,8 +66,8 @@ cdef class InteractiveLPBackend: """ if base_ring is None: - from sage.rings.all import AA - base_ring = AA + from sage.rings.all import QQ + base_ring = QQ self.lp = InteractiveLPProblem([], [], [], base_ring=base_ring) self.set_verbosity(0) @@ -92,7 +92,7 @@ cdef class InteractiveLPBackend: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "InteractiveLP") sage: p.base_ring() - Algebraic Real Field + Rational Field """ return self.lp.base_ring() @@ -308,7 +308,7 @@ cdef class InteractiveLPBackend: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "InteractiveLP") sage: p._AbcxCVPRd() - ([], (), (), (), (), (), 'max', Algebraic Real Field, 0) + ([], (), (), (), (), (), 'max', Rational Field, 0) """ A, b, c, x = self.lp.Abcx() constraint_types = self.lp.constraint_types() diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 65716aa6bca..862e81c22b8 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -598,7 +598,8 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p = MixedIntegerLinearProgram(solver='ppl') sage: p.base_ring() Rational Field - sage: p = MixedIntegerLinearProgram(solver='InteractiveLP') + sage: from sage.rings.all import AA + sage: p = MixedIntegerLinearProgram(solver='InteractiveLP', base_ring=AA) sage: p.base_ring() Algebraic Real Field sage: d = polytopes.dodecahedron() From ce60dd00e2797772efc616a8f0b0f316a0664095 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Tue, 12 Apr 2016 17:54:04 +0200 Subject: [PATCH 095/163] typos --- src/sage/rings/polynomial/polynomial_element.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 3c8857776c3..a56f80c37c3 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -5270,7 +5270,7 @@ cdef class Polynomial(CommutativeAlgebraElement): r""" Compute the dispersion set of two polynomials. - The dispersion set of `p` and `q` is the set of nonnegative integers + The dispersion set of `f` and `g` is the set of nonnegative integers `n` such that `f(x + n)` and `g(x)` have a nonconstant common factor. When ``other`` is ``None``, compute the auto-dispersion set of @@ -5325,7 +5325,7 @@ cdef class Polynomial(CommutativeAlgebraElement): r""" Compute the dispersion of a pair of polynomials. - The dispersion of `p` and `q` is the largest nonnegative integer `n` + The dispersion of `f` and `g` is the largest nonnegative integer `n` such that `f(x + n)` and `g(x)` have a nonconstant common factor. When ``other`` is ``None``, compute the auto-dispersion of ``self``, From a8ee09a95a6bf7b80564dd644c7d82b609b790c7 Mon Sep 17 00:00:00 2001 From: Marc Masdeu Date: Wed, 13 Apr 2016 12:07:01 +0100 Subject: [PATCH 096/163] Added functionality in expect.py to set the server and command. --- src/sage/interfaces/expect.py | 71 +++++++++++++++++++++++------------ src/sage/interfaces/magma.py | 1 - 2 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/sage/interfaces/expect.py b/src/sage/interfaces/expect.py index 216a1ffb93f..f718867aa00 100644 --- a/src/sage/interfaces/expect.py +++ b/src/sage/interfaces/expect.py @@ -132,33 +132,13 @@ def __init__(self, name, prompt, command=None, server=None, Interface.__init__(self, name) self.__is_remote = False self.__remote_cleaner = remote_cleaner - if command is None: - command = name - if server is not None: - if ulimit: - command = "sage-native-execute ssh -t %s 'ulimit %s; %s'"%(server, ulimit, command) - else: - command = "sage-native-execute ssh -t %s '%s'"%(server, command) - self.__is_remote = True - eval_using_file_cutoff = 0 # don't allow this! - if verbose_start: - print "Using remote server" - print command - self._server = server - if server_tmpdir is None: - # TO DO: Why default to /tmp/? Might be better to use the expect process itself to get a tmp folder - print "No remote temporary directory (option server_tmpdir) specified, using /tmp/ on "+server - self.__remote_tmpdir = "/tmp/" - else: - self.__remote_tmpdir = server_tmpdir - else: - self._server = None - self.__do_cleaner = do_cleaner + self._expect = None self._eval_using_file_cutoff = eval_using_file_cutoff - self.__command = command + self.__verbose_start = verbose_start + self.set_server_and_command(server, command, server_tmpdir, ulimit) + self.__do_cleaner = do_cleaner self._prompt = prompt self._restart_on_ctrlc = restart_on_ctrlc - self.__verbose_start = verbose_start if path is not None: self.__path = os.path.abspath(path) elif script_subdirectory is None: @@ -169,7 +149,6 @@ def __init__(self, name, prompt, command=None, server=None, raise EnvironmentError("path %r does not exist" % self.__path) self.__initialized = False self.__seq = -1 - self._expect = None self._session_number = 0 self.__init_code = init_code @@ -185,6 +164,48 @@ def __init__(self, name, prompt, command=None, server=None, self._available_vars = [] self._terminal_echo = terminal_echo + def set_server_and_command(self,server = None,command = None, server_tmpdir = None, ulimit = None): + """ + Changes the server and the command to use for this interface. This raises a Runtime error + if the interface is already started. + """ + if self._expect: + raise RuntimeError("interface has already started") + if command is None: + command = self.name() + self._server = server + if server is not None: + if ulimit: + command = "sage-native-execute ssh -t %s 'ulimit %s; %s'"%(server, ulimit, command) + else: + command = "sage-native-execute ssh -t %s '%s'"%(server, command) + self.__is_remote = True + self._eval_using_file_cutoff = 0 # don't allow this! + if self.__verbose_start: + print "Using remote server" + print command + if server_tmpdir is None: + # TO DO: Why default to /tmp/? Might be better to use the expect process itself to get a tmp folder + print "No remote temporary directory (option server_tmpdir) specified, using /tmp/ on "+server + self.__remote_tmpdir = "/tmp/" + else: + self.__remote_tmpdir = server_tmpdir + else: + self.__is_remote = False + self.__command = command + + def server(self): + """ + Returns the server used in this interface. + """ + return self._server + + def command(self): + """ + Returns the command used in this interface. + """ + return self.__command + def _get(self, wait=0.1, alternate_prompt=None): if self._expect is None: self._start() diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index 00e36787e21..80f8c54e77b 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -2791,7 +2791,6 @@ def magma_version(): sage: magma_version() # random, optional - magma ((2, 14, 9), 'V2.14-9') """ - global magma t = tuple([int(n) for n in magma.eval('GetVersion()').split()]) return t, 'V%s.%s-%s'%t From 48e223529cbad63c3497a5ae3aec46fdf75de5c8 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 7 Apr 2016 12:52:33 +0200 Subject: [PATCH 097/163] Patch to mpfr required for it to get it building on Cygwin again --- .../pkgs/mpfr/patches/mpfr-3.1.4-1.src.patch | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/build/pkgs/mpfr/patches/mpfr-3.1.4-1.src.patch b/build/pkgs/mpfr/patches/mpfr-3.1.4-1.src.patch index 1d8d0c41b0d..68c294586a2 100644 --- a/build/pkgs/mpfr/patches/mpfr-3.1.4-1.src.patch +++ b/build/pkgs/mpfr/patches/mpfr-3.1.4-1.src.patch @@ -1,11 +1,22 @@ ---- mpfr-3.1.4/src/mpfr-impl.h 2016-03-06 12:33:04.000000000 +0100 -+++ mpfr-3.1.4/src/mpfr-impl.h 2016-03-06 17:25:16.341090700 +0100 -@@ -257,6 +257,8 @@ typedef struct __gmpfr_cache_s *mpfr_cac - # if defined(_WIN32) - # error "Both __unix__ and _WIN32 are defined" - # endif -+#endif -+#if (defined(__unix__) && !defined(__CYGWIN__)) - # if __GMP_LIBGMP_DLL - # error "__unix__ is defined and __GMP_LIBGMP_DLL is true" - # endif +--- trunk/src/mpfr-impl.h 2016/04/08 15:55:03 10257 ++++ trunk/src/mpfr-impl.h 2016/04/08 23:17:07 10260 +@@ -208,19 +208,6 @@ + # define MPFR_WIN_THREAD_SAFE_DLL 1 + #endif + +-/* Detect some possible inconsistencies under Unix. */ +-#if defined(__unix__) +-# if defined(_WIN32) +-# error "Both __unix__ and _WIN32 are defined" +-# endif +-# if __GMP_LIBGMP_DLL +-# error "__unix__ is defined and __GMP_LIBGMP_DLL is true" +-# endif +-# if defined(MPFR_WIN_THREAD_SAFE_DLL) +-# error "Both __unix__ and MPFR_WIN_THREAD_SAFE_DLL are defined" +-# endif +-#endif +- + #if defined(__MPFR_WITHIN_MPFR) || !defined(MPFR_WIN_THREAD_SAFE_DLL) + extern MPFR_THREAD_ATTR mpfr_flags_t __gmpfr_flags; + extern MPFR_THREAD_ATTR mpfr_exp_t __gmpfr_emin; From 88561528acf90120d298a14d21062ee7ede2600b Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Tue, 12 Apr 2016 11:45:55 +0200 Subject: [PATCH 098/163] Added a brief explanatory comment to the patch. --- build/pkgs/mpfr/patches/mpfr-3.1.4-1.src.patch | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/pkgs/mpfr/patches/mpfr-3.1.4-1.src.patch b/build/pkgs/mpfr/patches/mpfr-3.1.4-1.src.patch index 68c294586a2..74ec30faf63 100644 --- a/build/pkgs/mpfr/patches/mpfr-3.1.4-1.src.patch +++ b/build/pkgs/mpfr/patches/mpfr-3.1.4-1.src.patch @@ -1,3 +1,6 @@ +Patch from upstream needed to fix an error when compiling on Cygwin +See http://trac.sagemath.org/ticket/20423 for discussion and link to +the original upstream patch. --- trunk/src/mpfr-impl.h 2016/04/08 15:55:03 10257 +++ trunk/src/mpfr-impl.h 2016/04/08 23:17:07 10260 @@ -208,19 +208,6 @@ From 8f0c777e13f768ae343816058e5c9a9a7d69e743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9r=C3=A9nice=20Delcroix-Oger?= Date: Wed, 13 Apr 2016 14:52:06 +0200 Subject: [PATCH 099/163] I add some details one the documentation (especially the definition of a branch) --- src/sage/combinat/binary_tree.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/binary_tree.py b/src/sage/combinat/binary_tree.py index 0de58044d96..df77dba37c2 100644 --- a/src/sage/combinat/binary_tree.py +++ b/src/sage/combinat/binary_tree.py @@ -2123,8 +2123,12 @@ def hook_number(self): r""" Return the number of hooks. + Recalling that a branch is a path from a vertex of the tree to a leaf, + the leftmost (resp. rightmost) branch of a vertex `v` is the branch from + `v` made only of left (resp. right) edges. + The hook of a vertex `v` is a set of vertices formed by the - union of `{v}`, its leftmost and rightmost branches. + union of `{v}`, and the vertices of its leftmost and rightmost branches. There is a unique way to partition the set of vertices in hooks. The number of hooks in such a partition is the hook number of the tree. @@ -2173,8 +2177,15 @@ def hook_number(self): def twisting_number(self): r""" - Return a pair (number of straight left branches, number of straight - right branches). + Return a pair (number of maximal left branches, number of maximal right + branches). + + Recalling that a branch of a vertex `v` is a path from a vertex of the + tree to a leaf, a left (resp. right) branch is a branch made only of + left (resp. right) edges. The length of a branch is the number of edges + composing it. A left (resp. right) branch is maximal if it is not + included in a strictly longer left (resp. right) branch. + OUTPUT : From e58fcf5fb0bacdb69cd1e534bddcc12d63ab41d1 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 13 Apr 2016 13:56:40 -0500 Subject: [PATCH 100/163] Fixing RSK_inverse due to dictionaries not being ordered. --- src/sage/combinat/rsk.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/rsk.py b/src/sage/combinat/rsk.py index c708fe60921..c89f3540121 100644 --- a/src/sage/combinat/rsk.py +++ b/src/sage/combinat/rsk.py @@ -555,6 +555,16 @@ def RSK_inverse(p, q, output='array', insertion='RSK'): Traceback (most recent call last): ... ValueError: p(=[[1, 2, 3]]) and q(=[[1, 2]]) must have the same shape + + Check that :trac:`20430` is fixed:: + + sage: RSK([1,1,1,1,1,1,1,2,2,2,3], [1,1,1,1,1,1,3,2,2,2,1]) + [[[1, 1, 1, 1, 1, 1, 1, 2, 2], [2], [3]], + [[1, 1, 1, 1, 1, 1, 1, 2, 2], [2], [3]]] + sage: t = SemistandardTableau([[1, 1, 1, 1, 1, 1, 1, 2, 2], [2], [3]]) + sage: RSK_inverse(t, t, 'array') + [[1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3], + [1, 1, 1, 1, 1, 1, 3, 2, 2, 2, 1]] """ if insertion == 'hecke': return hecke_insertion_reverse(p, q, output) @@ -577,7 +587,8 @@ def RSK_inverse(p, q, output='array', insertion='RSK'): use_EG = (insertion == 'EG') - for i in reversed(d.values()): # Delete last entry from i-th row of p_copy + for key in sorted(d, reverse=True): # Delete last entry from i-th row of p_copy + i = d[key] x = p_copy[i].pop() # Always the right-most entry for row in reversed(p_copy[:i]): y_pos = bisect_left(row,x) - 1 @@ -626,8 +637,9 @@ def RSK_inverse(p, q, output='array', insertion='RSK'): #d is now a double family such that for every integers k and j, #the value d[k][j] is the row i such that the (i, j)-th cell of #q is filled with k. - for value, row_dict in reversed(d.items()): - for i in reversed(row_dict.values()): + for value, row_dict in sorted(d.items(), reverse=True, key=lambda x: x[0]): + for key in sorted(row_dict, reverse=True): + i = row_dict[key] x = p_copy[i].pop() # Always the right-most entry for row in reversed(p_copy[:i]): y = bisect_left(row,x) - 1 From 8a28c1a382438c330b8a17297961605f4f3cd9c8 Mon Sep 17 00:00:00 2001 From: Marc Masdeu Date: Wed, 13 Apr 2016 21:07:38 +0100 Subject: [PATCH 101/163] Added examples. Moved environment variables to expect. --- src/sage/interfaces/expect.py | 40 ++++++++++++++++++++++++++++++++--- src/sage/interfaces/magma.py | 10 +-------- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/sage/interfaces/expect.py b/src/sage/interfaces/expect.py index f718867aa00..13470cd0981 100644 --- a/src/sage/interfaces/expect.py +++ b/src/sage/interfaces/expect.py @@ -130,6 +130,18 @@ def __init__(self, name, prompt, command=None, server=None, terminal_echo=True): Interface.__init__(self, name) + + # Read environment variables + env_name = 'SAGE_%s_{}'%self.name().upper() + import os + if server is None: + server = os.getenv(env_name.format('SERVER')) + if server_tmpdir is None: + server_tmpdir = os.getenv(env_name.format('TMPDIR')) + if command is None: + command = os.getenv(env_name.format('COMMAND')) + if script_subdirectory is None: + script_subdirectory = os.getenv(env_name.format('SCRIPT_SUBDIRECTORY')) self.__is_remote = False self.__remote_cleaner = remote_cleaner self._expect = None @@ -164,10 +176,19 @@ def __init__(self, name, prompt, command=None, server=None, self._available_vars = [] self._terminal_echo = terminal_echo - def set_server_and_command(self,server = None,command = None, server_tmpdir = None, ulimit = None): + def set_server_and_command(self, server=None, command=None, server_tmpdir=None, ulimit=None): """ - Changes the server and the command to use for this interface. This raises a Runtime error - if the interface is already started. + Changes the server and the command to use for this interface. + This raises a Runtime error if the interface is already started. + + EXAMPLES:: + + sage: magma.set_server_and_command(server = 'remote', command = 'mymagma') # indirect doctest + No remote temporary directory (option server_tmpdir) specified, using /tmp/ on remote + sage: magma.server() + 'remote' + sage: magma.command() + "sage-native-execute ssh -t remote 'mymagma'" """ if self._expect: raise RuntimeError("interface has already started") @@ -197,12 +218,25 @@ def set_server_and_command(self,server = None,command = None, server_tmpdir = No def server(self): """ Returns the server used in this interface. + + EXAMPLES:: + + sage: magma.set_server_and_command(server = 'remote') + No remote temporary directory (option server_tmpdir) specified, using /tmp/ on remote + sage: magma.server() # indirect doctest + 'remote' """ return self._server def command(self): """ Returns the command used in this interface. + + EXAMPLES:: + + sage: magma.set_server_and_command(command = 'magma-2.19') + sage: magma.command() # indirect doctest + 'magma-2.19' """ return self.__command diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index 80f8c54e77b..643c991bf66 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -332,16 +332,8 @@ def __init__(self, script_subdirectory=None, # Obtain the parameters from the environment, to allow the magma = Magma() phrase # to work with non-default parameters. - import os - if server is None: - server = os.getenv('SAGE_MAGMA_SERVER') - if server_tmpdir is None: - server_tmpdir = os.getenv('SAGE_MAGMA_SERVER_TMPDIR') - if command is None: - command = os.getenv('SAGE_MAGMA_COMMAND') - if script_subdirectory is None: - script_subdirectory = os.getenv('SAGE_MAGMA_SCRIPT_SUBDIRECTORY') if seed is None: + import os seed = os.getenv('SAGE_MAGMA_SEED') Expect.__init__(self, From c257d783d1ad63d4f513fc02ee2a614736afb407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9r=C3=A9nice=20Delcroix-Oger?= Date: Wed, 13 Apr 2016 22:45:37 +0200 Subject: [PATCH 102/163] Correction of spaces --- src/sage/combinat/binary_tree.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/binary_tree.py b/src/sage/combinat/binary_tree.py index df77dba37c2..a7c553cd3f7 100644 --- a/src/sage/combinat/binary_tree.py +++ b/src/sage/combinat/binary_tree.py @@ -2125,7 +2125,7 @@ def hook_number(self): Recalling that a branch is a path from a vertex of the tree to a leaf, the leftmost (resp. rightmost) branch of a vertex `v` is the branch from - `v` made only of left (resp. right) edges. + `v` made only of left (resp. right) edges. The hook of a vertex `v` is a set of vertices formed by the union of `{v}`, and the vertices of its leftmost and rightmost branches. @@ -2178,13 +2178,13 @@ def hook_number(self): def twisting_number(self): r""" Return a pair (number of maximal left branches, number of maximal right - branches). + branches). - Recalling that a branch of a vertex `v` is a path from a vertex of the - tree to a leaf, a left (resp. right) branch is a branch made only of - left (resp. right) edges. The length of a branch is the number of edges - composing it. A left (resp. right) branch is maximal if it is not - included in a strictly longer left (resp. right) branch. + Recalling that a branch of a vertex `v` is a path from a vertex of the + tree to a leaf, a left (resp. right) branch is a branch made only of + left (resp. right) edges. The length of a branch is the number of edges + composing it. A left (resp. right) branch is maximal if it is not + included in a strictly longer left (resp. right) branch. OUTPUT : From 623a2e20968e6f263fc0a9699a65ccdee4981850 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 6 Apr 2016 23:20:34 -0700 Subject: [PATCH 103/163] get_solver: Allow a callable as the solver argument --- src/sage/numerical/backends/generic_backend.pyx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 28d443647c9..1cad74802a2 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -1301,6 +1301,9 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None, ba or ``None``. If ``solver=None`` (default), the default solver is used (see ``default_mip_solver`` method). + ``solver`` can also be a callable, in which case it is called, + and its result is returned. + - ``base_ring`` -- If not ``None``, request a solver that works over this (ordered) field. If ``base_ring`` is not a field, its fraction field is used. @@ -1348,6 +1351,13 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None, ba sage: p.base_ring() Rational Field + + Passing a callable as the 'solver': + + sage: from sage.numerical.backends.glpk_backend import GLPKBackend + sage: p = get_solver(GLPKBackend); p + <...sage.numerical.backends.glpk_backend.GLPKBackend...> + """ if solver is None: @@ -1368,6 +1378,12 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None, ba if solver == "Coin" and constraint_generation: solver = "Glpk" + elif callable(solver): + kwds = {} + if base_ring is not None: + kwds['base_ring']=base_ring + return solver(**kwds) + else: solver = solver.capitalize() From 09377ac96d08efa39b9148a690fe19bd33b8a07a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 10 Apr 2016 18:53:41 -0700 Subject: [PATCH 104/163] MixedIntegerLinearProgram.__copy__: Don't create a throw-away GLPK instance --- src/sage/numerical/mip.pyx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 65716aa6bca..09ee8849671 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -536,8 +536,11 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: q.number_of_constraints() 1 """ + def copying_solver(**kwdargs): + return ( self._backend).copy() + cdef MixedIntegerLinearProgram p = \ - MixedIntegerLinearProgram(solver="GLPK") + MixedIntegerLinearProgram(solver=copying_solver) try: p._variables = copy(self._variables) except AttributeError: @@ -554,7 +557,6 @@ cdef class MixedIntegerLinearProgram(SageObject): except AttributeError: pass - p._backend = ( self._backend).copy() return p def __getitem__(self, v): From de06e653e3b811acc80c1a07a696b847cce27b3d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 2 Apr 2016 00:27:48 -0700 Subject: [PATCH 105/163] Use TestSuite for testing MIP backends --- src/sage/numerical/backends/coin_backend.pyx | 8 ++++++++ src/sage/numerical/backends/generic_backend.pxd | 4 +++- src/sage/numerical/backends/generic_backend.pyx | 10 ++++++++++ src/sage/numerical/backends/glpk_backend.pyx | 7 +++++++ src/sage/numerical/backends/ppl_backend.pyx | 9 +++++++++ 5 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/sage/numerical/backends/coin_backend.pyx b/src/sage/numerical/backends/coin_backend.pyx index b994eadc836..40ff557df2d 100644 --- a/src/sage/numerical/backends/coin_backend.pyx +++ b/src/sage/numerical/backends/coin_backend.pyx @@ -25,6 +25,14 @@ from copy import copy cdef class CoinBackend(GenericBackend): + """ + TESTS:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "Coin") # optional - cbc + sage: TestSuite(p).run(skip="_test_pickling") # optional - cbc + """ + def __cinit__(self, maximization = True): """ Cython constructor diff --git a/src/sage/numerical/backends/generic_backend.pxd b/src/sage/numerical/backends/generic_backend.pxd index 53c3f26579e..4ba3f6714a9 100644 --- a/src/sage/numerical/backends/generic_backend.pxd +++ b/src/sage/numerical/backends/generic_backend.pxd @@ -5,7 +5,9 @@ # http://www.gnu.org/licenses/ ############################################################################## -cdef class GenericBackend: +from sage.structure.sage_object cimport SageObject + +cdef class GenericBackend (SageObject): cpdef int add_variable(self, lower_bound=*, upper_bound=*, binary=*, continuous=*, integer=*, obj=*, name=*) except -1 cpdef int add_variables(self, int, lower_bound=*, upper_bound=*, binary=*, continuous=*, integer=*, obj=*, names=*) except -1 cpdef set_variable_type(self, int variable, int vtype) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 28d443647c9..13449437188 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -481,6 +481,16 @@ cdef class GenericBackend: """ raise NotImplementedError() + def _test_add_linear_constraints(self, **options): + """ + Run tests on the method :meth:`.add_linear_constraints`. + """ + tester = self._tester(**options) + self.add_variables(5) + self.add_linear_constraints(5, None, 2) + tester.assertEqual(self.row(4), ([], [])) + tester.assertEqual(self.row_bounds(4), (None, 2.0)) + cpdef int solve(self) except -1: """ Solve the problem. diff --git a/src/sage/numerical/backends/glpk_backend.pyx b/src/sage/numerical/backends/glpk_backend.pyx index d8a833eb2ec..d148cbc33e5 100644 --- a/src/sage/numerical/backends/glpk_backend.pyx +++ b/src/sage/numerical/backends/glpk_backend.pyx @@ -34,6 +34,13 @@ include "cysignals/signals.pxi" cdef class GLPKBackend(GenericBackend): + """ + TESTS:: + + sage: p = MixedIntegerLinearProgram(solver="GLPK") + sage: TestSuite(p.get_backend()).run(skip="_test_pickling") + """ + def __cinit__(self, maximization = True): """ Constructor diff --git a/src/sage/numerical/backends/ppl_backend.pyx b/src/sage/numerical/backends/ppl_backend.pyx index c82691cdc52..ce1053c503b 100644 --- a/src/sage/numerical/backends/ppl_backend.pyx +++ b/src/sage/numerical/backends/ppl_backend.pyx @@ -25,6 +25,15 @@ from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational cdef class PPLBackend(GenericBackend): + + """ + TESTS:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "PPL") + sage: TestSuite(p).run(skip="_test_pickling") + """ + cdef object mip cdef list Matrix cdef list row_lower_bound From 052960c76172b193f95349e57c1a86fec603bd8a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 5 Apr 2016 20:57:08 -0700 Subject: [PATCH 106/163] Add comment --- src/sage/numerical/backends/generic_backend.pxd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/numerical/backends/generic_backend.pxd b/src/sage/numerical/backends/generic_backend.pxd index 4ba3f6714a9..8b44d336c63 100644 --- a/src/sage/numerical/backends/generic_backend.pxd +++ b/src/sage/numerical/backends/generic_backend.pxd @@ -7,6 +7,8 @@ from sage.structure.sage_object cimport SageObject +# We inherit from SageObject to make some testing infrastructure available. + cdef class GenericBackend (SageObject): cpdef int add_variable(self, lower_bound=*, upper_bound=*, binary=*, continuous=*, integer=*, obj=*, name=*) except -1 cpdef int add_variables(self, int, lower_bound=*, upper_bound=*, binary=*, continuous=*, integer=*, obj=*, names=*) except -1 From e70a97bf7dc960986e4829da28c5bd6cf1959b67 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 5 Apr 2016 23:40:16 -0700 Subject: [PATCH 107/163] More _test_... functions --- .../numerical/backends/generic_backend.pyx | 73 ++++++++++++++++++- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 13449437188..1247b2b5c02 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -134,6 +134,25 @@ cdef class GenericBackend: """ raise NotImplementedError() + def _test_add_variables(self, **options): + """ + Run tests on the method :meth:`.add_linear_constraints`. + + TESTS: + + Test, with an actual working backend, that the test works even if the problem + is not empty at the beginning:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver='GLPK') + sage: p.add_variables(2) + 1 + sage: p.add_linear_constraint([[0, 17], [1, 89]], None, 42) + sage: p._test_add_linear_constraint_vector() + """ + tester = self._tester(**options) + # Tests here + cpdef set_variable_type(self, int variable, int vtype): """ Set the type of a variable @@ -417,6 +436,32 @@ cdef class GenericBackend: upper_bound_d = None if upper_bound is None else upper_bound[d] self.add_linear_constraint(coefficients_d, lower_bound_d, upper_bound_d, name=name) + def _test_add_linear_constraint_vector(self, **options): + """ + Run tests on the method :meth:`.add_linear_constraints`. + + TESTS: + + Test, with an actual working backend, that the test works even if the problem + is not empty at the beginning:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver='GLPK') + sage: p.add_variables(2) + 1 + sage: p.add_linear_constraint([[0, 17], [1, 89]], None, 42) + sage: p._test_add_linear_constraint_vector() + """ + tester = self._tester(**options) + from sage.modules.all import vector + # Ensure there are at least 2 variables + self.add_variables(2) + coeffs = ([0, vector([1, 2])], [1, vector([2, 3])]) + upper = vector([5, 5]) + lower = vector([0, 0]) + self.add_linear_constraint_vector(2, coeffs, lower, upper, 'foo') + # FIXME: Tests here. Careful what we expect regarding ranged constraints with some solvers. + cpdef add_col(self, list indices, list coeffs): """ Add a column. @@ -484,12 +529,32 @@ cdef class GenericBackend: def _test_add_linear_constraints(self, **options): """ Run tests on the method :meth:`.add_linear_constraints`. + + TESTS: + + Test, with an actual working backend, that the test works even if the problem + is not empty at the beginning:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver='GLPK') + sage: p.add_variables(2) + 1 + sage: p.add_linear_constraint([[0, 17], [1, 89]], None, 42) + sage: p._test_add_linear_constraints() """ tester = self._tester(**options) - self.add_variables(5) - self.add_linear_constraints(5, None, 2) - tester.assertEqual(self.row(4), ([], [])) - tester.assertEqual(self.row_bounds(4), (None, 2.0)) + nrows_before = self.nrows() + nrows_added = 5 + self.add_linear_constraints(nrows_added, None, 2) + nrows_after = self.nrows() + # Test correct number of rows + tester.assertEqual(nrows_after, nrows_before+nrows_added, "Added the wrong number of rows") + # Test contents of the new rows are correct (sparse zero) + for i in range(nrows_before, nrows_after): + tester.assertEqual(self.row(i), ([], [])) + tester.assertEqual(self.row_bounds(i), (None, 2.0)) + # FIXME: Not sure if we should test that no new variables were added. + # Perhaps some backend may need to introduce explicit slack variables? cpdef int solve(self) except -1: """ From 0a76184c9840b107a2ff1d99299750f305b00c1e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 5 Apr 2016 23:54:52 -0700 Subject: [PATCH 108/163] get_solver: Make doctest work no matter whether backends are Python or Sage objects --- src/sage/numerical/backends/generic_backend.pyx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 1247b2b5c02..e300cdd0c60 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -1405,22 +1405,22 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None, ba sage: p.base_ring() Real Double Field sage: p = get_solver(base_ring=QQ); p - + <...sage.numerical.backends.ppl_backend.PPLBackend...> sage: p = get_solver(base_ring=ZZ); p - + <...sage.numerical.backends.ppl_backend.PPLBackend...> sage: p.base_ring() Rational Field sage: p = get_solver(base_ring=AA); p - + <...sage.numerical.backends.interactivelp_backend.InteractiveLPBackend...> sage: p.base_ring() Algebraic Real Field sage: d = polytopes.dodecahedron() sage: p = get_solver(base_ring=d.base_ring()); p - + <...sage.numerical.backends.interactivelp_backend.InteractiveLPBackend...> sage: p.base_ring() Number Field in sqrt5 with defining polynomial x^2 - 5 sage: p = get_solver(solver='InteractiveLP', base_ring=QQ); p - + <...sage.numerical.backends.interactivelp_backend.InteractiveLPBackend...> sage: p.base_ring() Rational Field """ From a6f0f4de68e182324d8bab85decb3d0fb40e3b04 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 6 Apr 2016 00:02:40 -0700 Subject: [PATCH 109/163] Run testsuite with all MIP backends --- src/sage/numerical/backends/coin_backend.pyx | 6 +++++- src/sage/numerical/backends/cplex_backend.pyx | 12 ++++++++++++ src/sage/numerical/backends/cvxopt_backend.pyx | 5 +++++ src/sage/numerical/backends/glpk_backend.pyx | 10 +++++++--- src/sage/numerical/backends/gurobi_backend.pyx | 12 ++++++++++++ .../numerical/backends/interactivelp_backend.pyx | 8 ++++++++ 6 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/sage/numerical/backends/coin_backend.pyx b/src/sage/numerical/backends/coin_backend.pyx index 40ff557df2d..383df02c963 100644 --- a/src/sage/numerical/backends/coin_backend.pyx +++ b/src/sage/numerical/backends/coin_backend.pyx @@ -26,7 +26,11 @@ from copy import copy cdef class CoinBackend(GenericBackend): """ - TESTS:: + MIP Backend that uses the COIN solver (CBC). + + TESTS: + + General backend testsuite:: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "Coin") # optional - cbc diff --git a/src/sage/numerical/backends/cplex_backend.pyx b/src/sage/numerical/backends/cplex_backend.pyx index 8987f016af6..701224f0ce9 100644 --- a/src/sage/numerical/backends/cplex_backend.pyx +++ b/src/sage/numerical/backends/cplex_backend.pyx @@ -20,6 +20,18 @@ from sage.numerical.mip import MIPSolverException cdef class CPLEXBackend(GenericBackend): + """ + MIP Backend that uses the CPLEX solver. + + TESTS: + + General backend testsuite:: + + sage: p = MixedIntegerLinearProgram(solver="CPLEX") # optional - CPLEX + sage: TestSuite(p.get_backend()).run(skip="_test_pickling") # optional - CPLEX + + """ + def __cinit__(self, maximization = True): """ Constructor diff --git a/src/sage/numerical/backends/cvxopt_backend.pyx b/src/sage/numerical/backends/cvxopt_backend.pyx index c1864e100e6..5cc7c003281 100644 --- a/src/sage/numerical/backends/cvxopt_backend.pyx +++ b/src/sage/numerical/backends/cvxopt_backend.pyx @@ -36,6 +36,11 @@ cdef class CVXOPTBackend(GenericBackend): sage: p Mixed Integer Program ( maximization, 0 variables, 0 constraints ) + + General backend testsuite:: + + sage: p = MixedIntegerLinearProgram(solver="CVXOPT") + sage: TestSuite(p.get_backend()).run(skip="_test_pickling") """ cdef list objective_function #c_matrix diff --git a/src/sage/numerical/backends/glpk_backend.pyx b/src/sage/numerical/backends/glpk_backend.pyx index d148cbc33e5..1596ec4d7e8 100644 --- a/src/sage/numerical/backends/glpk_backend.pyx +++ b/src/sage/numerical/backends/glpk_backend.pyx @@ -35,10 +35,14 @@ include "cysignals/signals.pxi" cdef class GLPKBackend(GenericBackend): """ - TESTS:: + MIP Backend that uses the GLPK solver. - sage: p = MixedIntegerLinearProgram(solver="GLPK") - sage: TestSuite(p.get_backend()).run(skip="_test_pickling") + TESTS: + + General backend testsuite:: + + sage: p = MixedIntegerLinearProgram(solver="GLPK") + sage: TestSuite(p.get_backend()).run(skip="_test_pickling") """ def __cinit__(self, maximization = True): diff --git a/src/sage/numerical/backends/gurobi_backend.pyx b/src/sage/numerical/backends/gurobi_backend.pyx index b025a8273a3..d52cea174b0 100644 --- a/src/sage/numerical/backends/gurobi_backend.pyx +++ b/src/sage/numerical/backends/gurobi_backend.pyx @@ -31,6 +31,18 @@ include "cysignals/memory.pxi" from sage.numerical.mip import MIPSolverException cdef class GurobiBackend(GenericBackend): + + """ + MIP Backend that uses the Gurobi solver. + + TESTS: + + General backend testsuite:: + + sage: p = MixedIntegerLinearProgram(solver="Gurobi") # optional - Gurobi + sage: TestSuite(p.get_backend()).run(skip="_test_pickling") # optional - Gurobi + """ + def __init__(self, maximization = True): """ Constructor diff --git a/src/sage/numerical/backends/interactivelp_backend.pyx b/src/sage/numerical/backends/interactivelp_backend.pyx index 578c77d328f..fcbc260bd32 100644 --- a/src/sage/numerical/backends/interactivelp_backend.pyx +++ b/src/sage/numerical/backends/interactivelp_backend.pyx @@ -38,6 +38,14 @@ cdef class InteractiveLPBackend: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "InteractiveLP") + + TESTS: + + General backend testsuite:: + + sage: p = MixedIntegerLinearProgram(solver="InteractiveLP") + sage: TestSuite(p.get_backend()).run(skip="_test_pickling") + """ def __cinit__(self, maximization = True, base_ring = None): From 0bd7a877d44ee013faa8bdb50a7622cf28251385 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 6 Apr 2016 00:34:50 -0700 Subject: [PATCH 110/163] GenericBackend._test_add_variables(): Add tests from CVXOPTBackend --- src/sage/numerical/backends/generic_backend.pyx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index e300cdd0c60..5e2bf08c658 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -148,10 +148,23 @@ cdef class GenericBackend: sage: p.add_variables(2) 1 sage: p.add_linear_constraint([[0, 17], [1, 89]], None, 42) - sage: p._test_add_linear_constraint_vector() + sage: p._test_add_variables() """ tester = self._tester(**options) - # Tests here + p = self + # Test from CVXOPT interface: + ncols_added = 5 + ncols_before = p.ncols() + add_variables_result = p.add_variables(ncols_added) + ncols_after = p.ncols() + tester.assertEqual(ncols_after, ncols_before+ncols_added, "Added the wrong number of columns") + # Test from CVXOPT interface, continued + ncols_before = p.ncols() + add_variables_result = p.add_variables(2, lower_bound=-2.0, obj=42.0, names=['a','b']) + ncols_after = p.ncols() + tester.assertEqual(p.col_bounds(ncols_before), (-2.0, None)) # FIXME: tol 1e-8 + tester.assertEqual(p.col_name(ncols_before), 'a') + tester.assertEqual(p.objective_coefficient(ncols_before), 42.0) # FIXME: tol 1e-8 cpdef set_variable_type(self, int variable, int vtype): """ From 6b55e16fd1e781c5071143806655233f03ec91a0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 8 Apr 2016 09:57:36 -0700 Subject: [PATCH 111/163] Add a classmethod _test_solve --- .../numerical/backends/generic_backend.pyx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 5e2bf08c658..d46600a86b9 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -539,6 +539,9 @@ cdef class GenericBackend: """ raise NotImplementedError() + ## This test method is written as an instance method that works + ## even if variables and constraints have already been added to the backend. + ## The test makes changes to the backend. def _test_add_linear_constraints(self, **options): """ Run tests on the method :meth:`.add_linear_constraints`. @@ -595,6 +598,22 @@ cdef class GenericBackend: """ raise NotImplementedError() + ## Any test methods involving calls to 'solve' are set up as class methods, + ## which make a fresh instance of the backend. + @classmethod + def _test_solve(cls, **options): + p = cls() # fresh instance of the backend + print options + tester = p._tester(**options) # <---- doesn't work ("assert tester._instance is instance" fails); we get a tester passed in **options... + # From doctest of GenericBackend.solve + tester.assertIsNone(p.add_linear_constraints(5, 0, None)) + tester.assertIsNone(p.add_col(range(5), range(5))) + tester.assertEqual(p.solve(), 77) # should be 0... + tester.assertIsNone(p.objective_coefficient(0,1)) + from sage.numerical.mip import MIPSolverException + with tester.assertRaises(MIPSolverException) as cm: + p.solve() + cpdef get_objective_value(self): """ Return the value of the objective function. From e14afd3f81f6e72b1226636023a4b58748cd0cd1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 8 Apr 2016 10:11:16 -0700 Subject: [PATCH 112/163] GenericBackend._test_solve: Finish --- .../numerical/backends/generic_backend.pyx | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index d46600a86b9..868dcb75863 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -601,17 +601,30 @@ cdef class GenericBackend: ## Any test methods involving calls to 'solve' are set up as class methods, ## which make a fresh instance of the backend. @classmethod - def _test_solve(cls, **options): + def _test_solve(cls, tester=None, **options): + """ + Trivial test for the solve method. + + TEST:: + + sage: from sage.numerical.backends.generic_backend import GenericBackend + sage: p = GenericBackend() + sage: p._test_solve() + Traceback (most recent call last): + ... + NotImplementedError: ... + """ p = cls() # fresh instance of the backend - print options - tester = p._tester(**options) # <---- doesn't work ("assert tester._instance is instance" fails); we get a tester passed in **options... - # From doctest of GenericBackend.solve + if tester is None: + tester = p._tester(**options) + # From doctest of GenericBackend.solve: tester.assertIsNone(p.add_linear_constraints(5, 0, None)) tester.assertIsNone(p.add_col(range(5), range(5))) - tester.assertEqual(p.solve(), 77) # should be 0... + tester.assertEqual(p.solve(), 0) tester.assertIsNone(p.objective_coefficient(0,1)) from sage.numerical.mip import MIPSolverException - with tester.assertRaises(MIPSolverException) as cm: + #with tester.assertRaisesRegexp(MIPSolverException, "unbounded") as cm: ## --- too specific + with tester.assertRaises(MIPSolverException) as cm: # unbounded p.solve() cpdef get_objective_value(self): From 48b9fe51d9eafadd50d9590f93a35645af4af176 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 9 Apr 2016 15:20:33 -0700 Subject: [PATCH 113/163] Change from mutating instance _test methods to class methods --- .../numerical/backends/generic_backend.pyx | 86 +++++++++---------- 1 file changed, 41 insertions(+), 45 deletions(-) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 868dcb75863..71f560e9993 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -134,24 +134,23 @@ cdef class GenericBackend: """ raise NotImplementedError() - def _test_add_variables(self, **options): + @classmethod + def _test_add_variables(cls, tester=None, **options): """ Run tests on the method :meth:`.add_linear_constraints`. - TESTS: - - Test, with an actual working backend, that the test works even if the problem - is not empty at the beginning:: + TEST:: - sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver='GLPK') - sage: p.add_variables(2) - 1 - sage: p.add_linear_constraint([[0, 17], [1, 89]], None, 42) + sage: from sage.numerical.backends.generic_backend import GenericBackend + sage: p = GenericBackend() sage: p._test_add_variables() + Traceback (most recent call last): + ... + NotImplementedError """ - tester = self._tester(**options) - p = self + p = cls() # fresh instance of the backend + if tester is None: + tester = p._tester(**options) # Test from CVXOPT interface: ncols_added = 5 ncols_before = p.ncols() @@ -449,30 +448,30 @@ cdef class GenericBackend: upper_bound_d = None if upper_bound is None else upper_bound[d] self.add_linear_constraint(coefficients_d, lower_bound_d, upper_bound_d, name=name) - def _test_add_linear_constraint_vector(self, **options): + @classmethod + def _test_add_linear_constraint_vector(cls, tester=None, **options): """ Run tests on the method :meth:`.add_linear_constraints`. - TESTS: - - Test, with an actual working backend, that the test works even if the problem - is not empty at the beginning:: + TEST:: - sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver='GLPK') - sage: p.add_variables(2) - 1 - sage: p.add_linear_constraint([[0, 17], [1, 89]], None, 42) + sage: from sage.numerical.backends.generic_backend import GenericBackend + sage: p = GenericBackend() sage: p._test_add_linear_constraint_vector() + Traceback (most recent call last): + ... + NotImplementedError """ - tester = self._tester(**options) + p = cls() # fresh instance of the backend + if tester is None: + tester = p._tester(**options) from sage.modules.all import vector # Ensure there are at least 2 variables - self.add_variables(2) + p.add_variables(2) coeffs = ([0, vector([1, 2])], [1, vector([2, 3])]) upper = vector([5, 5]) lower = vector([0, 0]) - self.add_linear_constraint_vector(2, coeffs, lower, upper, 'foo') + p.add_linear_constraint_vector(2, coeffs, lower, upper, 'foo') # FIXME: Tests here. Careful what we expect regarding ranged constraints with some solvers. cpdef add_col(self, list indices, list coeffs): @@ -539,36 +538,33 @@ cdef class GenericBackend: """ raise NotImplementedError() - ## This test method is written as an instance method that works - ## even if variables and constraints have already been added to the backend. - ## The test makes changes to the backend. - def _test_add_linear_constraints(self, **options): + @classmethod + def _test_add_linear_constraints(cls, tester=None, **options): """ Run tests on the method :meth:`.add_linear_constraints`. - TESTS: - - Test, with an actual working backend, that the test works even if the problem - is not empty at the beginning:: + TEST:: - sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver='GLPK') - sage: p.add_variables(2) - 1 - sage: p.add_linear_constraint([[0, 17], [1, 89]], None, 42) + sage: from sage.numerical.backends.generic_backend import GenericBackend + sage: p = GenericBackend() sage: p._test_add_linear_constraints() + Traceback (most recent call last): + ... + NotImplementedError """ - tester = self._tester(**options) - nrows_before = self.nrows() + p = cls() # fresh instance of the backend + if tester is None: + tester = p._tester(**options) + nrows_before = p.nrows() nrows_added = 5 - self.add_linear_constraints(nrows_added, None, 2) - nrows_after = self.nrows() + p.add_linear_constraints(nrows_added, None, 2) + nrows_after = p.nrows() # Test correct number of rows tester.assertEqual(nrows_after, nrows_before+nrows_added, "Added the wrong number of rows") # Test contents of the new rows are correct (sparse zero) for i in range(nrows_before, nrows_after): - tester.assertEqual(self.row(i), ([], [])) - tester.assertEqual(self.row_bounds(i), (None, 2.0)) + tester.assertEqual(p.row(i), ([], [])) + tester.assertEqual(p.row_bounds(i), (None, 2.0)) # FIXME: Not sure if we should test that no new variables were added. # Perhaps some backend may need to introduce explicit slack variables? @@ -612,7 +608,7 @@ cdef class GenericBackend: sage: p._test_solve() Traceback (most recent call last): ... - NotImplementedError: ... + NotImplementedError """ p = cls() # fresh instance of the backend if tester is None: From 53947298ddca760b6155994622e143e0e3afc4c2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 9 Apr 2016 16:02:40 -0700 Subject: [PATCH 114/163] New method _test_ncols_nonnegative --- src/sage/numerical/backends/generic_backend.pyx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 71f560e9993..806b373b76c 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -767,6 +767,11 @@ cdef class GenericBackend: raise NotImplementedError() + def _test_ncols_nonnegative(self, **options): + tester = self._tester(**options) + p = self + tester.assertGreaterEqual(self.ncols(), 0) + cpdef int nrows(self): """ Return the number of rows/constraints. From 7138fa03a951cf62d970bf346893499fe8fd3daa Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 11 Apr 2016 12:02:50 -0700 Subject: [PATCH 115/163] GenericBackend: Remove failing _test methods from this ticket to make the patchbot and its friends happy The failing tests (and fixes for the bugs exposed by them) will be reinstated in a different ticket. --- .../numerical/backends/generic_backend.pyx | 41 +------------------ 1 file changed, 2 insertions(+), 39 deletions(-) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 806b373b76c..4f3b01a96d4 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -151,19 +151,12 @@ cdef class GenericBackend: p = cls() # fresh instance of the backend if tester is None: tester = p._tester(**options) - # Test from CVXOPT interface: + # Test from CVXOPT interface (part 1): ncols_added = 5 ncols_before = p.ncols() add_variables_result = p.add_variables(ncols_added) ncols_after = p.ncols() tester.assertEqual(ncols_after, ncols_before+ncols_added, "Added the wrong number of columns") - # Test from CVXOPT interface, continued - ncols_before = p.ncols() - add_variables_result = p.add_variables(2, lower_bound=-2.0, obj=42.0, names=['a','b']) - ncols_after = p.ncols() - tester.assertEqual(p.col_bounds(ncols_before), (-2.0, None)) # FIXME: tol 1e-8 - tester.assertEqual(p.col_name(ncols_before), 'a') - tester.assertEqual(p.objective_coefficient(ncols_before), 42.0) # FIXME: tol 1e-8 cpdef set_variable_type(self, int variable, int vtype): """ @@ -470,8 +463,7 @@ cdef class GenericBackend: p.add_variables(2) coeffs = ([0, vector([1, 2])], [1, vector([2, 3])]) upper = vector([5, 5]) - lower = vector([0, 0]) - p.add_linear_constraint_vector(2, coeffs, lower, upper, 'foo') + p.add_linear_constraint_vector(2, coeffs, None, upper, 'foo') # FIXME: Tests here. Careful what we expect regarding ranged constraints with some solvers. cpdef add_col(self, list indices, list coeffs): @@ -594,35 +586,6 @@ cdef class GenericBackend: """ raise NotImplementedError() - ## Any test methods involving calls to 'solve' are set up as class methods, - ## which make a fresh instance of the backend. - @classmethod - def _test_solve(cls, tester=None, **options): - """ - Trivial test for the solve method. - - TEST:: - - sage: from sage.numerical.backends.generic_backend import GenericBackend - sage: p = GenericBackend() - sage: p._test_solve() - Traceback (most recent call last): - ... - NotImplementedError - """ - p = cls() # fresh instance of the backend - if tester is None: - tester = p._tester(**options) - # From doctest of GenericBackend.solve: - tester.assertIsNone(p.add_linear_constraints(5, 0, None)) - tester.assertIsNone(p.add_col(range(5), range(5))) - tester.assertEqual(p.solve(), 0) - tester.assertIsNone(p.objective_coefficient(0,1)) - from sage.numerical.mip import MIPSolverException - #with tester.assertRaisesRegexp(MIPSolverException, "unbounded") as cm: ## --- too specific - with tester.assertRaises(MIPSolverException) as cm: # unbounded - p.solve() - cpdef get_objective_value(self): """ Return the value of the objective function. From 91146adaa6e7bc6ed240b9c4214bd7552e68692a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 13 Apr 2016 23:00:37 -0700 Subject: [PATCH 116/163] Rename copy methods to __copy__ in backends and call __copy__ from GenericBackend.copy --- src/sage/numerical/backends/coin_backend.pxd | 2 +- src/sage/numerical/backends/coin_backend.pyx | 2 +- src/sage/numerical/backends/cplex_backend.pxd | 2 +- src/sage/numerical/backends/cplex_backend.pyx | 2 +- .../numerical/backends/generic_backend.pxd | 2 ++ .../numerical/backends/generic_backend.pyx | 34 +++++++++++++++++++ src/sage/numerical/backends/glpk_backend.pxd | 2 +- src/sage/numerical/backends/glpk_backend.pyx | 2 +- .../numerical/backends/gurobi_backend.pxd | 2 +- .../numerical/backends/gurobi_backend.pyx | 2 +- 10 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/sage/numerical/backends/coin_backend.pxd b/src/sage/numerical/backends/coin_backend.pxd index 8d44d1ee11b..c45427cb2e5 100644 --- a/src/sage/numerical/backends/coin_backend.pxd +++ b/src/sage/numerical/backends/coin_backend.pxd @@ -197,7 +197,7 @@ cdef class CoinBackend(GenericBackend): cdef list col_names, row_names cdef str prob_name - cpdef CoinBackend copy(self) + cpdef __copy__(self) cpdef get_basis_status(self) cpdef int set_basis_status(self, list cstat, list rstat) except -1 cpdef get_binva_row(self, int i) diff --git a/src/sage/numerical/backends/coin_backend.pyx b/src/sage/numerical/backends/coin_backend.pyx index b994eadc836..f159db3ac54 100644 --- a/src/sage/numerical/backends/coin_backend.pyx +++ b/src/sage/numerical/backends/coin_backend.pyx @@ -1194,7 +1194,7 @@ cdef class CoinBackend(GenericBackend): else: return "" - cpdef CoinBackend copy(self): + cpdef __copy__(self): """ Returns a copy of self. diff --git a/src/sage/numerical/backends/cplex_backend.pxd b/src/sage/numerical/backends/cplex_backend.pxd index 27e720fcdaa..57c81bbc7d1 100644 --- a/src/sage/numerical/backends/cplex_backend.pxd +++ b/src/sage/numerical/backends/cplex_backend.pxd @@ -19,7 +19,7 @@ cdef class CPLEXBackend(GenericBackend): cdef c_cpxlp * lp cdef current_sol cdef str _logfilename - cpdef CPLEXBackend copy(self) + cpdef __copy__(self) cdef extern from "cplex.h": diff --git a/src/sage/numerical/backends/cplex_backend.pyx b/src/sage/numerical/backends/cplex_backend.pyx index 8987f016af6..9f0be4dc16c 100644 --- a/src/sage/numerical/backends/cplex_backend.pyx +++ b/src/sage/numerical/backends/cplex_backend.pyx @@ -1437,7 +1437,7 @@ cdef class CPLEXBackend(GenericBackend): status = CPXwriteprob(self.env, self.lp, filename, ext) check(status) - cpdef CPLEXBackend copy(self): + cpdef __copy__(self): r""" Returns a copy of self. diff --git a/src/sage/numerical/backends/generic_backend.pxd b/src/sage/numerical/backends/generic_backend.pxd index 53c3f26579e..bfb4b9a6d81 100644 --- a/src/sage/numerical/backends/generic_backend.pxd +++ b/src/sage/numerical/backends/generic_backend.pxd @@ -44,6 +44,8 @@ cdef class GenericBackend: cpdef solver_parameter(self, name, value=*) cpdef zero(self) cpdef base_ring(self) + cpdef __copy__(self) + cpdef copy(self) cpdef bint is_variable_basic(self, int index) cpdef bint is_variable_nonbasic_at_lower_bound(self, int index) cpdef bint is_slack_variable_basic(self, int index) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 28d443647c9..24282449738 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -744,6 +744,40 @@ cdef class GenericBackend: """ raise NotImplementedError() + cpdef copy(self): + """ + Returns a copy of self. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = MixedIntegerLinearProgram(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver + sage: b = p.new_variable() # optional - Nonexistent_LP_solver + sage: p.add_constraint(b[1] + b[2] <= 6) # optional - Nonexistent_LP_solver + sage: p.set_objective(b[1] + b[2]) # optional - Nonexistent_LP_solver + sage: copy(p).solve() # optional - Nonexistent_LP_solver + 6.0 + """ + return self.__copy__() + + # Override this method in backends. + cpdef __copy__(self): + """ + Returns a copy of self. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = MixedIntegerLinearProgram(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver + sage: b = p.new_variable() # optional - Nonexistent_LP_solver + sage: p.add_constraint(b[1] + b[2] <= 6) # optional - Nonexistent_LP_solver + sage: p.set_objective(b[1] + b[2]) # optional - Nonexistent_LP_solver + sage: p.get_backend().solve(); # optional - Nonexistent_LP_solver + sage: p.get_backend().get_objective_value() # optional - Nonexistent_LP_solver + 6.0 + """ + raise NotImplementedError() + cpdef row(self, int i): """ Return a row diff --git a/src/sage/numerical/backends/glpk_backend.pxd b/src/sage/numerical/backends/glpk_backend.pxd index 2ccbd67dc81..f09f2909741 100644 --- a/src/sage/numerical/backends/glpk_backend.pxd +++ b/src/sage/numerical/backends/glpk_backend.pxd @@ -26,7 +26,7 @@ cdef class GLPKBackend(GenericBackend): cdef glp_smcp * smcp cdef int simplex_or_intopt cdef search_tree_data_t search_tree_data - cpdef GLPKBackend copy(self) + cpdef __copy__(self) cpdef int print_ranges(self, char * filename = *) except -1 cpdef double get_row_dual(self, int variable) cpdef double get_col_dual(self, int variable) diff --git a/src/sage/numerical/backends/glpk_backend.pyx b/src/sage/numerical/backends/glpk_backend.pyx index d8a833eb2ec..2a632998418 100644 --- a/src/sage/numerical/backends/glpk_backend.pyx +++ b/src/sage/numerical/backends/glpk_backend.pyx @@ -1600,7 +1600,7 @@ cdef class GLPKBackend(GenericBackend): """ glp_write_mps(self.lp, modern, NULL, filename) - cpdef GLPKBackend copy(self): + cpdef __copy__(self): """ Returns a copy of self. diff --git a/src/sage/numerical/backends/gurobi_backend.pxd b/src/sage/numerical/backends/gurobi_backend.pxd index 0b1025a4be3..560e3a61687 100644 --- a/src/sage/numerical/backends/gurobi_backend.pxd +++ b/src/sage/numerical/backends/gurobi_backend.pxd @@ -96,7 +96,7 @@ cdef class GurobiBackend(GenericBackend): cdef GRBenv * env cdef GRBenv * env_master cdef GRBmodel * model - cpdef GurobiBackend copy(self) + cpdef __copy__(self) cdef int num_vars diff --git a/src/sage/numerical/backends/gurobi_backend.pyx b/src/sage/numerical/backends/gurobi_backend.pyx index b025a8273a3..156ff303f4a 100644 --- a/src/sage/numerical/backends/gurobi_backend.pyx +++ b/src/sage/numerical/backends/gurobi_backend.pyx @@ -1159,7 +1159,7 @@ cdef class GurobiBackend(GenericBackend): else: raise RuntimeError("This should not happen.") - cpdef GurobiBackend copy(self): + cpdef __copy__(self): """ Returns a copy of self. From ba857dd9802f7712838cbd15896a8ac755a0a503 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Thu, 14 Apr 2016 08:02:05 +0200 Subject: [PATCH 117/163] Fast conversion of quadratic elements to real/complex balls --- src/module_list.py | 9 ++- src/sage/rings/complex_arb.pyx | 51 ++++++++++++++-- src/sage/rings/real_arb.pxd | 3 + src/sage/rings/real_arb.pyx | 107 +++++++++++++++++++++++++++++++-- 4 files changed, 159 insertions(+), 11 deletions(-) diff --git a/src/module_list.py b/src/module_list.py index ffe3dd6abdc..9a3a6d04a4a 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -871,7 +871,8 @@ def uname_specific(name, value, alternative): Extension("sage.matrix.matrix_complex_ball_dense", ["sage/matrix/matrix_complex_ball_dense.pyx"], libraries=['arb', 'mpfi', 'mpfr'], - include_dirs=[SAGE_INC + '/flint']), + include_dirs=[SAGE_INC + '/flint'], + language = "c++"), Extension('sage.matrix.matrix_complex_double_dense', sources = ['sage/matrix/matrix_complex_double_dense.pyx']), @@ -1260,7 +1261,8 @@ def uname_specific(name, value, alternative): Extension("sage.rings.complex_arb", ["sage/rings/complex_arb.pyx"], - libraries=['mpfi', 'mpfr', 'gmp']), + libraries=['mpfi', 'mpfr', 'gmp'], + language = 'c++'), Extension('sage.rings.complex_double', sources = ['sage/rings/complex_double.pyx'], @@ -1331,7 +1333,8 @@ def uname_specific(name, value, alternative): Extension("sage.rings.real_arb", ["sage/rings/real_arb.pyx"], - libraries = ['mpfi', 'mpfr']), + libraries = ['mpfi', 'mpfr'], + language = 'c++'), Extension('sage.rings.real_lazy', sources = ['sage/rings/real_lazy.pyx']), diff --git a/src/sage/rings/complex_arb.pyx b/src/sage/rings/complex_arb.pyx index 79b78908316..895107646c4 100644 --- a/src/sage/rings/complex_arb.pyx +++ b/src/sage/rings/complex_arb.pyx @@ -137,6 +137,8 @@ import sage.categories.fields cimport sage.rings.integer cimport sage.rings.rational +import sage.rings.number_field.number_field as number_field + from cpython.float cimport PyFloat_AS_DOUBLE from cpython.int cimport PyInt_AS_LONG from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE @@ -148,13 +150,14 @@ from sage.libs.arb.acb_hypgeom cimport * from sage.libs.arb.acb_modular cimport * from sage.libs.arb.arf cimport arf_init, arf_get_mpfr, arf_set_mpfr, arf_clear, arf_set_mag, arf_set from sage.libs.arb.mag cimport mag_init, mag_clear, mag_add, mag_set_d, MAG_BITS, mag_is_inf, mag_is_finite, mag_zero -from sage.libs.flint.fmpz cimport fmpz_t, fmpz_init, fmpz_get_mpz, fmpz_set_mpz, fmpz_clear +from sage.libs.flint.fmpz cimport fmpz_t, fmpz_init, fmpz_get_mpz, fmpz_set_mpz, fmpz_clear, fmpz_abs from sage.libs.flint.fmpq cimport fmpq_t, fmpq_init, fmpq_set_mpq, fmpq_clear -from sage.libs.gmp.mpz cimport mpz_fits_ulong_p, mpz_fits_slong_p, mpz_get_ui, mpz_get_si +from sage.libs.gmp.mpz cimport mpz_fits_ulong_p, mpz_fits_slong_p, mpz_get_ui, mpz_get_si, mpz_sgn from sage.rings.complex_field import ComplexField from sage.rings.complex_interval_field import ComplexIntervalField from sage.rings.integer_ring import ZZ -from sage.rings.real_arb cimport mpfi_to_arb, arb_to_mpfi +from sage.rings.number_field.number_field_element_quadratic cimport NumberFieldElement_quadratic +from sage.rings.real_arb cimport mpfi_to_arb, arb_to_mpfi, real_part_of_quadratic_element_to_arb from sage.rings.real_arb import RealBallField from sage.rings.real_mpfr cimport RealField_class, RealField, RealNumber from sage.rings.ring import Field @@ -428,6 +431,10 @@ class ComplexBallField(UniqueRepresentation, Field): True sage: CBF.has_coerce_map_from(RealBallField(52)) False + sage: CBF.has_coerce_map_from(QuadraticField(-2)) + True + sage: CBF.has_coerce_map_from(QuadraticField(2, embedding=None)) + False Check that there are no coercions from interval or floating-point parents:: @@ -442,6 +449,10 @@ class ComplexBallField(UniqueRepresentation, Field): """ if isinstance(other, (RealBallField, ComplexBallField)): return (other._prec >= self._prec) + elif isinstance(other, number_field.NumberField_quadratic): + emb = other.coerce_embedding() + if emb is not None: + return self.has_coerce_map_from(emb.codomain()) def _element_constructor_(self, x=None, y=None): r""" @@ -484,6 +495,9 @@ class ComplexBallField(UniqueRepresentation, Field): [0.3333333333333333 +/- 7.04e-17] + [0.1666666666666667 +/- 7.04e-17]*I sage: ComplexBallField(106)(1/3, 1/6) [0.33333333333333333333333333333333 +/- 6.94e-33] + [0.16666666666666666666666666666666 +/- 7.70e-33]*I + sage: NF. = QuadraticField(-2) + sage: CBF(1/5 + a/2) + [0.2000000000000000 +/- 4.45e-17] + [0.707106781186547 +/- 5.86e-16]*I sage: CBF(infinity, NaN) [+/- inf] + nan*I sage: CBF(x) @@ -500,7 +514,7 @@ class ComplexBallField(UniqueRepresentation, Field): sage: CBF(1+I, 2) Traceback (most recent call last): ... - TypeError: unable to convert I + 1 to a RealBall + ValueError: nonzero imaginary part """ try: return self.element_class(self, x, y) @@ -694,9 +708,19 @@ cdef class ComplexBall(RingElement): Traceback (most recent call last): ... TypeError: unsupported initializer + sage: NF. = QuadraticField(-1, embedding=CC(0, -1)) + sage: CBF(a) + -1.000000000000000*I + sage: NF. = QuadraticField(-1, embedding=None) + sage: CBF(a) + Traceback (most recent call last): + ... + ValueError: need an embedding """ cdef fmpz_t tmpz cdef fmpq_t tmpq + cdef NumberFieldElement_quadratic x_as_qe + cdef long myprec RingElement.__init__(self, parent) @@ -726,6 +750,25 @@ cdef class ComplexBall(RingElement): elif isinstance(x, ComplexIntervalFieldElement): ComplexIntervalFieldElement_to_acb(self.value, x) + elif isinstance(x, NumberFieldElement_quadratic): + x_as_qe = x + real_part_of_quadratic_element_to_arb(acb_realref(self.value), + x_as_qe, prec(self)) + myprec = prec(self) + 4 + if mpz_sgn(x_as_qe.D.value) < 0: + if x_as_qe._parent._embedding is None: + raise ValueError("need an embedding") + fmpz_init(tmpz) + fmpz_set_mpz(tmpz, x_as_qe.D.value) + fmpz_abs(tmpz, tmpz) + arb_sqrt_fmpz(acb_imagref(self.value), tmpz, myprec) + fmpz_set_mpz(tmpz, x_as_qe.b) + arb_mul_fmpz(acb_imagref(self.value), acb_imagref(self.value), tmpz, myprec) + fmpz_set_mpz(tmpz, x_as_qe.denom) + arb_div_fmpz(acb_imagref(self.value), acb_imagref(self.value), tmpz, prec(self)) + fmpz_clear(tmpz) + if not x_as_qe.standard_embedding: + acb_conj(self.value, self.value) else: raise TypeError("unsupported initializer") elif isinstance(x, RealBall) and isinstance(y, RealBall): diff --git a/src/sage/rings/real_arb.pxd b/src/sage/rings/real_arb.pxd index 7f39f9c4efd..34d2407cde9 100644 --- a/src/sage/rings/real_arb.pxd +++ b/src/sage/rings/real_arb.pxd @@ -1,3 +1,5 @@ +cimport sage.rings.number_field.number_field_element_quadratic as nfeq + from sage.libs.arb.arb cimport arb_t from sage.libs.mpfi cimport mpfi_t from sage.rings.real_mpfi cimport RealIntervalField_class, RealIntervalFieldElement @@ -6,6 +8,7 @@ from sage.structure.element cimport RingElement cdef void mpfi_to_arb(arb_t target, const mpfi_t source, const long precision) cdef int arb_to_mpfi(mpfi_t target, arb_t source, const long precision) except -1 +cdef int real_part_of_quadratic_element_to_arb(arb_t res, nfeq.NumberFieldElement_quadratic x, const long prec) except -1 cdef class RealBall(RingElement): cdef arb_t value diff --git a/src/sage/rings/real_arb.pyx b/src/sage/rings/real_arb.pyx index fd9ab476294..624acc8b19f 100644 --- a/src/sage/rings/real_arb.pyx +++ b/src/sage/rings/real_arb.pyx @@ -189,7 +189,6 @@ Classes and Methods include "cysignals/signals.pxi" - from cpython.float cimport PyFloat_AS_DOUBLE from cpython.int cimport PyInt_AS_LONG from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE @@ -208,7 +207,7 @@ from sage.libs.flint.fmpz cimport ( fmpz_t, fmpz_init, fmpz_get_mpz, fmpz_set_mpz, fmpz_clear, fmpz_fdiv_ui ) from sage.libs.flint.fmpq cimport fmpq_t, fmpq_init, fmpq_set_mpq, fmpq_clear -from sage.libs.gmp.mpz cimport mpz_fits_ulong_p, mpz_fits_slong_p, mpz_get_ui, mpz_get_si +from sage.libs.gmp.mpz cimport mpz_fits_ulong_p, mpz_fits_slong_p, mpz_get_ui, mpz_get_si, mpz_sgn from sage.libs.mpfi cimport mpfi_get_left, mpfi_get_right, mpfi_interv_fr from sage.libs.mpfr cimport mpfr_t, mpfr_init2, mpfr_clear, mpfr_sgn, MPFR_PREC_MIN, mpfr_equal_p from sage.libs.mpfr cimport MPFR_RNDN, MPFR_RNDU, MPFR_RNDD, MPFR_RNDZ @@ -222,6 +221,8 @@ from sage.structure.element cimport Element, ModuleElement, RingElement import operator import sage.categories.fields +import sage.rings.number_field.number_field as number_field + from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.real_mpfi import RealIntervalField, RealIntervalField_class @@ -304,6 +305,55 @@ cdef int arb_to_mpfi(mpfi_t target, arb_t source, const long precision) except - mpfr_clear(left) mpfr_clear(right) +cdef int real_part_of_quadratic_element_to_arb(arb_t res, + nfeq.NumberFieldElement_quadratic x, const long prec) except -1: + r""" + Convert the real part of a quadratic element to an arb object of type + ``arb_t``. + + This function does *not* check that the parent has a real or complex + embedding. + + TESTS:: + + sage: NF. = QuadraticField(2) + sage: a = (sqrt2 - 1)^1000 + sage: RBF(a) + [1.676156872756536e-383 +/- 4.39e-399] + + sage: NF. = QuadraticField(-2) + sage: CBF(1/3 + a).real() + [0.3333333333333333 +/- 7.04e-17] + """ + cdef fmpz_t tmpz + cdef arb_t rootD + cdef long myprec = prec + 6 + fmpz_init(tmpz) + arb_init(rootD) + while True: # a-b√D might cancel + fmpz_set_mpz(tmpz, x.a) + arb_set_fmpz(res, tmpz) + if mpz_sgn(x.D.value) > 0: + if _do_sig(myprec): sig_on() + fmpz_set_mpz(tmpz, x.D.value) + arb_sqrt_fmpz(rootD, tmpz, myprec) + fmpz_set_mpz(tmpz, x.b) + if x.standard_embedding: + arb_addmul_fmpz(res, rootD, tmpz, myprec) + else: + arb_submul_fmpz(res, rootD, tmpz, myprec) + if _do_sig(myprec): sig_off() + if arb_rel_accuracy_bits(res) < prec - 4: + myprec *= 2 + continue + break + if _do_sig(myprec): sig_on() + arb_clear(rootD) + fmpz_clear(tmpz) + fmpz_set_mpz(tmpz, x.denom) + arb_div_fmpz(res, res, tmpz, prec) + if _do_sig(myprec): sig_off() + return 0 class RealBallField(UniqueRepresentation, Field): r""" @@ -436,11 +486,19 @@ class RealBallField(UniqueRepresentation, Field): False sage: RealBallField().has_coerce_map_from(RR) False + sage: RBF.has_coerce_map_from(QuadraticField(2)) + True + sage: RBF.has_coerce_map_from(QuadraticField(2, embedding=None)) + False + sage: RBF.has_coerce_map_from(QuadraticField(-2)) + False """ if isinstance(other, RealBallField): return (other._prec >= self._prec) - else: - return False + elif isinstance(other, number_field.NumberField_quadratic): + emb = other.coerce_embedding() + if emb is not None: + return self.has_coerce_map_from(emb.codomain()) def _element_constructor_(self, mid=None, rad=None): """ @@ -1073,6 +1131,12 @@ cdef class RealBall(RingElement): sage: RBF(pi, 0.125r) [3e+0 +/- 0.267] + :: + + sage: NF. = QuadraticField(2) + sage: RBF(1/5 + sqrt2/2) + [0.907106781186547 +/- 5.33e-16] + Note that integers and floating-point numbers are ''not'' rounded to the parent's precision:: @@ -1151,12 +1215,39 @@ cdef class RealBall(RingElement): ... ValueError: unsupported string format + sage: NF. = QuadraticField(2) + sage: RBF.coerce(a) + [1.414213562373095 +/- 3.03e-16] + sage: NF. = QuadraticField(2, embedding=-1.4) + sage: RBF(a) + [-1.414213562373095 +/- 3.03e-16] + sage: NF. = QuadraticField(2, embedding=None) + sage: RBF(a) + Traceback (most recent call last): + ... + ValueError: need an embedding + sage: RBF.coerce(a) + Traceback (most recent call last): + ... + TypeError: no canonical coercion... + sage: QQi. = QuadraticField(-1) + sage: RBF(QQi(3)) + 3.000000000000000 + sage: RBF(i) + Traceback (most recent call last): + ... + ValueError: nonzero imaginary part + sage: RBF.coerce(QQi(3)) + Traceback (most recent call last): + ... + TypeError: no canonical coercion... """ import sage.symbolic.constants cdef fmpz_t tmpz cdef fmpq_t tmpq cdef arf_t tmpr cdef mag_t tmpm + cdef nfeq.NumberFieldElement_quadratic mid_as_qe Element.__init__(self, parent) @@ -1188,6 +1279,14 @@ cdef class RealBall(RingElement): arb_set_arf(self.value, tmpr) # no rounding! arf_clear(tmpr) if _do_sig(prec(self)): sig_off() + elif isinstance(mid, nfeq.NumberFieldElement_quadratic): + mid_as_qe = mid + if mpz_sgn(mid_as_qe.b) != 0: + if mpz_sgn(mid_as_qe.D.value) < 0: + raise ValueError("nonzero imaginary part") + elif mid_as_qe._parent._embedding is None: + raise ValueError("need an embedding") + real_part_of_quadratic_element_to_arb(self.value, mid_as_qe, prec(self)) elif isinstance(mid, sage.rings.infinity.AnInfinity): if isinstance(mid, sage.rings.infinity.PlusInfinity): arb_pos_inf(self.value) From 31977871ebc1d25677951f8cbbe6dee3105c9868 Mon Sep 17 00:00:00 2001 From: Marc Masdeu Date: Thu, 14 Apr 2016 08:27:06 +0100 Subject: [PATCH 118/163] Fixed user_config and environment variable clash. --- src/sage/interfaces/magma.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index 643c991bf66..5a63af8db2a 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -300,7 +300,7 @@ class Magma(ExtraTabCompletion, Expect): """ def __init__(self, script_subdirectory=None, logfile=None, server=None, server_tmpdir=None, - user_config=False, seed=None, command='magma'): + user_config=False, seed=None, command=None): """ INPUT: @@ -327,6 +327,10 @@ def __init__(self, script_subdirectory=None, sage: Magma(logfile=tmp_filename()) Magma """ + if command is None: + import os + command = os.getenv('SAGE_MAGMA_COMMAND') or 'magma' + if not user_config: command += ' -n' @@ -340,6 +344,7 @@ def __init__(self, script_subdirectory=None, name = "magma", prompt = ">>SAGE>>", command = command, + command_args = command_args, server = server, server_tmpdir = server_tmpdir, script_subdirectory = script_subdirectory, From c5ceb093b7ffb558182f44662a0e3225257a40b3 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 14 Apr 2016 10:33:33 +0200 Subject: [PATCH 119/163] Deprecate src/sage/ext/interactive_constructors_c.pyx --- src/doc/en/reference/repl/index.rst | 2 -- src/sage/all.py | 5 +++-- src/sage/ext/interactive_constructors_c.pyx | 5 +++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/doc/en/reference/repl/index.rst b/src/doc/en/reference/repl/index.rst index 1ddae8bf0e3..eb5a575d6a8 100644 --- a/src/doc/en/reference/repl/index.rst +++ b/src/doc/en/reference/repl/index.rst @@ -87,8 +87,6 @@ Miscellaneous .. toctree:: :maxdepth: 2 - sage/ext/interactive_constructors_c - sage/repl/readline_extra_commands sage/repl/interpreter diff --git a/src/sage/all.py b/src/sage/all.py index 489fb1ad0fa..669c77be334 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -268,7 +268,9 @@ def quit_sage(verbose=True): from sage.libs.all import symmetrica symmetrica.end() -from sage.ext.interactive_constructors_c import inject_on, inject_off +# A deprecation(20442) warning will be given when this module is +# imported, in particular when these functions are used. +lazy_import("sage.ext.interactive_constructors_c", ["inject_on", "inject_off"]) sage.structure.sage_object.register_unpickle_override('sage.categories.category', 'Sets', Sets) sage.structure.sage_object.register_unpickle_override('sage.categories.category_types', 'HeckeModules', HeckeModules) @@ -278,7 +280,6 @@ def quit_sage(verbose=True): sage.structure.sage_object.register_unpickle_override('sage.categories.category_types', 'VectorSpaces', VectorSpaces) sage.structure.sage_object.register_unpickle_override('sage.categories.category_types', 'Schemes_over_base', sage.categories.schemes.Schemes_over_base) sage.structure.sage_object.register_unpickle_override('sage.categories.category_types', 'ModularAbelianVarieties', ModularAbelianVarieties) -#sage.structure.sage_object.register_unpickle_override('sage.categories.category_types', '', ) # Cache the contents of star imports. sage.misc.lazy_import.save_cache_file() diff --git a/src/sage/ext/interactive_constructors_c.pyx b/src/sage/ext/interactive_constructors_c.pyx index c14d66529af..9bc5b4cf745 100644 --- a/src/sage/ext/interactive_constructors_c.pyx +++ b/src/sage/ext/interactive_constructors_c.pyx @@ -3,6 +3,9 @@ Constructors that automatically inject variables into the global module scope """ import sage.rings.all +import sage.misc.superseded +sage.misc.superseded.deprecation(20442, + '''The inject_on() functionality is deprecated, use the syntax "R. = PolynomialRing(QQ)" instead.''') _verbose=True _inject_mode_off = False @@ -26,6 +29,8 @@ def inject_on(verbose=True): EXAMPLES:: sage: inject_on(verbose=True) + doctest:...: DeprecationWarning: The inject_on() functionality is deprecated, use the syntax "R. = PolynomialRing(QQ)" instead. + See http://trac.sagemath.org/20442 for details. Redefining: FiniteField Frac FractionField FreeMonoid GF LaurentSeriesRing NumberField PolynomialRing quo quotient sage: GF(9,'b') Defining b From ce26d46998e1c1be16babab4225dc0f9e1dac281 Mon Sep 17 00:00:00 2001 From: Marc Masdeu Date: Thu, 14 Apr 2016 11:07:35 +0100 Subject: [PATCH 120/163] Removed command_args parameter from one place. --- 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 5a63af8db2a..d7546b0fcab 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -344,7 +344,6 @@ def __init__(self, script_subdirectory=None, name = "magma", prompt = ">>SAGE>>", command = command, - command_args = command_args, server = server, server_tmpdir = server_tmpdir, script_subdirectory = script_subdirectory, From d114661139f46fa8e97f79a8c6176c535879e523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 14 Apr 2016 16:35:20 +0200 Subject: [PATCH 121/163] trac #19446 two details in doc of comb method of binary trees --- src/sage/combinat/binary_tree.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/binary_tree.py b/src/sage/combinat/binary_tree.py index a7c553cd3f7..02fad41649f 100644 --- a/src/sage/combinat/binary_tree.py +++ b/src/sage/combinat/binary_tree.py @@ -2040,7 +2040,7 @@ def comb(self, side='left'): r""" Return the comb of a tree. - There are two combs in a binary tree : a left comb and a right comb. + There are two combs in a binary tree: a left comb and a right comb. Consider all the vertices of the leftmost (resp. rightmost) branch of the root. The left (resp. right) comb is the list of right (resp. left) @@ -2070,7 +2070,7 @@ def comb(self, side='left'): [.] sage: BT = BinaryTree( '[[[[., [., .]], .], [[., .], [[[., .], [., .]], [., .]]]], [., [[[., .], [[[., .], [., .]], .]], .]]]' ) sage: ascii_art(BT) - ________o________ + ________o________ / \ __o__ o / \ \ From d1d84c64d7c73f709280af181bf39736dad3487d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 14 Apr 2016 09:58:04 -0700 Subject: [PATCH 122/163] get_solver: Add doctest --- src/sage/numerical/backends/generic_backend.pyx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 1cad74802a2..45ffaf68d47 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -1352,12 +1352,25 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None, ba sage: p.base_ring() Rational Field - Passing a callable as the 'solver': + Passing a callable as the 'solver':: sage: from sage.numerical.backends.glpk_backend import GLPKBackend sage: p = get_solver(GLPKBackend); p <...sage.numerical.backends.glpk_backend.GLPKBackend...> + Passing a callable that customizes a backend:: + + sage: def glpk_exact_solver(): + ....: from sage.numerical.backends.generic_backend import get_solver + ....: b = get_solver(solver="GLPK") + ....: b.solver_parameter("simplex_or_intopt", "exact_simplex_only") + ....: return b + sage: delsarte_bound_additive_hamming_space(19,15,7,solver=glpk_exact_solver) # long time + glp_exact... + ... + OPTIMAL SOLUTION FOUND + 2 + """ if solver is None: From cc2d999c5234040914a4fee66708f33124eef8a9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 14 Apr 2016 10:19:22 -0700 Subject: [PATCH 123/163] Mention solver=callable in docstrings and error messages --- src/sage/numerical/backends/generic_backend.pyx | 2 +- src/sage/numerical/mip.pyx | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 45ffaf68d47..d5f2f1626fa 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -1429,4 +1429,4 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None, ba return InteractiveLPBackend(base_ring=base_ring) else: - raise ValueError("'solver' should be set to 'GLPK', 'Coin', 'CPLEX', 'CVXOPT', 'Gurobi', 'PPL', 'InteractiveLP', or None (in which case the default one is used).") + raise ValueError("'solver' should be set to 'GLPK', 'Coin', 'CPLEX', 'CVXOPT', 'Gurobi', 'PPL', 'InteractiveLP', None (in which case the default one is used), or a callable.") diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 09ee8849671..d3daa4df995 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -267,6 +267,10 @@ cdef class MixedIntegerLinearProgram(SageObject): - If ``solver=None`` (default), the default solver is used (see :func:`default_mip_solver`) + - ``solver`` can also be a callable, + see :func:`sage.numerical.backends.generic_backend.get_solver` for + examples. + - ``maximization`` - When set to ``True`` (default), the ``MixedIntegerLinearProgram`` @@ -358,8 +362,12 @@ cdef class MixedIntegerLinearProgram(SageObject): - PPL (``solver="PPL"``). See the `PPL `_ web site. - -If ``solver=None`` (default), the default solver is used (see - ``default_mip_solver`` method. + - If ``solver=None`` (default), the default solver is used, see + :func:`default_mip_solver`. + + - ``solver`` can also be a callable, + see :func:`sage.numerical.backends.generic_backend.get_solver` for + examples. - ``maximization`` From c079a14424e9a1ea77179c658284b867b8401ef8 Mon Sep 17 00:00:00 2001 From: Marc Masdeu Date: Thu, 14 Apr 2016 21:25:30 +0100 Subject: [PATCH 124/163] Deprecated and removed from global namesplace the function magma_version. --- src/sage/interfaces/all.py | 9 ++++++++- src/sage/interfaces/magma.py | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sage/interfaces/all.py b/src/sage/interfaces/all.py index e751feff750..11e2544dfb1 100644 --- a/src/sage/interfaces/all.py +++ b/src/sage/interfaces/all.py @@ -15,7 +15,14 @@ from gnuplot import gnuplot from kash import kash, kash_version, Kash from lisp import lisp, Lisp -from magma import magma, magma_version, Magma +from magma import magma, Magma +from sage.misc.superseded import deprecated_callable_import +deprecated_callable_import(20388, + 'sage.interfaces.magma', + globals(), + locals(), + ["magma_version"], +"This function has been deprecated. Use magma.version() instead.") from magma_free import magma_free from macaulay2 import macaulay2, Macaulay2 from maple import maple, Maple diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index d7546b0fcab..882ac64688b 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -2774,6 +2774,8 @@ def magma_version(): Return the version of Magma that you have in your PATH on your computer. + It has been deprecated, use instead :meth:`sage.interfaces.magma.Magma.version`. + OUTPUT: From fd4af09a512bbf177ac04551095a8518560460b5 Mon Sep 17 00:00:00 2001 From: Ben Salisbury Date: Thu, 14 Apr 2016 16:42:09 -0400 Subject: [PATCH 125/163] documentation fixes and changed _monomial to _variable --- .../lie/infinity_crystals.rst | 24 ++++--- .../combinat/crystals/monomial_crystals.py | 62 +++++++++---------- 2 files changed, 48 insertions(+), 38 deletions(-) diff --git a/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst b/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst index c307d91e6f8..80b25ca1b36 100644 --- a/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst +++ b/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst @@ -206,7 +206,7 @@ at least one `y_i(k) \neq 0`. The crystal structure on this set is defined by where `\{h_i : i \in I\}` and `\{\Lambda_i : i \in I \}` are the simple coroots and fundamental weights, respectively. With a chosen set of integers -`C = (c_{ij})_{i\neq j}` such that `c_{ij}+c{ji} =1`, one defines +`C = (c_{ij})_{i\neq j}` such that `c_{ij}+c_{ji} =1`, one defines .. MATH:: @@ -227,7 +227,16 @@ where `(a_{ij})` is a Cartan matrix. Then Monomial crystals depend on the choice of positive integers `C = (c_{ij})_{i\neq j}` satisfying the condition `c_{ij}+c_{ji}=1`. This choice has been made in Sage such that `c_{ij} = 1` if - `i < j` and `c_{ij} = 0` if `i>j`. + `i < j` and `c_{ij} = 0` if `i>j`, but other choices may be used if + deliberately stated at the intialization of the crystal:: + + sage: c = Matrix([[0,0,1],[1,0,0],[0,1,0]]) + sage: La = RootSystem(['C',3]).weight_lattice().fundamental_weights() + sage: M = crystals.NakajimaMonomials(2*La[1], c=c) + sage: M.c() + [0 0 1] + [1 0 0] + [0 1 0] It is shown in [KKS2007]_ that the connected component of `\widehat{\mathcal{M}}` containing the element `\boldsymbol{1}`, which we denote by @@ -242,18 +251,19 @@ containing the element `\boldsymbol{1}`, which we denote by sage: m.weight_in_root_lattice() -2*alpha[0] - 2*alpha[1] - 2*alpha[2] - alpha[3] -We can also model `B(\infty)` using the monomials `A_{i,k}` instead: - -.. link:: +We can also model `B(\infty)` using the variables `A_{i,k}` instead:: - sage: Minf.set_monomials('A') + sage: Minf = crystals.infinity.NakajimaMonomials(['C',3,1]) + sage: minf = Minf.highest_weight_vector() + sage: m = minf.f_string([0,1,2,3,2,1,0]); m + sage: Minf.set_variables('A') sage: m A(0,0)^-1 A(0,3)^-1 A(1,0)^-1 A(1,2)^-1 A(2,0)^-1 A(2,1)^-1 A(3,0)^-1 sage: m.weight() -2*Lambda[0] + 2*Lambda[1] - 2*delta sage: m.weight_in_root_lattice() -2*alpha[0] - 2*alpha[1] - 2*alpha[2] - alpha[3] - sage: Minf.set_monomials('Y') + sage: Minf.set_variables('Y') Building the crystal graph output for these monomial crystals is the same as the constructions above:: diff --git a/src/sage/combinat/crystals/monomial_crystals.py b/src/sage/combinat/crystals/monomial_crystals.py index 1c39fcce0ab..165a11f9cf7 100644 --- a/src/sage/combinat/crystals/monomial_crystals.py +++ b/src/sage/combinat/crystals/monomial_crystals.py @@ -33,7 +33,7 @@ where `\{h_i : i \in I\}` and `\{\Lambda_i : i \in I \}` are the simple coroots and fundamental weights, respectively. With a chosen set of integers -`C = (c_{ij})_{i\neq j}` such that `c_{ij}+c{ji} =1`, one defines +`C = (c_{ij})_{i\neq j}` such that `c_{ij}+c_{ji} =1`, one defines .. MATH:: @@ -116,7 +116,7 @@ class NakajimaMonomial(Element): r""" An element of the monomial crystal. - Monomials of the form `Y_{i_1,k_1}^{a_1} \cdots Y_{i_t,k_t}^{y_t}`, + Monomials of the form `Y_{i_1,k_1}^{y_1} \cdots Y_{i_t,k_t}^{y_t}`, where `i_1, \dots, i_t` are elements of the index set, `k_1, \dots, k_t` are nonnegative integers, and `y_1, \dots, y_t` are integers. @@ -133,13 +133,13 @@ class NakajimaMonomial(Element): An example using the `A` variables:: sage: M = crystals.infinity.NakajimaMonomials("A3") - sage: M.set_monomials('A') + sage: M.set_variables('A') sage: mg = M.module_generators[0] sage: mg.f_string([1,2,3,2,1]) A(1,0)^-1 A(1,1)^-1 A(2,0)^-2 A(3,0)^-1 sage: mg.f_string([3,2,1]) A(1,2)^-1 A(2,1)^-1 A(3,0)^-1 - sage: M.set_monomials('Y') + sage: M.set_variables('Y') """ def __init__(self, parent, Y, A): @@ -167,12 +167,12 @@ def _repr_(self): sage: M = crystals.infinity.NakajimaMonomials(['A',5,2]) sage: x = M({(1,0):1, (2,2):-2, (0,5):10}); x Y(0,5)^10 Y(1,0) Y(2,2)^-2 - sage: M.set_monomials('A') + sage: M.set_variables('A') sage: x A(1,0)^-2 A(1,1)^-2 A(2,0)^-4 A(2,1)^-2 A(3,0)^-2 - sage: M.set_monomials('Y') + sage: M.set_variables('Y') """ - return getattr(self, '_repr_' + self.parent()._monomial)() + return getattr(self, '_repr_' + self.parent()._variable)() def _repr_Y(self): r""" @@ -290,12 +290,12 @@ def _latex_(self): sage: x = M.module_generators[0].f_string([1,0,2]) sage: latex(x) Y_{0,0}^{-1} Y_{1,0}^{-1} Y_{1,1}^{2} Y_{2,0} Y_{2,1}^{-1} - sage: M.set_monomials('A') + sage: M.set_variables('A') sage: latex(x) A_{0,0}^{-1} A_{1,0}^{-1} A_{2,0}^{-1} - sage: M.set_monomials('Y') + sage: M.set_variables('Y') """ - return getattr(self, '_latex_' + self.parent()._monomial)() + return getattr(self, '_latex_' + self.parent()._variable)() def _latex_Y(self): r""" @@ -581,7 +581,7 @@ def e(self, i): None] sage: M = crystals.infinity.NakajimaMonomials(['D',4,1]) - sage: M.set_monomials('A') + sage: M.set_variables('A') sage: m = M.module_generators[0].f_string([4,2,3,0]) sage: [m.e(i) for i in M.index_set()] [A(2,1)^-1 A(3,1)^-1 A(4,0)^-1, @@ -589,7 +589,7 @@ def e(self, i): None, A(0,2)^-1 A(2,1)^-1 A(4,0)^-1, None] - sage: M.set_monomials('Y') + sage: M.set_variables('Y') """ if i not in self.parent().index_set(): raise ValueError("i must be an element of the index set") @@ -711,7 +711,7 @@ class InfinityCrystalOfNakajimaMonomials(UniqueRepresentation, Parent): where `\{h_i : i \in I\}` and `\{\Lambda_i : i \in I \}` are the simple coroots and fundamental weights, respectively. With a chosen set of non-negative integers `C = (c_{ij})_{i\neq j}` such that - `c_{ij} + c{ji} = 1`, one defines + `c_{ij} + c_{ji} = 1`, one defines .. MATH:: @@ -737,7 +737,7 @@ class InfinityCrystalOfNakajimaMonomials(UniqueRepresentation, Parent): - ``cartan_type`` -- a Cartan type - ``c`` -- (optional) the matrix `(c_{ij})_{i,j \in I}` such that - `c_{ii} = 0` for all `i \in I`, `c_{ij} \in ZZ_{>0}` for all + `c_{ii} = 0` for all `i \in I`, `c_{ij} \in \ZZ_{>0}` for all `i,j \in I`, and `c_{ij} + c_{ji} = 1` for all `i \neq j`; the default is `c_{ij} = 0` if `i < j` and `0` otherwise @@ -847,14 +847,14 @@ def __classcall_private__(cls, ct, c=None, use_Y=None): """ if use_Y is not None: from sage.misc.superseded import deprecation - deprecation(18895, 'use_Y is deprecated; use the set_monomials() method instead.') + deprecation(18895, 'use_Y is deprecated; use the set_variables() method instead.') cartan_type = CartanType(ct) n = len(cartan_type.index_set()) c = InfinityCrystalOfNakajimaMonomials._normalize_c(c, n) M = super(InfinityCrystalOfNakajimaMonomials, cls).__classcall__(cls, cartan_type, c) if use_Y: - M.set_monomials('A') + M.set_variables('A') return M def __init__(self, ct, c, category=None): @@ -866,7 +866,7 @@ def __init__(self, ct, c, category=None): """ self._cartan_type = ct self._c = c - self._monomial = 'Y' + self._variable = 'Y' if category is None: category = (HighestWeightCrystals(), InfiniteEnumeratedSets()) @@ -999,12 +999,12 @@ def weight_lattice_realization(self): return F.weight_lattice(extended=True) return F.weight_lattice() - def set_monomials(self, letter): + def set_variables(self, letter): r""" Set the type of monomials to use for the element output. If the `A` variables are used, the output is written as - `\prod_i Y_{i0}^{\lambda_i} \prod_{ik} A_{ik}^c_{ik}`, where + `\prod_{i\in I} Y_{i,0}^{\lambda_i} \prod_{i,k} A_{i,k}^{c_{i,k}}`, where `\sum_{i \in I} \lambda_i \Lambda_i` is the corresponding dominant weight. @@ -1012,8 +1012,8 @@ def set_monomials(self, letter): - ``letter`` -- can be one of the following: - * ``'Y'`` - use `Y_{ik}`, corresponds to fundamental weights - * ``'A'`` - use `A_{ik}`, corresponds to simple roots + * ``'Y'`` - use `Y_{i,k}`, corresponds to fundamental weights + * ``'A'`` - use `A_{i,k}`, corresponds to simple roots EXAMPLES:: @@ -1021,10 +1021,10 @@ def set_monomials(self, letter): sage: elt = M.highest_weight_vector().f_string([2,1,3,2,3,2,4,3]) sage: elt Y(1,2) Y(2,0)^-1 Y(2,2)^-1 Y(3,0)^-1 Y(3,2)^-1 Y(4,0) - sage: M.set_monomials('A') + sage: M.set_variables('A') sage: elt A(1,1)^-1 A(2,0)^-1 A(2,1)^-2 A(3,0)^-2 A(3,1)^-1 A(4,0)^-1 - sage: M.set_monomials('Y') + sage: M.set_variables('Y') :: @@ -1033,26 +1033,26 @@ def set_monomials(self, letter): sage: lw = M.lowest_weight_vectors()[0] sage: lw Y(1,2)^-1 Y(2,1)^-1 - sage: M.set_monomials('A') + sage: M.set_variables('A') sage: lw Y(1,0) Y(2,0) A(1,0)^-1 A(1,1)^-1 A(2,0)^-2 - sage: M.set_monomials('Y') + sage: M.set_variables('Y') """ if letter not in ['Y', 'A']: raise ValueError("invalid monomial type") - self._monomial = letter + self._variable = letter - def get_monomials(self): + def get_variables(self): """ Return the type of monomials to use for the element output. EXAMPLES:: sage: M = crystals.infinity.NakajimaMonomials(['A', 4]) - sage: M.get_monomials() + sage: M.get_variables() 'Y' """ - return self._monomial + return self._variable Element = NakajimaMonomial @@ -1098,7 +1098,7 @@ def f(self, i): :: sage: M = crystals.infinity.NakajimaMonomials("E8") - sage: M.set_monomials('A') + sage: M.set_variables('A') sage: m = M.module_generators[0].f_string([4,2,3,8]) sage: m A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1 @@ -1111,7 +1111,7 @@ def f(self, i): A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(6,0)^-1 A(8,0)^-1, A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(7,1)^-1 A(8,0)^-1, A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-2] - sage: M.set_monomials('Y') + sage: M.set_variables('Y') """ if self.phi(i) == 0: return None From e010047863d35c5ff7bd939027c2a5c14abb7a8e Mon Sep 17 00:00:00 2001 From: Ben Salisbury Date: Thu, 14 Apr 2016 16:49:58 -0400 Subject: [PATCH 126/163] fixed doctest error --- src/doc/en/thematic_tutorials/lie/infinity_crystals.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst b/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst index 80b25ca1b36..a237896ecc1 100644 --- a/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst +++ b/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst @@ -255,9 +255,8 @@ We can also model `B(\infty)` using the variables `A_{i,k}` instead:: sage: Minf = crystals.infinity.NakajimaMonomials(['C',3,1]) sage: minf = Minf.highest_weight_vector() - sage: m = minf.f_string([0,1,2,3,2,1,0]); m sage: Minf.set_variables('A') - sage: m + sage: m = minf.f_string([0,1,2,3,2,1,0]); m A(0,0)^-1 A(0,3)^-1 A(1,0)^-1 A(1,2)^-1 A(2,0)^-1 A(2,1)^-1 A(3,0)^-1 sage: m.weight() -2*Lambda[0] + 2*Lambda[1] - 2*delta From 6c7236e3c3934fbd8b0a4b473acaa5b2243cd856 Mon Sep 17 00:00:00 2001 From: Marc Masdeu Date: Thu, 14 Apr 2016 21:50:42 +0100 Subject: [PATCH 127/163] Revert "Deprecated and removed from global namesplace the function magma_version." This reverts commit c079a14424e9a1ea77179c658284b867b8401ef8. --- src/sage/interfaces/all.py | 9 +-------- src/sage/interfaces/magma.py | 2 -- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/sage/interfaces/all.py b/src/sage/interfaces/all.py index 11e2544dfb1..e751feff750 100644 --- a/src/sage/interfaces/all.py +++ b/src/sage/interfaces/all.py @@ -15,14 +15,7 @@ from gnuplot import gnuplot from kash import kash, kash_version, Kash from lisp import lisp, Lisp -from magma import magma, Magma -from sage.misc.superseded import deprecated_callable_import -deprecated_callable_import(20388, - 'sage.interfaces.magma', - globals(), - locals(), - ["magma_version"], -"This function has been deprecated. Use magma.version() instead.") +from magma import magma, magma_version, Magma from magma_free import magma_free from macaulay2 import macaulay2, Macaulay2 from maple import maple, Maple diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index 882ac64688b..d7546b0fcab 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -2774,8 +2774,6 @@ def magma_version(): Return the version of Magma that you have in your PATH on your computer. - It has been deprecated, use instead :meth:`sage.interfaces.magma.Magma.version`. - OUTPUT: From f4996488101ac3ce85550fb14b2eec08185408a6 Mon Sep 17 00:00:00 2001 From: Marc Masdeu Date: Thu, 14 Apr 2016 22:07:40 +0100 Subject: [PATCH 128/163] Deprecated magma_version instead of its importing. --- src/sage/interfaces/magma.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index d7546b0fcab..da041f83582 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -2787,8 +2787,9 @@ def magma_version(): sage: magma_version() # random, optional - magma ((2, 14, 9), 'V2.14-9') """ - t = tuple([int(n) for n in magma.eval('GetVersion()').split()]) - return t, 'V%s.%s-%s'%t + from sage.misc.superseded import deprecation + deprecation(20388, 'This function has been deprecated. Use magma.version() instead.') + return magma.version() class MagmaGBLogPrettyPrinter: """ From 37e87a5df7de14853e63074e0d07d678e61b1029 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 14 Apr 2016 15:45:26 -0700 Subject: [PATCH 129/163] Replace delsarte test by one that does not expose a bug in GLPK exact --- src/sage/numerical/backends/generic_backend.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index d5f2f1626fa..0e7b7af5822 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -1365,11 +1365,11 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None, ba ....: b = get_solver(solver="GLPK") ....: b.solver_parameter("simplex_or_intopt", "exact_simplex_only") ....: return b - sage: delsarte_bound_additive_hamming_space(19,15,7,solver=glpk_exact_solver) # long time + sage: delsarte_bound_additive_hamming_space(11,3,4,solver=glpk_exact_solver) # long time glp_exact... ... OPTIMAL SOLUTION FOUND - 2 + 8 """ if solver is None: From 8f968edfcf9e0ae5bd59f9b0c24142273d4637fe Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 9 Apr 2016 17:25:43 -0700 Subject: [PATCH 130/163] _test_copy: New --- .../numerical/backends/generic_backend.pyx | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index b726b29ab0e..753b8d5b9b6 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -29,6 +29,8 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** +from copy import copy + cdef class GenericBackend: cpdef base_ring(self): @@ -1058,6 +1060,36 @@ cdef class GenericBackend: """ raise NotImplementedError() + def _test_copy(self, **options): + """ + Test whether the backend can be copied + and at least the problem data of the copy is equal to that of the original. + Does not test whether solutions or solver parameters are copied. + """ + tester = self._tester(**options) + cp = copy(self) + tester.assertEqual(self.ncols(), cp.ncols()) + tester.assertEqual(self.nrows(), cp.nrows()) + tester.assertEqual(self.objective_constant_term(), cp.objective_constant_term()) + tester.assertEqual(self.problem_name(), cp.problem_name()) + tester.assertEqual(self.is_maximization(), cp.is_maximization()) + for i in range(self.ncols()): + tester.assertEqual(self.objective_coefficient(i) == cp.objective_coefficient(i)) + tester.assertEqual(self.is_variable_binary(i) == cp.is_variable_binary(i)) + tester.assertEqual(self.is_variable_integer(i) == cp.is_variable_integer(i)) + tester.assertEqual(self.is_variable_continuous(i) == cp.is_variable_continuous(i)) + tester.assertEqual(self.col_bounds(i) == cp.col_bounds(i)) + # don't test variable_lower_bound, variable_upper_bound because we already test col_bounds. + # TODO: Add a test elsewhere to ensure that variable_lower_bound, variable_upper_bound + # are consistent with col_bounds. + tester.assertEqual(self.col_name(i) == cp.col_name(i)) + for i in range(self.nrows()): + tester.assertEqual(self.row_bounds(i) == cp.row_bounds(i)) + tester.assertEqual(self.row(i) == cp.row(i)) + tester.assertEqual(self.row_name(i) == cp.row_name(i)) + + # TODO: Add a test class method that calls _test_copy on some populated MIP. + cpdef variable_upper_bound(self, int index, value = False): """ Return or define the upper bound on a variable From 3e26c4d19ab3533cdbe2002d7539083086aa2bce Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 9 Apr 2016 17:30:42 -0700 Subject: [PATCH 131/163] _test_copy_does_not_share_data: New --- .../numerical/backends/generic_backend.pyx | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 753b8d5b9b6..c65daf54b12 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -1060,14 +1060,7 @@ cdef class GenericBackend: """ raise NotImplementedError() - def _test_copy(self, **options): - """ - Test whether the backend can be copied - and at least the problem data of the copy is equal to that of the original. - Does not test whether solutions or solver parameters are copied. - """ - tester = self._tester(**options) - cp = copy(self) + def _do_test_problem_data(self, tester, cp): tester.assertEqual(self.ncols(), cp.ncols()) tester.assertEqual(self.nrows(), cp.nrows()) tester.assertEqual(self.objective_constant_term(), cp.objective_constant_term()) @@ -1087,6 +1080,26 @@ cdef class GenericBackend: tester.assertEqual(self.row_bounds(i) == cp.row_bounds(i)) tester.assertEqual(self.row(i) == cp.row(i)) tester.assertEqual(self.row_name(i) == cp.row_name(i)) + + def _test_copy(self, **options): + """ + Test whether the backend can be copied + and at least the problem data of the copy is equal to that of the original. + Does not test whether solutions or solver parameters are copied. + """ + tester = self._tester(**options) + cp = copy(self) + self._do_test_problem_data(tester, cp) + + def _test_copy_does_not_share_data(self, **options): + """ + Test whether copy makes an independent copy of the backend. + """ + tester = self._tester(**options) + cp = copy(self) + cpcp = copy(cp) + del cp + self._do_test_problem_data(tester, cpcp) # TODO: Add a test class method that calls _test_copy on some populated MIP. From cdd159d165c33eff92e006f435a3a0c7384a2487 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 9 Apr 2016 18:39:02 -0700 Subject: [PATCH 132/163] test_copy_some_mips: New --- .../numerical/backends/generic_backend.pyx | 58 +++++++++++++------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index c65daf54b12..f73edd18178 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -1061,25 +1061,37 @@ cdef class GenericBackend: raise NotImplementedError() def _do_test_problem_data(self, tester, cp): - tester.assertEqual(self.ncols(), cp.ncols()) - tester.assertEqual(self.nrows(), cp.nrows()) - tester.assertEqual(self.objective_constant_term(), cp.objective_constant_term()) - tester.assertEqual(self.problem_name(), cp.problem_name()) - tester.assertEqual(self.is_maximization(), cp.is_maximization()) - for i in range(self.ncols()): - tester.assertEqual(self.objective_coefficient(i) == cp.objective_coefficient(i)) - tester.assertEqual(self.is_variable_binary(i) == cp.is_variable_binary(i)) - tester.assertEqual(self.is_variable_integer(i) == cp.is_variable_integer(i)) - tester.assertEqual(self.is_variable_continuous(i) == cp.is_variable_continuous(i)) - tester.assertEqual(self.col_bounds(i) == cp.col_bounds(i)) + """ + TESTS: + + Test, with an actual working backend, that comparing a problem with itself works:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver='GLPK') + sage: tester = p._tester() + sage: p._do_test_problem_data(tester, p) + """ + def assert_equal_problem_data(method): + tester.assertEqual(getattr(self, method)(), getattr(cp, method)(), + "{} does not match".format(method)) + for method in ("ncols", "nrows", "objective_constant_term", "problem_name", "is_maximization"): + assert_equal_problem_data(method) + def assert_equal_col_data(method): + for i in range(self.ncols()): + tester.assertEqual(getattr(self, method)(i), getattr(cp, method)(i), + "{}({}) does not match".format(method, i)) + for method in ("objective_coefficient", "is_variable_binary", "is_variable_binary", "is_variable_integer", + "is_variable_continuous", "col_bounds", "col_name"): # don't test variable_lower_bound, variable_upper_bound because we already test col_bounds. # TODO: Add a test elsewhere to ensure that variable_lower_bound, variable_upper_bound # are consistent with col_bounds. - tester.assertEqual(self.col_name(i) == cp.col_name(i)) - for i in range(self.nrows()): - tester.assertEqual(self.row_bounds(i) == cp.row_bounds(i)) - tester.assertEqual(self.row(i) == cp.row(i)) - tester.assertEqual(self.row_name(i) == cp.row_name(i)) + assert_equal_col_data(method) + def assert_equal_row_data(method): + for i in range(self.nrows()): + tester.assertEqual(getattr(self, method)(i), getattr(cp, method)(i), + "{}({}) does not match".format(method, i)) + for method in ("row_bounds", "row", "row_name"): + assert_equal_row_data(method) def _test_copy(self, **options): """ @@ -1101,7 +1113,19 @@ cdef class GenericBackend: del cp self._do_test_problem_data(tester, cpcp) - # TODO: Add a test class method that calls _test_copy on some populated MIP. + # TODO: We should have a more systematic way of generating MIPs for testing. + @classmethod + def _test_copy_some_mips(cls, tester=None, **options): + p = cls() # fresh instance of the backend + if tester is None: + tester = p._tester(**options) + # From doctest of GenericBackend.solve: + p.add_linear_constraints(5, 0, None) + p.add_col(range(5), range(5)) + # From doctest of GenericBackend.problem_name: + p.problem_name("There once was a french fry") + p._test_copy(**options) + p._test_copy_does_not_share_data(**options) cpdef variable_upper_bound(self, int index, value = False): """ From 5da5f894a8fcb211e1dd60e407a61c1d37d5a153 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 14 Apr 2016 21:39:45 -0500 Subject: [PATCH 133/163] Fixing the handling of (deprecated) use_Y. --- src/sage/combinat/crystals/monomial_crystals.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/crystals/monomial_crystals.py b/src/sage/combinat/crystals/monomial_crystals.py index 165a11f9cf7..cd3c7947a89 100644 --- a/src/sage/combinat/crystals/monomial_crystals.py +++ b/src/sage/combinat/crystals/monomial_crystals.py @@ -848,13 +848,17 @@ def __classcall_private__(cls, ct, c=None, use_Y=None): if use_Y is not None: from sage.misc.superseded import deprecation deprecation(18895, 'use_Y is deprecated; use the set_variables() method instead.') + else: + use_Y = True cartan_type = CartanType(ct) n = len(cartan_type.index_set()) c = InfinityCrystalOfNakajimaMonomials._normalize_c(c, n) M = super(InfinityCrystalOfNakajimaMonomials, cls).__classcall__(cls, cartan_type, c) - if use_Y: + if not use_Y: M.set_variables('A') + else: + M.set_variables('Y') return M def __init__(self, ct, c, category=None): From 4b9fd051aa512b59351de115b21c744d9c312e8e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 14 Apr 2016 19:43:17 -0700 Subject: [PATCH 134/163] InteractiveLPBackend.__copy__: New --- .../backends/interactivelp_backend.pyx | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/sage/numerical/backends/interactivelp_backend.pyx b/src/sage/numerical/backends/interactivelp_backend.pyx index fcbc260bd32..c2d3094a6a9 100644 --- a/src/sage/numerical/backends/interactivelp_backend.pyx +++ b/src/sage/numerical/backends/interactivelp_backend.pyx @@ -1,5 +1,5 @@ r""" -COIN Backend +InteractiveLP Backend AUTHORS: @@ -22,6 +22,7 @@ AUTHORS: from sage.numerical.mip import MIPSolverException from sage.numerical.interactive_simplex_method import InteractiveLPProblem, default_variable_name from sage.modules.all import vector +from copy import copy cdef class InteractiveLPBackend: """ @@ -86,6 +87,28 @@ cdef class InteractiveLPBackend: self.row_names = [] + cpdef __copy__(self): + """ + Returns a copy of self. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = MixedIntegerLinearProgram(solver = "InteractiveLP") + sage: b = p.new_variable() + sage: p.add_constraint(b[1] + b[2] <= 6) + sage: p.set_objective(b[1] + b[2]) + sage: p.get_backend().solve() + 0 + sage: p.get_backend().get_objective_value() + 6 + """ + cp = InteractiveLPBackend(base_ring=self.base_ring()) + cp.lp = self.lp # it's considered immutable; so no need to copy. + cp.row_names = copy(self.row_names) + cp.prob_name = self.prob_name + return cp + cpdef base_ring(self): """ Return the base ring. From fe1a8b91506ef368912ec0e89b3083cb03fa7f01 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 15 Apr 2016 00:18:19 -0700 Subject: [PATCH 135/163] PPLBackend: Implement __copy__ --- src/sage/numerical/backends/ppl_backend.pyx | 34 +++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/sage/numerical/backends/ppl_backend.pyx b/src/sage/numerical/backends/ppl_backend.pyx index ce1053c503b..73d022d4052 100644 --- a/src/sage/numerical/backends/ppl_backend.pyx +++ b/src/sage/numerical/backends/ppl_backend.pyx @@ -23,6 +23,7 @@ from sage.numerical.mip import MIPSolverException from sage.libs.ppl import MIP_Problem, Variable, Variables_Set, Linear_Expression, Constraint, Generator from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational +from copy import copy cdef class PPLBackend(GenericBackend): @@ -98,6 +99,39 @@ cdef class PPLBackend(GenericBackend): cpdef zero(self): return self.base_ring()(0) + cpdef __copy__(self): + """ + Returns a copy of self. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = MixedIntegerLinearProgram(solver = "PPL") + sage: b = p.new_variable() + sage: p.add_constraint(b[1] + b[2] <= 6) + sage: p.set_objective(b[1] + b[2]) + sage: cp = copy(p.get_backend()) + sage: cp.solve() + 0 + sage: cp.get_objective_value() + 6 + """ + cp = PPLBackend() + cp.Matrix = [row[:] for row in self.Matrix] + cp.row_lower_bound = self.row_lower_bound[:] + cp.row_upper_bound = self.row_upper_bound[:] + cp.col_lower_bound = self.col_lower_bound[:] + cp.col_upper_bound = self.col_upper_bound[:] + cp.objective_function = self.objective_function[:] + cp.row_name_var = self.row_name_var[:] + cp.col_name_var = self.col_name_var[:] + cp.name = self.name + cp.obj_constant_term = self.obj_constant_term + cp.obj_denominator = self.obj_denominator + cp.integer_variables = copy(self.integer_variables) + cp.is_maximize = self.is_maximize + return cp + def init_mip(self): """ Converting the matrix form of the MIP Problem to PPL MIP_Problem. From d9f1ec8e2d2f21a384063da3303bb26aab0f3867 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 15 Apr 2016 00:19:06 -0700 Subject: [PATCH 136/163] CVXOPTBackend: Implement __copy__ --- .../numerical/backends/cvxopt_backend.pyx | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/sage/numerical/backends/cvxopt_backend.pyx b/src/sage/numerical/backends/cvxopt_backend.pyx index 5cc7c003281..c736f3cc133 100644 --- a/src/sage/numerical/backends/cvxopt_backend.pyx +++ b/src/sage/numerical/backends/cvxopt_backend.pyx @@ -19,6 +19,7 @@ AUTHORS: from sage.numerical.mip import MIPSolverException from cvxopt import solvers +from copy import copy cdef class CVXOPTBackend(GenericBackend): """ @@ -95,6 +96,43 @@ cdef class CVXOPTBackend(GenericBackend): else: self.set_sense(-1) + cpdef __copy__(self): + # Added a second inequality to this doctest, + # because otherwise CVXOPT complains: ValueError: Rank(A) < p or Rank([G; A]) < n + """ + Returns a copy of self. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = MixedIntegerLinearProgram(solver = "CVXOPT") + sage: b = p.new_variable() + sage: p.add_constraint(b[1] + b[2] <= 6) + sage: p.add_constraint(b[2] <= 5) + sage: p.set_objective(b[1] + b[2]) + sage: cp = copy(p.get_backend()) + sage: cp.solve() + 0 + sage: cp.get_objective_value() + 6.0 + """ + cp = CVXOPTBackend() + cp.objective_function = self.objective_function[:] + cp.G_matrix = [row[:] for row in self.G_matrix] + cp.prob_name = self.prob_name + cp.obj_constant_term = self.obj_constant_term + cp.is_maximize = self.is_maximize + + cp.row_lower_bound = self.row_lower_bound[:] + cp.row_upper_bound = self.row_upper_bound[:] + cp.col_lower_bound = self.col_lower_bound[:] + cp.col_upper_bound = self.col_upper_bound[:] + + cp.row_name_var = self.row_name_var[:] + cp.col_name_var = self.col_name_var[:] + + cp.param = copy(self.param) + return cp cpdef int add_variable(self, lower_bound=0.0, upper_bound=None, binary=False, continuous=True, integer=False, obj=None, name=None) except -1: """ From 751d621339a105527fe9c9f4592f20333a4d3003 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 15 Apr 2016 00:20:42 -0700 Subject: [PATCH 137/163] InteractiveLPBackend.__copy__: Fix doctest --- src/sage/numerical/backends/interactivelp_backend.pyx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/numerical/backends/interactivelp_backend.pyx b/src/sage/numerical/backends/interactivelp_backend.pyx index c2d3094a6a9..b6fece90654 100644 --- a/src/sage/numerical/backends/interactivelp_backend.pyx +++ b/src/sage/numerical/backends/interactivelp_backend.pyx @@ -98,9 +98,10 @@ cdef class InteractiveLPBackend: sage: b = p.new_variable() sage: p.add_constraint(b[1] + b[2] <= 6) sage: p.set_objective(b[1] + b[2]) - sage: p.get_backend().solve() + sage: cp = copy(p.get_backend()) + sage: cp.solve() 0 - sage: p.get_backend().get_objective_value() + sage: cp.get_objective_value() 6 """ cp = InteractiveLPBackend(base_ring=self.base_ring()) From 9d39e0cb57487eb9f3215f5c8424b981aeeb669d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 15 Apr 2016 00:21:14 -0700 Subject: [PATCH 138/163] CoinBackend: In add_col, don't forget to append to col_names --- src/sage/numerical/backends/coin_backend.pyx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/numerical/backends/coin_backend.pyx b/src/sage/numerical/backends/coin_backend.pyx index 7aee6f6ae22..7556e0b27df 100644 --- a/src/sage/numerical/backends/coin_backend.pyx +++ b/src/sage/numerical/backends/coin_backend.pyx @@ -735,6 +735,9 @@ cdef class CoinBackend(GenericBackend): self.si.addCol (1, c_indices, c_values, 0, self.si.getInfinity(), 0) + self.col_names.append("") + + cpdef int solve(self) except -1: r""" Solves the problem. From 7c9709775df95560278a9a5ff776b4033c48882f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 15 Apr 2016 00:21:50 -0700 Subject: [PATCH 139/163] GenericBackend.__copy__: Fix doctest --- src/sage/numerical/backends/generic_backend.pyx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index f73edd18178..1506e85a511 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -858,8 +858,10 @@ cdef class GenericBackend: sage: b = p.new_variable() # optional - Nonexistent_LP_solver sage: p.add_constraint(b[1] + b[2] <= 6) # optional - Nonexistent_LP_solver sage: p.set_objective(b[1] + b[2]) # optional - Nonexistent_LP_solver - sage: p.get_backend().solve(); # optional - Nonexistent_LP_solver - sage: p.get_backend().get_objective_value() # optional - Nonexistent_LP_solver + sage: cp = copy(p.get_backend()) # optional - Nonexistent_LP_solver + sage: cp.solve() # optional - Nonexistent_LP_solver + 0 + sage: cp.get_objective_value() # optional - Nonexistent_LP_solver 6.0 """ raise NotImplementedError() From ce10c703cf8922c1cb3c3d0f4295727efe322cc3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 15 Apr 2016 00:23:03 -0700 Subject: [PATCH 140/163] GenericBackend._test_copy_some_mips: Change test so it does not use 0 coefficients --- src/sage/numerical/backends/generic_backend.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 1506e85a511..003e5785f02 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -1123,7 +1123,8 @@ cdef class GenericBackend: tester = p._tester(**options) # From doctest of GenericBackend.solve: p.add_linear_constraints(5, 0, None) - p.add_col(range(5), range(5)) + # p.add_col(range(5), range(5)) -- bad test because COIN sparsifies the 0s away on copy + p.add_col(range(5), range(1, 6)) # From doctest of GenericBackend.problem_name: p.problem_name("There once was a french fry") p._test_copy(**options) From 580e913062ab40b595fdb508d94b06c44a384f40 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 15 Apr 2016 00:23:43 -0700 Subject: [PATCH 141/163] GenericBackend._do_test_problem_data: Compare types as well --- src/sage/numerical/backends/generic_backend.pyx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 003e5785f02..3515d102eb6 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -1073,6 +1073,8 @@ cdef class GenericBackend: sage: tester = p._tester() sage: p._do_test_problem_data(tester, p) """ + tester.assertEqual(type(self), type(cp), + "Classes do not match") def assert_equal_problem_data(method): tester.assertEqual(getattr(self, method)(), getattr(cp, method)(), "{} does not match".format(method)) From f8e16b68014544b96698dc11c8fa90e4a187e3d3 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Fri, 15 Apr 2016 09:38:05 +0200 Subject: [PATCH 142/163] misplaced fmpz_clear --- src/sage/rings/real_arb.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/real_arb.pyx b/src/sage/rings/real_arb.pyx index 624acc8b19f..f97557eab33 100644 --- a/src/sage/rings/real_arb.pyx +++ b/src/sage/rings/real_arb.pyx @@ -348,10 +348,10 @@ cdef int real_part_of_quadratic_element_to_arb(arb_t res, continue break if _do_sig(myprec): sig_on() - arb_clear(rootD) - fmpz_clear(tmpz) fmpz_set_mpz(tmpz, x.denom) arb_div_fmpz(res, res, tmpz, prec) + arb_clear(rootD) + fmpz_clear(tmpz) if _do_sig(myprec): sig_off() return 0 From 5ec9040c9bbf46103eec0505cfec1e350f414dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 15 Apr 2016 09:54:52 +0200 Subject: [PATCH 143/163] trac #20435 fixing the parents in factorisation of integer multi-polynomials --- .../polynomial/multi_polynomial_libsingular.pyx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index c5065d40638..d64ce04c1b8 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -4198,7 +4198,16 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn sage: R. = K[] sage: f = (a + 1)*x^145*y^84 + (a + 1)*x^205*y^17 + x^32*y^112 + x^92*y^45 sage: for i in range(100): - ... assert len(f.factor()) == 4 + ....: assert len(f.factor()) == 4 + + Test for :trac:`20345`:: + + sage: x,y = polygen(ZZ,'x,y') + sage: p = x**2-y**2 + sage: z = factor(p); z + (x - y) * (x + y) + sage: z[0][0].parent() + Multivariate Polynomial Ring in x, y over Integer Ring """ cdef ring *_ring = self._parent_ring cdef poly *ptemp @@ -4217,7 +4226,7 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn try: frac_field = self._parent._base.fraction_field() F = self.change_ring(frac_field).factor() - FF = [(f[0].change_ring(self._parent), f[1]) for f in F] + FF = [(self._parent(f[0]), f[1]) for f in F] U = self._parent._base(F.unit()).factor() return Factorization(list(U) + FF, unit=U.unit()) except Exception: From 3e28bfc536ac1b69c365213f4ff385874b6aee1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 15 Apr 2016 10:01:02 +0200 Subject: [PATCH 144/163] trac #20435 correct link to trac --- src/sage/rings/polynomial/multi_polynomial_libsingular.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index d64ce04c1b8..4fa3e73e8f8 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -4200,7 +4200,7 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn sage: for i in range(100): ....: assert len(f.factor()) == 4 - Test for :trac:`20345`:: + Test for :trac:`20435`:: sage: x,y = polygen(ZZ,'x,y') sage: p = x**2-y**2 From 39642ea067ed12f8e0fd28f8da481720d7c5778e Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 15 Apr 2016 11:04:36 -0500 Subject: [PATCH 145/163] Make sure for the iterator that k is an int. --- src/sage/combinat/integer_vector.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/integer_vector.py b/src/sage/combinat/integer_vector.py index 1df0bb12512..a3ee179a5ec 100644 --- a/src/sage/combinat/integer_vector.py +++ b/src/sage/combinat/integer_vector.py @@ -781,14 +781,15 @@ def __iter__(self): rem = -1 # Amount remaining cur = [self.n+1] + k = int(self.k) while cur: cur[-1] -= 1 rem += 1 if rem == 0: - yield cur + [Integer(0)] * (self.k - len(cur)) + yield cur + [Integer(0)] * (k - len(cur)) elif cur[-1] < 0: rem += cur.pop() - elif len(cur) == self.k - 1: + elif len(cur) == k - 1: yield cur + [Integer(rem)] else: cur.append(rem + 1) From 16cf997700988f28928f94f2a813c9c053e4acc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 15 Apr 2016 21:18:04 +0200 Subject: [PATCH 146/163] trac #19599 one minor change in doc --- 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 3d8dc992216..efdf8beac88 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -15673,7 +15673,7 @@ def wiener_index(self, by_weight=False, algorithm=None, r""" Return the Wiener index of the graph. - The graph is expected to not have cycles of negative weight. + The graph is expected to have no cycles of negative weight. The Wiener index of a graph `G` is `W(G) = \frac 1 2 \sum_{u,v\in G} d(u,v)` From 27e6918f60edab24310cc38c14c3b0f400dd8736 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sat, 16 Apr 2016 12:19:11 +0200 Subject: [PATCH 147/163] Update to newest PyPI versions --- build/pkgs/nbconvert/checksums.ini | 6 +++--- build/pkgs/nbconvert/package-version.txt | 2 +- build/pkgs/notebook/checksums.ini | 6 +++--- build/pkgs/notebook/package-version.txt | 2 +- build/pkgs/numpy/checksums.ini | 6 +++--- build/pkgs/numpy/package-version.txt | 2 +- build/pkgs/pickleshare/checksums.ini | 6 +++--- build/pkgs/pickleshare/package-version.txt | 2 +- build/pkgs/pillow/checksums.ini | 6 +++--- build/pkgs/pillow/package-version.txt | 2 +- build/pkgs/setuptools_scm/checksums.ini | 6 +++--- build/pkgs/setuptools_scm/package-version.txt | 2 +- build/pkgs/vcversioner/checksums.ini | 6 +++--- build/pkgs/vcversioner/package-version.txt | 2 +- 14 files changed, 28 insertions(+), 28 deletions(-) diff --git a/build/pkgs/nbconvert/checksums.ini b/build/pkgs/nbconvert/checksums.ini index 212e5fa11aa..06666ba7e64 100644 --- a/build/pkgs/nbconvert/checksums.ini +++ b/build/pkgs/nbconvert/checksums.ini @@ -1,4 +1,4 @@ tarball=nbconvert-VERSION.tar.gz -sha1=079fd51c75e5c8b76f9679aa7fc5ad8176004720 -md5=58ee16f5da0dd2976ff992511d6bfb43 -cksum=75346878 +sha1=0f3c792cc624e09b2524cd0a010618036647a8cd +md5=710e356c4effcfddd29a3c10f1d8c3e6 +cksum=137274062 diff --git a/build/pkgs/nbconvert/package-version.txt b/build/pkgs/nbconvert/package-version.txt index ee74734aa22..6aba2b245a8 100644 --- a/build/pkgs/nbconvert/package-version.txt +++ b/build/pkgs/nbconvert/package-version.txt @@ -1 +1 @@ -4.1.0 +4.2.0 diff --git a/build/pkgs/notebook/checksums.ini b/build/pkgs/notebook/checksums.ini index e73b5a93040..8ba4997e1b6 100644 --- a/build/pkgs/notebook/checksums.ini +++ b/build/pkgs/notebook/checksums.ini @@ -1,4 +1,4 @@ tarball=notebook-VERSION.tar.gz -sha1=cf953bcaf73ba43e7ef0ce829126b6605105dd0f -md5=d390cf1a0785a43711a2548c8f3c91b8 -cksum=2636243005 +sha1=f5466b2f04e1b9c913dcf79f7e03812eba718d0d +md5=3a72bb93f6f01caec69e5e65718f5f7f +cksum=3858938777 diff --git a/build/pkgs/notebook/package-version.txt b/build/pkgs/notebook/package-version.txt index ee74734aa22..6aba2b245a8 100644 --- a/build/pkgs/notebook/package-version.txt +++ b/build/pkgs/notebook/package-version.txt @@ -1 +1 @@ -4.1.0 +4.2.0 diff --git a/build/pkgs/numpy/checksums.ini b/build/pkgs/numpy/checksums.ini index 8afe164d6cd..33d0cc00e8d 100644 --- a/build/pkgs/numpy/checksums.ini +++ b/build/pkgs/numpy/checksums.ini @@ -1,4 +1,4 @@ tarball=numpy-VERSION.tar.gz -sha1=46a653d3a3e474bf284cbf213c2ce5fa85c39371 -md5=aed294de0aa1ac7bd3f9745f4f1968ad -cksum=4106390035 +sha1=3e43596cba1d5df4002dd0c87d4041f31ea6e1b5 +md5=bc56fb9fc2895aa4961802ffbdb31d0b +cksum=906704492 diff --git a/build/pkgs/numpy/package-version.txt b/build/pkgs/numpy/package-version.txt index 45419ec63fd..1cac385c6cb 100644 --- a/build/pkgs/numpy/package-version.txt +++ b/build/pkgs/numpy/package-version.txt @@ -1 +1 @@ -1.10.4.p1 +1.11.0 diff --git a/build/pkgs/pickleshare/checksums.ini b/build/pkgs/pickleshare/checksums.ini index a385506705a..69926ba959d 100644 --- a/build/pkgs/pickleshare/checksums.ini +++ b/build/pkgs/pickleshare/checksums.ini @@ -1,4 +1,4 @@ tarball=pickleshare-VERSION.tar.gz -sha1=3f9176870b015f5c84ab4e7afb42470f78a0ed34 -md5=7fadddce8b1b0110c4ef905be795001a -cksum=2251024212 +sha1=94e109ff8b35768388d5a2466b841cd47853f81e +md5=29d74cde0255546b6b2e1b48a0b31a54 +cksum=3883458288 diff --git a/build/pkgs/pickleshare/package-version.txt b/build/pkgs/pickleshare/package-version.txt index 5a2a5806df6..7486fdbc50b 100644 --- a/build/pkgs/pickleshare/package-version.txt +++ b/build/pkgs/pickleshare/package-version.txt @@ -1 +1 @@ -0.6 +0.7.2 diff --git a/build/pkgs/pillow/checksums.ini b/build/pkgs/pillow/checksums.ini index 6cb77b8fa0f..1d6e55ce60d 100644 --- a/build/pkgs/pillow/checksums.ini +++ b/build/pkgs/pillow/checksums.ini @@ -1,4 +1,4 @@ tarball=Pillow-VERSION.tar.gz -sha1=71d8dc1dd38ba2582f7cca8b5ce70af03d19db23 -md5=d382a86c4b9b1c8de684bd00dad43bb8 -cksum=4161496668 +sha1=5381cdd06dc00a86b0221110c768d7b49c27dc56 +md5=7cfd093c11205d9e2ebe3c51dfcad510 +cksum=4253553307 diff --git a/build/pkgs/pillow/package-version.txt b/build/pkgs/pillow/package-version.txt index 94ff29cc4de..944880fa15e 100644 --- a/build/pkgs/pillow/package-version.txt +++ b/build/pkgs/pillow/package-version.txt @@ -1 +1 @@ -3.1.1 +3.2.0 diff --git a/build/pkgs/setuptools_scm/checksums.ini b/build/pkgs/setuptools_scm/checksums.ini index 79cc040e560..a228ef1930b 100644 --- a/build/pkgs/setuptools_scm/checksums.ini +++ b/build/pkgs/setuptools_scm/checksums.ini @@ -1,4 +1,4 @@ tarball=setuptools_scm-VERSION.tar.gz -sha1=c953cc4228654d151be4cd84db6391fd050eb3bf -md5=99823e2cd564b996f18820a065f0a974 -cksum=3074680780 +sha1=423fe07da9f27e0a4676f7e98f8e9f4839b6d4a4 +md5=4c5c896ba52e134bbc3507bac6400087 +cksum=1357969954 diff --git a/build/pkgs/setuptools_scm/package-version.txt b/build/pkgs/setuptools_scm/package-version.txt index 4dae2985b58..1cac385c6cb 100644 --- a/build/pkgs/setuptools_scm/package-version.txt +++ b/build/pkgs/setuptools_scm/package-version.txt @@ -1 +1 @@ -1.10.1 +1.11.0 diff --git a/build/pkgs/vcversioner/checksums.ini b/build/pkgs/vcversioner/checksums.ini index b6fc1fbc8ca..4d294a20994 100644 --- a/build/pkgs/vcversioner/checksums.ini +++ b/build/pkgs/vcversioner/checksums.ini @@ -1,4 +1,4 @@ tarball=vcversioner-VERSION.tar.gz -sha1=723c0915665aa1f01831731605acdd6afe0af9b6 -md5=7848a365ced9941053bc25d9a9f8f4b4 -cksum=343721373 +sha1=ce076b62e8f0772bf79f29762bfc3cf09f6781b5 +md5=aab6ef5e0cf8614a1b1140ed5b7f107d +cksum=1650555311 diff --git a/build/pkgs/vcversioner/package-version.txt b/build/pkgs/vcversioner/package-version.txt index a82ac72a25f..53c62eb78b9 100644 --- a/build/pkgs/vcversioner/package-version.txt +++ b/build/pkgs/vcversioner/package-version.txt @@ -1 +1 @@ -2.14.0.0 +2.16.0.0 From ab7fc10213f1a9aafa8ff8f6fccf1af6a7d857d0 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sat, 16 Apr 2016 12:19:43 +0200 Subject: [PATCH 148/163] Add pathlib2 (new pickleshare dependency) --- build/pkgs/pathlib2/SPKG.txt | 11 +++++++++++ build/pkgs/pathlib2/checksums.ini | 4 ++++ build/pkgs/pathlib2/dependencies | 5 +++++ build/pkgs/pathlib2/package-version.txt | 1 + build/pkgs/pathlib2/spkg-install | 3 +++ build/pkgs/pathlib2/type | 1 + build/pkgs/pickleshare/dependencies | 2 +- 7 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/pathlib2/SPKG.txt create mode 100644 build/pkgs/pathlib2/checksums.ini create mode 100644 build/pkgs/pathlib2/dependencies create mode 100644 build/pkgs/pathlib2/package-version.txt create mode 100755 build/pkgs/pathlib2/spkg-install create mode 100644 build/pkgs/pathlib2/type diff --git a/build/pkgs/pathlib2/SPKG.txt b/build/pkgs/pathlib2/SPKG.txt new file mode 100644 index 00000000000..3330269b541 --- /dev/null +++ b/build/pkgs/pathlib2/SPKG.txt @@ -0,0 +1,11 @@ += pathlib = + +== Description == + +Object-oriented filesystem paths + +The old pathlib module on bitbucket is in bugfix-only mode. The goal +of pathlib2 is to provide a backport of standard pathlib module which +tracks the standard library module, so all the newest features of the +standard pathlib can be used also on older Python versions. + diff --git a/build/pkgs/pathlib2/checksums.ini b/build/pkgs/pathlib2/checksums.ini new file mode 100644 index 00000000000..b5c3632d898 --- /dev/null +++ b/build/pkgs/pathlib2/checksums.ini @@ -0,0 +1,4 @@ +tarball=pathlib2-VERSION.tar.gz +sha1=a4329faa7d2f0ba2430eeb57372d3a72ccce6ca2 +md5=38e4f58b4d69dfcb9edb49a54a8b28d2 +cksum=512183712 diff --git a/build/pkgs/pathlib2/dependencies b/build/pkgs/pathlib2/dependencies new file mode 100644 index 00000000000..ae35f4f0477 --- /dev/null +++ b/build/pkgs/pathlib2/dependencies @@ -0,0 +1,5 @@ +setuptools + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/pathlib2/package-version.txt b/build/pkgs/pathlib2/package-version.txt new file mode 100644 index 00000000000..7ec1d6db408 --- /dev/null +++ b/build/pkgs/pathlib2/package-version.txt @@ -0,0 +1 @@ +2.1.0 diff --git a/build/pkgs/pathlib2/spkg-install b/build/pkgs/pathlib2/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/pathlib2/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/pathlib2/type b/build/pkgs/pathlib2/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/pathlib2/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/pickleshare/dependencies b/build/pkgs/pickleshare/dependencies index 945c50635dd..7f16f620fe4 100644 --- a/build/pkgs/pickleshare/dependencies +++ b/build/pkgs/pickleshare/dependencies @@ -1,4 +1,4 @@ -setuptools pathpy +setuptools pathpy pathlib2 ---------- All lines of this file are ignored except the first. From 667caa5f34709f0b9f6fec3b85f5034a4e518133 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sat, 16 Apr 2016 12:20:17 +0200 Subject: [PATCH 149/163] Remove notebook patch that is now upstream --- .../notebook/patches/help_link_url_fix.patch | 24 ------------------- 1 file changed, 24 deletions(-) delete mode 100644 build/pkgs/notebook/patches/help_link_url_fix.patch diff --git a/build/pkgs/notebook/patches/help_link_url_fix.patch b/build/pkgs/notebook/patches/help_link_url_fix.patch deleted file mode 100644 index cfa07dee3d3..00000000000 --- a/build/pkgs/notebook/patches/help_link_url_fix.patch +++ /dev/null @@ -1,24 +0,0 @@ -Use require.toUrl for help_links - -Dirty patch for the minified js, real PR is at -https://github.com/jupyter/notebook/pull/958 - - ---- a/notebook/static/notebook/js/main.min.js 2016-01-15 10:20:30.769442884 +0100 -+++ b/notebook/static/notebook/js/main.min.js 2016-01-15 10:20:53.073164049 +0100 -@@ -28358,7 +28358,7 @@ - .append($("") - .attr('target', '_blank') - .attr('title', 'Opens in a new window') -- .attr('href', link.url) -+ .attr('href', require.toUrl(link.url)) - .append($("") - .addClass("fa fa-external-link menu-icon pull-right") - ) -@@ -30547,4 +30547,4 @@ - define("notebook/js/main", function(){}); - - --//# sourceMappingURL=main.min.js.map -\ No newline at end of file -+//# sourceMappingURL=main.min.js.map From 9df2d3d8d5d064498ba271cbcf685ca456d3dbd4 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sat, 16 Apr 2016 12:20:47 +0200 Subject: [PATCH 150/163] Fix trivial conflict in numpy patch --- .../numpy/patches/numpy-1.10.1-asarray_conversion.patch | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/build/pkgs/numpy/patches/numpy-1.10.1-asarray_conversion.patch b/build/pkgs/numpy/patches/numpy-1.10.1-asarray_conversion.patch index 7a65280efe7..e5f04a4194e 100644 --- a/build/pkgs/numpy/patches/numpy-1.10.1-asarray_conversion.patch +++ b/build/pkgs/numpy/patches/numpy-1.10.1-asarray_conversion.patch @@ -1,3 +1,8 @@ +BUG: Let linspace accept input that has an array_interface but is not otherwise a numpy or python type. + +Upstream PR: https://github.com/numpy/numpy/pull/6659 + + diff -Naur numpy-1.10.1.orig/numpy/core/function_base.py numpy-1.10.1/numpy/core/function_base.py --- numpy-1.10.1.orig/numpy/core/function_base.py 2015-11-11 10:12:45.583322683 +1300 +++ numpy-1.10.1/numpy/core/function_base.py 2015-11-11 10:14:05.813343880 +1300 @@ -5,8 +10,8 @@ diff -Naur numpy-1.10.1.orig/numpy/core/function_base.py numpy-1.10.1/numpy/core __all__ = ['logspace', 'linspace'] from . import numeric as _nx --from .numeric import result_type, NaN -+from .numeric import result_type, NaN, asanyarray +-from .numeric import result_type, NaN, shares_memory, MAY_SHARE_BOUNDS, TooHardError ++from .numeric import result_type, NaN, shares_memory, MAY_SHARE_BOUNDS, TooHardError, asanyarray def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None): From 6332955c7e2dca0f2fcd201d34aedac373fa126e Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sat, 16 Apr 2016 13:29:47 +0200 Subject: [PATCH 151/163] Fix trivial doctest failures --- src/sage/env.py | 2 +- src/sage/misc/cython.py | 4 ++-- src/sage/modules/vector_double_dense.pyx | 4 ++-- src/sage/repl/image.py | 19 +++++++++---------- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/sage/env.py b/src/sage/env.py index 79443bb7465..f4497d1e76a 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -165,7 +165,7 @@ def sage_include_directories(use_sources=False): sage: sage.env.sage_include_directories() ['.../include', '.../include/python...', - '.../python.../site-packages/numpy/core/include', + '.../python.../numpy/core/include', '.../python.../site-packages', '.../python.../site-packages/sage/ext'] """ diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index 7a740184cbc..f61735f0e4b 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -173,7 +173,7 @@ def pyx_preparse(s): 'ntl'], ['.../include', '.../include/python...', - '.../python.../site-packages/numpy/core/include', + '.../python.../numpy/core/include', '...', '.../sage/ext', '.../cysignals'], @@ -200,7 +200,7 @@ def pyx_preparse(s): ['bar', '.../include', '.../include/python...', - '.../python.../site-packages/numpy/core/include', + '.../python.../numpy/core/include', '...', '.../sage/ext', '.../cysignals'] diff --git a/src/sage/modules/vector_double_dense.pyx b/src/sage/modules/vector_double_dense.pyx index 6bf16751023..0809585bf1c 100644 --- a/src/sage/modules/vector_double_dense.pyx +++ b/src/sage/modules/vector_double_dense.pyx @@ -754,9 +754,9 @@ cdef class Vector_double_dense(FreeModuleElement): sage: v = vector(RDF, range(9)) sage: w = vector(CDF, [k+(9-k)*I for k in range(9)]) - sage: v.stats_kurtosis() + sage: v.stats_kurtosis() # rel tol 5e-16 -1.2300000000000002 - sage: w.stats_kurtosis() + sage: w.stats_kurtosis() # rel tol 5e-16 -1.2300000000000002 """ import scipy.stats diff --git a/src/sage/repl/image.py b/src/sage/repl/image.py index 174703ca40d..e593772abde 100644 --- a/src/sage/repl/image.py +++ b/src/sage/repl/image.py @@ -39,7 +39,7 @@ class Image(SageObject): - def __init__(self, mode, size, color=0): + def __init__(self, mode, size, color=(0,)): """ Creates a new image with the given mode and size. @@ -76,13 +76,12 @@ def __init__(self, mode, size, color=0): - ``size`` -- 2-tuple, containing (width, height) in pixels. - - ``color`` -- string or numeric. What colour to use for the - image. Default is black. If given, this should be a single - integer or floating point value for single-band modes, and a - tuple for multi-band modes (one value per band). When - creating RGB images, you can also use colour strings as - supported by the ImageColor module. If the colour is None, - the image is not initialised. + - ``color`` -- string or tuple of numeric. What colour to use + for the image. Default is black. If given, this should be a + a tuple with one value per band. When creating RGB images, + you can also use colour strings as supported by the + ImageColor module. If the colour is None, the image is not + initialised. OUTPUT: @@ -91,7 +90,7 @@ def __init__(self, mode, size, color=0): EXAMPLES:: sage: from sage.repl.image import Image - sage: Image('P', (16, 16), 13) + sage: Image('P', (16, 16), (13,)) 16x16px 8-bit Color image """ self._pil = PIL.Image.new(mode, size, color) @@ -233,7 +232,7 @@ def save(self, filename): EXAMPLES:: sage: from sage.repl.image import Image - sage: img = Image('P', (12, 34), 13) + sage: img = Image('P', (12, 34), (13,)) sage: filename = tmp_filename(ext='.png') sage: img.save(filename) sage: open(filename).read().startswith('\x89PNG') From 6a4acb53d3eb196e2d508b32508d57888884dc28 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sat, 16 Apr 2016 10:37:19 -0300 Subject: [PATCH 152/163] Trac 20166: change order + optimisation --- src/sage/combinat/integer_vector_weighted.py | 47 +++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/sage/combinat/integer_vector_weighted.py b/src/sage/combinat/integer_vector_weighted.py index 58fca1f4046..1e180f90139 100644 --- a/src/sage/combinat/integer_vector_weighted.py +++ b/src/sage/combinat/integer_vector_weighted.py @@ -274,9 +274,11 @@ def __iter__(self): return perm = Word(self._weights).standard_permutation() - l = [x for x in sorted(self._weights)] + perm = [len(self._weights)-i for i in perm] + l = [x for x in sorted(self._weights, reverse=True)] for x in iterator_fast(self._n, l): - yield perm.action(x) + yield [x[i] for i in perm] + #.action(x) #_left_to_right_multiply_on_right(Permutation(x)) def iterator_fast(n, l): @@ -286,44 +288,47 @@ def iterator_fast(n, l): INPUT: - ``n`` -- an integer - - ``l`` -- the weights in weakly increasing order + - ``l`` -- the weights in weakly decreasing order EXAMPLES:: sage: from sage.combinat.integer_vector_weighted import iterator_fast - sage: list(iterator_fast(3, [1,1,2])) - [[0, 1, 1], [1, 0, 1], [0, 3, 0], [1, 2, 0], [2, 1, 0], [3, 0, 0]] + sage: list(iterator_fast(3, [2,1,1])) + [[1, 1, 0], [1, 0, 1], [0, 3, 0], [0, 2, 1], [0, 1, 2], [0, 0, 3]] sage: list(iterator_fast(2, [2])) [[1]] """ if n < 0: return + zero = ZZ.zero() + one = ZZ.one() + if not l: if n == 0: yield [] return if len(l) == 1: - if n % l[-1] == 0: - yield [n / l[-1]] + if n % l[0] == 0: + yield [n / l[0]] return - k = -1 - cur = [n // l[k] + 1] - rem = n - cur[0] * l[k] # Amount remaining + k = 0 + cur = [n // l[k] + one] + rem = n - cur[-1] * l[k] # Amount remaining while cur: - cur[0] -= 1 + cur[-1] -= one rem += l[k] - if rem == 0: - yield [ZZ.zero()] * (len(l) - len(cur)) + cur - elif cur[0] < 0 or rem < 0: - rem += cur.pop(0) * l[k] - k += 1 + if rem == zero: + yield cur + [zero] * (len(l) - len(cur)) + elif cur[-1] < zero or rem < zero: + rem += cur.pop() * l[k] + k -= 1 elif len(l) == len(cur) + 1: - if rem % l[0] == 0: - yield [rem // l[0]] + cur + if rem % l[-1] == zero: + yield cur + [rem // l[-1]] else: - k -= 1 - cur.insert(0, rem // l[k] + 1) - rem -= cur[0] * l[k] + k += 1 + cur.append(rem // l[k] + one) + rem -= cur[-1] * l[k] From b5fb38b63c8c654259f1df110c31ed153be2b8f3 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Sat, 16 Apr 2016 17:17:07 +0200 Subject: [PATCH 153/163] Use "setup.py install" to install Python packages --- build/pkgs/babel/spkg-install | 2 +- build/pkgs/flask/spkg-install | 2 +- build/pkgs/flask_autoindex/spkg-install | 2 +- build/pkgs/flask_babel/spkg-install | 2 +- build/pkgs/flask_oldsessions/spkg-install | 2 +- build/pkgs/flask_openid/spkg-install | 2 +- build/pkgs/flask_silk/spkg-install | 2 +- build/pkgs/itsdangerous/spkg-install | 2 +- build/pkgs/python_openid/spkg-install | 2 +- build/pkgs/pytz/spkg-install | 2 +- build/pkgs/sagenb/spkg-install | 4 +++- build/pkgs/speaklater/spkg-install | 2 +- build/pkgs/twisted/spkg-install | 2 +- build/pkgs/werkzeug/spkg-install | 2 +- build/pkgs/zope_interface/spkg-install | 2 +- 15 files changed, 17 insertions(+), 15 deletions(-) diff --git a/build/pkgs/babel/spkg-install b/build/pkgs/babel/spkg-install index b28ab85c90c..afb3f302fd1 100755 --- a/build/pkgs/babel/spkg-install +++ b/build/pkgs/babel/spkg-install @@ -1,3 +1,3 @@ #!/usr/bin/env bash -cd src && pip install . +cd src && python setup.py install diff --git a/build/pkgs/flask/spkg-install b/build/pkgs/flask/spkg-install index b28ab85c90c..afb3f302fd1 100755 --- a/build/pkgs/flask/spkg-install +++ b/build/pkgs/flask/spkg-install @@ -1,3 +1,3 @@ #!/usr/bin/env bash -cd src && pip install . +cd src && python setup.py install diff --git a/build/pkgs/flask_autoindex/spkg-install b/build/pkgs/flask_autoindex/spkg-install index e24852a395d..2ae379ae348 100755 --- a/build/pkgs/flask_autoindex/spkg-install +++ b/build/pkgs/flask_autoindex/spkg-install @@ -1,3 +1,3 @@ #!/usr/bin/env bash -cd Flask* && pip install . +cd Flask* && python setup.py install diff --git a/build/pkgs/flask_babel/spkg-install b/build/pkgs/flask_babel/spkg-install index e24852a395d..2ae379ae348 100755 --- a/build/pkgs/flask_babel/spkg-install +++ b/build/pkgs/flask_babel/spkg-install @@ -1,3 +1,3 @@ #!/usr/bin/env bash -cd Flask* && pip install . +cd Flask* && python setup.py install diff --git a/build/pkgs/flask_oldsessions/spkg-install b/build/pkgs/flask_oldsessions/spkg-install index 980f25cb577..56301585aa0 100755 --- a/build/pkgs/flask_oldsessions/spkg-install +++ b/build/pkgs/flask_oldsessions/spkg-install @@ -1,3 +1,3 @@ #!/usr/bin/env bash -cd *flask-oldsessions* && pip install . +cd *flask-oldsessions* && python setup.py install diff --git a/build/pkgs/flask_openid/spkg-install b/build/pkgs/flask_openid/spkg-install index e24852a395d..2ae379ae348 100755 --- a/build/pkgs/flask_openid/spkg-install +++ b/build/pkgs/flask_openid/spkg-install @@ -1,3 +1,3 @@ #!/usr/bin/env bash -cd Flask* && pip install . +cd Flask* && python setup.py install diff --git a/build/pkgs/flask_silk/spkg-install b/build/pkgs/flask_silk/spkg-install index e24852a395d..2ae379ae348 100755 --- a/build/pkgs/flask_silk/spkg-install +++ b/build/pkgs/flask_silk/spkg-install @@ -1,3 +1,3 @@ #!/usr/bin/env bash -cd Flask* && pip install . +cd Flask* && python setup.py install diff --git a/build/pkgs/itsdangerous/spkg-install b/build/pkgs/itsdangerous/spkg-install index b28ab85c90c..afb3f302fd1 100755 --- a/build/pkgs/itsdangerous/spkg-install +++ b/build/pkgs/itsdangerous/spkg-install @@ -1,3 +1,3 @@ #!/usr/bin/env bash -cd src && pip install . +cd src && python setup.py install diff --git a/build/pkgs/python_openid/spkg-install b/build/pkgs/python_openid/spkg-install index fd983004d0f..9d8788038ec 100755 --- a/build/pkgs/python_openid/spkg-install +++ b/build/pkgs/python_openid/spkg-install @@ -1,3 +1,3 @@ #!/usr/bin/env bash -cd python-openid-* && pip install . +cd python-openid-* && python setup.py install diff --git a/build/pkgs/pytz/spkg-install b/build/pkgs/pytz/spkg-install index b28ab85c90c..afb3f302fd1 100755 --- a/build/pkgs/pytz/spkg-install +++ b/build/pkgs/pytz/spkg-install @@ -1,3 +1,3 @@ #!/usr/bin/env bash -cd src && pip install . +cd src && python setup.py install diff --git a/build/pkgs/sagenb/spkg-install b/build/pkgs/sagenb/spkg-install index 08fa12dddc3..8c0eced55f2 100755 --- a/build/pkgs/sagenb/spkg-install +++ b/build/pkgs/sagenb/spkg-install @@ -8,7 +8,9 @@ fi cd src -pip install . +# Install a flat package (not an egg), which is the same as how pip +# installs packages. +python setup.py install --single-version-externally-managed --record dummy if [ $? -ne 0 ]; then echo >&2 "Error installing SageNB" exit 1 diff --git a/build/pkgs/speaklater/spkg-install b/build/pkgs/speaklater/spkg-install index b28ab85c90c..afb3f302fd1 100755 --- a/build/pkgs/speaklater/spkg-install +++ b/build/pkgs/speaklater/spkg-install @@ -1,3 +1,3 @@ #!/usr/bin/env bash -cd src && pip install . +cd src && python setup.py install diff --git a/build/pkgs/twisted/spkg-install b/build/pkgs/twisted/spkg-install index b28ab85c90c..afb3f302fd1 100755 --- a/build/pkgs/twisted/spkg-install +++ b/build/pkgs/twisted/spkg-install @@ -1,3 +1,3 @@ #!/usr/bin/env bash -cd src && pip install . +cd src && python setup.py install diff --git a/build/pkgs/werkzeug/spkg-install b/build/pkgs/werkzeug/spkg-install index b28ab85c90c..afb3f302fd1 100755 --- a/build/pkgs/werkzeug/spkg-install +++ b/build/pkgs/werkzeug/spkg-install @@ -1,3 +1,3 @@ #!/usr/bin/env bash -cd src && pip install . +cd src && python setup.py install diff --git a/build/pkgs/zope_interface/spkg-install b/build/pkgs/zope_interface/spkg-install index b28ab85c90c..afb3f302fd1 100755 --- a/build/pkgs/zope_interface/spkg-install +++ b/build/pkgs/zope_interface/spkg-install @@ -1,3 +1,3 @@ #!/usr/bin/env bash -cd src && pip install . +cd src && python setup.py install From 38c6be9df03439d604f7b6c476cbebd6e606e7fd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 16 Apr 2016 17:33:55 -0700 Subject: [PATCH 154/163] MixedIntegerLinearProgram, GenericBackend: Add __deepcopy__ methods --- .../numerical/backends/generic_backend.pyx | 19 +++++++++++++++++++ src/sage/numerical/mip.pyx | 16 ++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 3515d102eb6..a846a12ce2e 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -866,6 +866,25 @@ cdef class GenericBackend: """ raise NotImplementedError() + def __deepcopy__(self, memo={}): + """ + Return a deep copy of ``self``. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = MixedIntegerLinearProgram(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver + sage: b = p.new_variable() # optional - Nonexistent_LP_solver + sage: p.add_constraint(b[1] + b[2] <= 6) # optional - Nonexistent_LP_solver + sage: p.set_objective(b[1] + b[2]) # optional - Nonexistent_LP_solver + sage: cp = deepcopy(p.get_backend()) # optional - Nonexistent_LP_solver + sage: cp.solve() # optional - Nonexistent_LP_solver + 0 + sage: cp.get_objective_value() # optional - Nonexistent_LP_solver + 6.0 + """ + return self.__copy__() + cpdef row(self, int i): """ Return a row diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 65716aa6bca..6d6f7278092 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -557,6 +557,22 @@ cdef class MixedIntegerLinearProgram(SageObject): p._backend = ( self._backend).copy() return p + def __deepcopy__(self, memo={}): + """ + Return a deep copy of ``self``. + + EXAMPLE:: + + sage: p = MixedIntegerLinearProgram() + sage: b = p.new_variable() + sage: p.add_constraint(b[1] + b[2] <= 6) + sage: p.set_objective(b[1] + b[2]) + sage: cp = deepcopy(p) + sage: cp.solve() + 6.0 + """ + return self.__copy__() + def __getitem__(self, v): r""" Returns the symbolic variable corresponding to the key From 0863ed3bdf02aab77e993a28b1b8dd65f4258418 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sun, 17 Apr 2016 13:59:37 +0200 Subject: [PATCH 155/163] Fix final doctest failure The color default should work for single AND multiband images, so a tuple is a bad choice. --- src/sage/repl/image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/repl/image.py b/src/sage/repl/image.py index e593772abde..ea0d96ccfa6 100644 --- a/src/sage/repl/image.py +++ b/src/sage/repl/image.py @@ -39,7 +39,7 @@ class Image(SageObject): - def __init__(self, mode, size, color=(0,)): + def __init__(self, mode, size, color='white'): """ Creates a new image with the given mode and size. From 3117b01e595febc8fc6de90eeb3cf19a7e97c7bd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 18 Apr 2016 11:49:06 -0700 Subject: [PATCH 156/163] MixedIntegerLinearProgram.__deepcopy__: Add another doctest --- src/sage/numerical/mip.pyx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 6d6f7278092..0a425f4d04c 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -570,6 +570,19 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: cp = deepcopy(p) sage: cp.solve() 6.0 + + TEST: + + Test that `deepcopy` makes actual copies but preserves identities:: + + sage: mip = MixedIntegerLinearProgram() + sage: ll = [mip, mip] + sage: dcll=deepcopy(ll) + sage: ll[0] is dcll[0] + False + sage: dcll[0] is dcll[1] + True + """ return self.__copy__() From 2d54769f0247d59abdd8e54a1019239eacac4feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 18 Apr 2016 21:49:18 +0200 Subject: [PATCH 157/163] not using == False in /combinat --- .../cluster_algebra_quiver/cluster_seed.py | 3 ++- .../cluster_algebra_quiver/mutation_type.py | 4 ++-- src/sage/combinat/crystals/alcove_path.py | 6 ++--- src/sage/combinat/crystals/tensor_product.py | 4 ++-- src/sage/combinat/designs/covering_design.py | 3 +-- src/sage/combinat/finite_state_machine.py | 17 +++++++------ src/sage/combinat/gelfand_tsetlin_patterns.py | 24 +++++++++++++------ src/sage/combinat/permutation.py | 11 ++++----- src/sage/combinat/tableau.py | 4 ++-- src/sage/combinat/words/suffix_trees.py | 4 ++-- 10 files changed, 44 insertions(+), 36 deletions(-) diff --git a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py index e32641bbfe6..e6872e8d5b2 100644 --- a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py +++ b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py @@ -4188,10 +4188,11 @@ def is_LeeLiZel_allowable(T,n,m,b,c): nEA1 += 1 if nAF1 == b*nAF2 or nEA2 == c*nEA1: uv_okay = True - if uv_okay == False: + if not uv_okay: return False return True + def get_green_vertices(C): r""" Get the green vertices from a matrix. Will go through each clumn and return diff --git a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py index a45a2ab3dda..7cf737d1150 100644 --- a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py +++ b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py @@ -1139,8 +1139,8 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False): else: long_cycle = [ cycle, ['A',n-1,1] ] # if we haven't found a "long_cycle", we are in finite type A - if long_cycle == False: - long_cycle = [ [], QuiverMutationType(['A',n]) ] + if not long_cycle: + long_cycle = [[], QuiverMutationType(['A', n])] # The 'connected vertices' are now computed. # Attention: 0-1-2 in type A_3 has connecting vertices 0 and 2, while in type D_3 it has connecting vertex 1; diff --git a/src/sage/combinat/crystals/alcove_path.py b/src/sage/combinat/crystals/alcove_path.py index 3afde4d9daa..f31620799d9 100644 --- a/src/sage/combinat/crystals/alcove_path.py +++ b/src/sage/combinat/crystals/alcove_path.py @@ -1723,14 +1723,14 @@ def compare_graphs(g1, g2, node1, node2): matched = False for o2 in g2.outgoing_edges( node2 ): if o2[2] == out_edge[2]: - if matched == True: + if matched: print "ERROR: Two edges with the same label for ", out_edge, " exist." return False matched = True result = compare_graphs(g1, g2, out_edge[1], o2[1]) - if result == False: + if not result: return False - if matched == False: + if not matched: print "ERROR: No matching edge for ", out_edge, "." return False return True diff --git a/src/sage/combinat/crystals/tensor_product.py b/src/sage/combinat/crystals/tensor_product.py index b9cd295cffb..c16330c8d97 100644 --- a/src/sage/combinat/crystals/tensor_product.py +++ b/src/sage/combinat/crystals/tensor_product.py @@ -1360,9 +1360,9 @@ def positions_of_unmatched_minus(self, i, dual=False, reverse=False): """ unmatched_plus = [] height = 0 - if reverse == True: + if reverse: self = self.reversed() - if dual == False: + if not dual: for j in range(len(self)): minus = self[j].phi(i) plus = self[j].epsilon(i) diff --git a/src/sage/combinat/designs/covering_design.py b/src/sage/combinat/designs/covering_design.py index 8249b3ebe2f..624ad343f87 100644 --- a/src/sage/combinat/designs/covering_design.py +++ b/src/sage/combinat/designs/covering_design.py @@ -309,12 +309,11 @@ def is_covering(self): tset[tuple(y)] = True for i in Svt: - if tset[tuple(i)] == False: # uncovered + if not tset[tuple(i)]: # uncovered return False return True # everything was covered - def v(self): """ Return `v`, the number of points in the covering design. diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index e6528330cef..8b110bc89f9 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -1612,8 +1612,8 @@ def is_final(self, is_final): sage: A.state((0, 0)).final_word_out = [] sage: A.state((0, 0)).is_final = False - sage: A.state((0, 0)).is_final == False - True + sage: A.state((0, 0)).is_final + False sage: A = FSMState('A', is_final=True, final_word_out=[]) sage: A.is_final = False @@ -3897,7 +3897,7 @@ def __call__(self, *args, **kwargs): kwargs['automatic_output_type'] = not 'format_output' in kwargs input_tape = args[0] if hasattr(input_tape, 'is_finite') and \ - input_tape.is_finite() == False: + not input_tape.is_finite(): if not 'iterator_type' in kwargs: kwargs['iterator_type'] = 'simple' return self.iter_process(*args, **kwargs) @@ -6283,7 +6283,7 @@ class is created and is used during the processing. for out in it_output] # process output: cannot return output to due input parameters - if options['list_of_outputs'] == False: + if not options['list_of_outputs']: if not it_output and only_accepted: raise ValueError('No accepting output was found but according ' 'to the given options, an accepting output ' @@ -11984,8 +11984,8 @@ class is created and is used during the processing. options = copy(self._process_default_options_) options.update(kwargs) - condensed_output = (options['list_of_outputs'] == False and - options['full_output'] == False) + condensed_output = (not options['list_of_outputs'] and + not options['full_output']) if condensed_output: options['list_of_outputs'] = True @@ -13171,8 +13171,8 @@ class is created and is used during the processing. options = copy(self._process_default_options_) options.update(kwargs) - condensed_output = (options['list_of_outputs'] == False and - options['full_output'] == False) + condensed_output = (not options['list_of_outputs'] and + not options['full_output']) if condensed_output: options['list_of_outputs'] = True @@ -13190,7 +13190,6 @@ class is created and is used during the processing. return result[0] return result - def _process_convert_output_(self, output_data, **kwargs): """ Helper function which converts the output of diff --git a/src/sage/combinat/gelfand_tsetlin_patterns.py b/src/sage/combinat/gelfand_tsetlin_patterns.py index c08f65958e5..4a921e18362 100644 --- a/src/sage/combinat/gelfand_tsetlin_patterns.py +++ b/src/sage/combinat/gelfand_tsetlin_patterns.py @@ -51,6 +51,7 @@ from sage.combinat.combinatorial_map import combinatorial_map from sage.misc.all import prod + class GelfandTsetlinPattern(ClonableArray): r""" A Gelfand-Tsetlin (sometimes written as Gelfand-Zetlin or Gelfand-Cetlin) @@ -502,8 +503,8 @@ def Tokuyama_coefficient(self, name='t'): """ R = PolynomialRing(ZZ, name) t = R.gen(0) - if self.is_strict() == False: - return R(0) + if not self.is_strict(): + return R.zero() return (t+1)**(self.number_of_special_entries()) * t**(self.number_of_boxes()) @@ -1017,12 +1018,21 @@ def _cftp(self, start_row): ALGORITHM: - The set of Gelfand-Tsetlin patterns can partially ordered by elementwise - domination. The partial order has unique maximum and minimum elements - that are computed by the methods ``_cftp_upper`` and ``_cftp_lower``. - We then run the Markov chain that randomly toggles each element up or - down from the past until the state reached from the upper and lower start + The set of Gelfand-Tsetlin patterns can partially ordered by + elementwise domination. The partial order has unique maximum + and minimum elements that are computed by the methods + :meth:`_cftp_upper` and :meth:`_cftp_lower`. We then run the Markov + chain that randomly toggles each element up or down from the + past until the state reached from the upper and lower start points coalesce as described in [Propp1997]_. + + EXAMPLES:: + + sage: G = GelfandTsetlinPatterns(3, 5) + sage: G._cftp(0) # random + [[5, 3, 2], [4, 2], [3]] + sage: G._cftp(0) in G + True """ from sage.misc.randstate import current_randstate from sage.misc.randstate import seed diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index b225d880b5f..938155b3fa6 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -966,7 +966,6 @@ def to_cycles(self, singletons=True): sage: all(from_cycles(size, p.to_cycles()) == p for p in sample) True - Note: there is an alternative implementation called ``_to_cycle_set`` which could be slightly (10%) faster for some input (typically for permutations of size in the range [100, 10000]). You can run the @@ -995,17 +994,17 @@ def to_cycles(self, singletons=True): l = self[:] - #Go through until we've considered every number between 1 and len(l) + # Go through until we've considered every number between 1 and len(l) for i in range(len(l)): - if l[i] == False: + if not l[i]: continue - cycleFirst = i+1 - cycle = [ cycleFirst ] + cycleFirst = i + 1 + cycle = [cycleFirst] l[i], next = False, l[i] while next != cycleFirst: cycle.append( next ) l[next - 1], next = False, l[next - 1] - #Add the cycle to the list of cycles + # Add the cycle to the list of cycles if singletons or len(cycle) > 1: cycles.append(tuple(cycle)) return cycles diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index 3cc12c796e8..8f2854e4c00 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -2620,10 +2620,10 @@ def _slide_down(self, c, n): right_neighbor = new_st[spotl][spotc + 1] if go_right is None or upper_neighbor > right_neighbor: go_right = True - if go_right == True: + if go_right is True: new_st[spotl][spotc] = right_neighbor spotc += 1 - elif go_right == False: + elif go_right is False: new_st[spotl][spotc] = upper_neighbor spotl += 1 else: diff --git a/src/sage/combinat/words/suffix_trees.py b/src/sage/combinat/words/suffix_trees.py index e10bc6efa69..e6068e0be35 100644 --- a/src/sage/combinat/words/suffix_trees.py +++ b/src/sage/combinat/words/suffix_trees.py @@ -630,7 +630,7 @@ def _process_letter(self, letter): (s,(k,i)) = self._active_state old_r = 0 (end_state, r) = self._test_and_split(s,(k,i-1),letter) - while end_state == False: + while not end_state: # adjoin a new state rr and create a transition from r to rr rr = len(self._transition_function) self._transition_function[rr] = {} @@ -1096,7 +1096,7 @@ def to_explicit_suffix_tree(self): (s,(k,i)) = self._active_state old_r = 0 (end_state, r) = self._test_and_split(s,(k,i-1), end_of_string) - while end_state == False: + while not end_state: (s, k) = self._canonize(self._suffix_link[s], (k,i-1)) (end_state, r) = self._test_and_split(s, (k,i-1), end_of_string) # remove the end of string symbol from the word From 36ccb1877b19ca9d308be7c951fdaeaac49bea3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 18 Apr 2016 21:52:47 +0200 Subject: [PATCH 158/163] not using == True in /combinat --- src/sage/combinat/cluster_algebra_quiver/cluster_seed.py | 4 ++-- src/sage/combinat/crystals/alcove_path.py | 4 ++-- src/sage/combinat/crystals/tensor_product.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py index e6872e8d5b2..66bc8604289 100644 --- a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py +++ b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py @@ -391,7 +391,7 @@ def use_c_vectors(self, use=True, bot_is_c=False, force=False): self._BC = copy(self._M) if self._bot_is_c != bot_is_c: # If we need to do this. It overrides the previous designations. self._bot_is_c = bot_is_c - if self._bot_is_c == True: + if self._bot_is_c: self._use_c_vec = True if self._m == self._n: # in this case, the second half of a 2n x n matrix is a c-matrix. self._C = copy(self._M[self._n:(self._n+self._m),:self._n]) @@ -623,7 +623,7 @@ def use_fpolys(self, use=True, user_labels=None, user_labels_prefix=None): self._yhat = dict([ (self._U.gen(j),prod([self._R.gen(i)**self._M[i,j] for i in xrange(self._n+self._m)])) for j in xrange(self._n)]) elif self._cluster: raise ValueError("should not be possible to have cluster variables without f-polynomials") # added this as a sanity check. This error should never appear however. - elif self._track_mut == True: # If we can navigate from the root to where we are + elif self._track_mut: # If we can navigate from the root to where we are if not self._use_g_vec: self.use_g_vectors(True) catchup = ClusterSeed(self._b_initial, user_labels=user_labels, user_labels_prefix=user_labels_prefix) diff --git a/src/sage/combinat/crystals/alcove_path.py b/src/sage/combinat/crystals/alcove_path.py index f31620799d9..7d6913d5dcf 100644 --- a/src/sage/combinat/crystals/alcove_path.py +++ b/src/sage/combinat/crystals/alcove_path.py @@ -417,7 +417,7 @@ def vertices(self): s = W.simple_reflections() highest_weight_crystal = self._highest_weight_crystal - if highest_weight_crystal == True: + if highest_weight_crystal: successors = 'bruhat_upper_covers' else: successors = 'quantum_bruhat_successors' @@ -622,7 +622,7 @@ def is_admissible(self): s = W.simple_reflections() highest_weight_crystal = self.parent()._highest_weight_crystal - if highest_weight_crystal == True: + if highest_weight_crystal: successors = 'bruhat_upper_covers' else: successors = 'quantum_bruhat_successors' diff --git a/src/sage/combinat/crystals/tensor_product.py b/src/sage/combinat/crystals/tensor_product.py index c16330c8d97..3442ee71c13 100644 --- a/src/sage/combinat/crystals/tensor_product.py +++ b/src/sage/combinat/crystals/tensor_product.py @@ -954,9 +954,9 @@ def __lt__(self, other): if len(self) != len(other): return False for i in range(len(self)): - if (self[i] < other[i]) == True: + if (self[i] < other[i]): return True - if (other[i] < self[i]) == True: + if (other[i] < self[i]): return False return False From a657d8363b837a78f7944c1d04f98715d538ed4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 19 Apr 2016 08:36:22 +0200 Subject: [PATCH 159/163] trac 20459 fixing the finite state machine doctests --- src/sage/combinat/finite_state_machine.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 8b110bc89f9..40aafeb96a1 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -6283,7 +6283,7 @@ class is created and is used during the processing. for out in it_output] # process output: cannot return output to due input parameters - if not options['list_of_outputs']: + if options['list_of_outputs'] is False: if not it_output and only_accepted: raise ValueError('No accepting output was found but according ' 'to the given options, an accepting output ' @@ -11984,7 +11984,7 @@ class is created and is used during the processing. options = copy(self._process_default_options_) options.update(kwargs) - condensed_output = (not options['list_of_outputs'] and + condensed_output = (options['list_of_outputs'] is False and not options['full_output']) if condensed_output: @@ -13171,7 +13171,7 @@ class is created and is used during the processing. options = copy(self._process_default_options_) options.update(kwargs) - condensed_output = (not options['list_of_outputs'] and + condensed_output = (options['list_of_outputs'] is False and not options['full_output']) if condensed_output: From 1ab50b762be4cad278074d64736ad28ab05e7e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 19 Apr 2016 17:51:25 +0200 Subject: [PATCH 160/163] changing some division into exact division in ascii_art --- src/sage/combinat/abstract_tree.py | 2 +- src/sage/combinat/binary_tree.py | 2 +- src/sage/combinat/ordered_tree.py | 4 ++-- src/sage/graphs/graph.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/abstract_tree.py b/src/sage/combinat/abstract_tree.py index 5d41c17716b..33545bf09a7 100644 --- a/src/sage/combinat/abstract_tree.py +++ b/src/sage/combinat/abstract_tree.py @@ -979,7 +979,7 @@ def _ascii_art_(self): if len(l_repr) == 0: lf_sep += "_"*(t_repr._root+1) else: lf_sep += "_"*(t_repr._l+1) ls_sep += " "*(t_repr._root) + "/" + " "*(t_repr._l-t_repr._root) - mid = whitesep + int((len(lf_sep)-whitesep)/2) + mid = whitesep + (len(lf_sep) - whitesep) // 2 node = node_to_str( self ) t_repr = AsciiArt([lf_sep[:mid-1] + node + lf_sep[mid+len(node)-1:], ls_sep]) * acc t_repr._root = mid diff --git a/src/sage/combinat/binary_tree.py b/src/sage/combinat/binary_tree.py index 8f966281cf9..f46ddb0a57a 100644 --- a/src/sage/combinat/binary_tree.py +++ b/src/sage/combinat/binary_tree.py @@ -421,7 +421,7 @@ def _ascii_art_( self ): lr_tree = self[0]._ascii_art_() rr_tree = self[1]._ascii_art_() nb_ = lr_tree._l - lr_tree._root + rr_tree._root - 1 - nb_L = int( nb_ / 2 ) + nb_L = nb_ // 2 nb_R = nb_L + ( 1 if nb_ % 2 == 1 else 0 ) f_line = " " ** Integer( lr_tree._root + 1 ) + "_" ** Integer( nb_L ) + node f_line += "_" ** Integer( nb_R ) diff --git a/src/sage/combinat/ordered_tree.py b/src/sage/combinat/ordered_tree.py index cefb02569e0..fd106c1aa5d 100644 --- a/src/sage/combinat/ordered_tree.py +++ b/src/sage/combinat/ordered_tree.py @@ -443,7 +443,7 @@ def to_undirected_graph(self): relabel = True roots = [self] g.add_vertex(name=self.label()) - while len(roots) != 0: + while len(roots): node = roots.pop() for child in node: g.add_vertex(name=child.label()) @@ -489,7 +489,7 @@ def to_poset(self, root_to_leaf=False): relations = [] elements = [self.label()] roots = [self] - while len(roots) != 0: + while len(roots): node = roots.pop() for child in node: elements.append(child.label()) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 7d5b38936d6..975a1121bfe 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -3906,7 +3906,7 @@ def matching(self, value_only=False, algorithm="Edmonds", use_edge_labels=True, return sum(weight(self.edge_label(u, v)) for u, v in d.iteritems()) * 0.5 else: - return Integer(len(d)/2) + return Integer(len(d) // 2) else: return [(u, v, self.edge_label(u, v)) for u, v in d.iteritems() if u < v] From 7f7a7b5da4d6e134120bd3f2755519dfcef77b88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 19 Apr 2016 21:47:24 +0200 Subject: [PATCH 161/163] trac 20468 reviewer's suggestion --- src/sage/combinat/ordered_tree.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/ordered_tree.py b/src/sage/combinat/ordered_tree.py index fd106c1aa5d..c3b5398162b 100644 --- a/src/sage/combinat/ordered_tree.py +++ b/src/sage/combinat/ordered_tree.py @@ -443,7 +443,7 @@ def to_undirected_graph(self): relabel = True roots = [self] g.add_vertex(name=self.label()) - while len(roots): + while roots: node = roots.pop() for child in node: g.add_vertex(name=child.label()) @@ -489,7 +489,7 @@ def to_poset(self, root_to_leaf=False): relations = [] elements = [self.label()] roots = [self] - while len(roots): + while roots: node = roots.pop() for child in node: elements.append(child.label()) From 13f3fa53f681aed5c40f762d5932050162c59870 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 19 Apr 2016 21:36:26 -0500 Subject: [PATCH 162/163] Implementing a coxeter_diagram for reducible Cartan/Coxeter types. --- .../combinat/root_system/type_reducible.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/sage/combinat/root_system/type_reducible.py b/src/sage/combinat/root_system/type_reducible.py index 58f2271a04c..74c5a262bcb 100644 --- a/src/sage/combinat/root_system/type_reducible.py +++ b/src/sage/combinat/root_system/type_reducible.py @@ -419,8 +419,35 @@ def is_affine(self): """ return False + @cached_method + def coxeter_diagram(self): + """ + Return the Coxeter diagram for ``self``. + + EXAMPLES:: + + sage: cd = CartanType("A2xB2xF4").coxeter_diagram() + sage: cd + Graph on 8 vertices + sage: cd.edges() + [(1, 2, 3), (3, 4, 4), (5, 6, 3), (6, 7, 4), (7, 8, 3)] + sage: CartanType("F4xA2").coxeter_diagram().edges() + [(1, 2, 3), (2, 3, 4), (3, 4, 3), (5, 6, 3)] + sage: cd = CartanType("A1xH3").coxeter_diagram(); cd + Graph on 4 vertices + sage: cd.edges() + [(2, 3, 3), (3, 4, 5)] + """ + from sage.graphs.graph import Graph + relabelling = self._index_relabelling + g = Graph(multiedges=False) + g.add_vertices(self.index_set()) + for i,t in enumerate(self._types): + for [e1, e2, l] in t.coxeter_diagram().edges(): + g.add_edge(relabelling[i,e1], relabelling[i,e2], label=l) + return g class AmbientSpace(ambient_space.AmbientSpace): """ From 950f0f53b4dd013cf69b544ad31f1c8a8c6e538b Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Thu, 21 Apr 2016 09:15:30 +0200 Subject: [PATCH 163/163] Updated SageMath version to 7.2.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 0091aa67e50..25a6e6cf911 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 7.2.beta4, Release Date: 2016-04-12 +SageMath version 7.2.beta5, Release Date: 2016-04-21 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index b9cda22f894..a4c5bec5b26 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=a688b4151ebffbd892199e47edf21f0e295ae06f -md5=1da510585db296e3930eaa33cbb40a87 -cksum=3334341322 +sha1=443f6e49b23bbc1e9004f32c802b83e35abe3a77 +md5=bb7132154839ae45a55ca1c46331289d +cksum=2428928852 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 4c5c8078521..3f7d1915f71 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -158 +159 diff --git a/src/bin/sage-banner b/src/bin/sage-banner index 071faf7a95d..1778fbea0a0 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ SageMath version 7.2.beta4, Release Date: 2016-04-12 │ +│ SageMath version 7.2.beta5, Release Date: 2016-04-21 │ │ 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 ec156c83333..dd74a5aff47 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='7.2.beta4' -SAGE_RELEASE_DATE='2016-04-12' +SAGE_VERSION='7.2.beta5' +SAGE_RELEASE_DATE='2016-04-21' diff --git a/src/sage/version.py b/src/sage/version.py index b8460dee608..af243c34a9a 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 = '7.2.beta4' -date = '2016-04-12' +version = '7.2.beta5' +date = '2016-04-21'