# Ticket #9944: trac9944_polynomial_speedup.patch

File trac9944_polynomial_speedup.patch, 16.5 KB (added by SimonKing, 11 years ago)

Speedup of polynomial arithmetic by improved base ring conversion

• ## sage/categories/algebras.py

```# HG changeset patch
# User Simon King <simon.king@uni-jena.de>
# Date 1302703896 -7200
# Node ID e48ba7940e6a9f0abca53621c48c8b7eccf30687
# Parent  3f897822f54ddce8138c5a7cc9b8b392f4f991b2
#9944: Speedup conversion from base ring. Call Parent.__init__ in ring initialization.

diff --git a/sage/categories/algebras.py b/sage/categories/algebras.py```
 a def __init_extra__(self): """ Declares the canonical coercion from ``self.base_ring()`` to ``self``. Declares the canonical coercion from ``self.base_ring()`` to ``self``, if there has been none before. EXAMPLES:: sage: A(1)          # indirect doctest B[word: ] """ # TODO: find and implement a good syntax! self.register_coercion(SetMorphism(function = self.from_base_ring, parent = Hom(self.base_ring(), self, Rings()))) # Should be a morphism of Algebras(self.base_ring()), but e.g. QQ is not in Algebras(QQ) mor = SetMorphism(function = self.from_base_ring, parent = Hom(self.base_ring(), self, Rings())) try: self.register_coercion(mor) except AssertionError: pass class ElementMethods: # TODO: move the content of AlgebraElement here
• ## sage/rings/polynomial/multi_polynomial_libsingular.pyx

`diff --git a/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/sage/rings/polynomial/multi_polynomial_libsingular.pyx`
 a - Martin Albrecht (2009-06): refactored the code to allow better re-use - Simon King (2011-03): Use a faster way of conversion from the base ring. TODO: - implement Real, Complex coefficient rings via libSINGULAR sage: P. = PolynomialRing(Integers(2^32),order='lex') sage: P(2^32-1) 4294967295 TEST: Make sure that a faster conversion map from the base ring is used; see trac ticket #9944:: sage: R. = PolynomialRing(ZZ) sage: R.convert_map_from(R.base_ring()) Polynomial base injection morphism: From: Integer Ring To:   Multivariate Polynomial Ring in x, y over Integer Ring """ MPolynomialRing_generic.__init__(self, base_ring, n, names, order) self._has_singular = True self._ring = singular_ring_new(base_ring, n, self._names, order) self._one_element = new_MP(self,p_ISet(1, self._ring)) self._zero_element = new_MP(self,NULL) # This polynomial ring should belong to Algebras(base_ring). # Algebras(...).parent_class, which was called from MPolynomialRing_generic.__init__, # tries to provide a conversion from the base ring, if it does not exist. # This is for algebras that only do the generic stuff in their initialisation. # But here, we want to use PolynomialBaseringInjection. Hence, we need to # wipe the memory and construct the conversion from scratch. from sage.rings.polynomial.polynomial_element import PolynomialBaseringInjection base_inject = PolynomialBaseringInjection(base_ring, self) self.register_conversion(base_inject) def __dealloc__(self): r"""
• ## sage/rings/polynomial/multi_polynomial_ring.py

`diff --git a/sage/rings/polynomial/multi_polynomial_ring.py b/sage/rings/polynomial/multi_polynomial_ring.py`
 a self._gens = tuple(self._gens) self._zero_tuple = tuple(v) self._has_singular = can_convert_to_singular(self) # This polynomial ring should belong to Algebras(base_ring). # Algebras(...).parent_class, which was called from MPolynomialRing_generic.__init__, # tries to provide a conversion from the base ring, if it does not exist. # This is for algebras that only do the generic stuff in their initialisation. # But here, we want to use PolynomialBaseringInjection. Hence, we need to # wipe the memory and construct the conversion from scratch. if n: from sage.rings.polynomial.polynomial_element import PolynomialBaseringInjection base_inject = PolynomialBaseringInjection(base_ring, self) self.register_conversion(base_inject) def _monomial_order_function(self): return self.__monomial_order_function
• ## sage/rings/polynomial/polynomial_element.pyx

`diff --git a/sage/rings/polynomial/polynomial_element.pyx b/sage/rings/polynomial/polynomial_element.pyx`
 a AUTHORS: -  William Stein: first version -  William Stein: first version. -  Martin Albrecht: Added singular coercion. -  Robert Bradshaw: Move Polynomial_generic_dense to Cython -  Robert Bradshaw: Move Polynomial_generic_dense to Cython. -  Simon King: Use a faster way of conversion from the base ring. TESTS:: This class is used for conversion from a ring to a polynomial over that ring. It calls the _new_constant_poly method on the generator, which should be optimized for a particular polynomial type. If the polynomial ring has a One and if the elements provide an ``_rmul_`` method, and ``_rmul_`` does *not* return None (which is the case for `p`-adics) then conversion is obtained by multiplying the base ring element with the One by means of ``_rmul_``. Otherwise It calls the _new_constant_poly method on the generator, which should be optimized for a particular polynomial type, but often isn't. Technically, it should be a method of the polynomial ring, but few polynomial rings are cython classes. NOTE: We use ``_rmul_`` and not ``_lmul_`` since for many polynomial rings ``_lmul_`` simply calls ``_rmul_``. EXAMPLES: We demonstrate that different polynomial ring classes use polynomial base injection maps:: sage: R. = Qp(3)[] sage: R.convert_map_from(R.base_ring()) Polynomial base injection morphism: From: 3-adic Field with capped relative precision 20 To:   Univariate Polynomial Ring in x over 3-adic Field with capped relative precision 20 sage: R. = Qp(3)[] sage: R.convert_map_from(R.base_ring()) Polynomial base injection morphism: From: 3-adic Field with capped relative precision 20 To:   Multivariate Polynomial Ring in x, y over 3-adic Field with capped relative precision 20 sage: R. = QQ[] sage: R.convert_map_from(R.base_ring()) Polynomial base injection morphism: From: Rational Field To:   Multivariate Polynomial Ring in x, y over Rational Field sage: R. = QQ[] sage: R.convert_map_from(R.base_ring()) Polynomial base injection morphism: From: Rational Field To:   Univariate Polynomial Ring in x over Rational Field By trac ticket #9944, there are now only very few exceptions:: sage: PolynomialRing(QQ,names=[]).convert_map_from(QQ) Call morphism: From: Rational Field To:   Multivariate Polynomial Ring in no variables over Rational Field """ cdef Polynomial _an_element cdef RingElement _an_element cdef object _one_rmul_ def __init__(self, domain, codomain): """ TESTS: TESTS:: sage: from sage.rings.polynomial.polynomial_element import PolynomialBaseringInjection sage: PolynomialBaseringInjection(QQ, QQ['x']) Polynomial base injection morphism: Traceback (most recent call last): ... AssertionError: domain must be basering :: sage: R. = Qp(2)[] sage: f = R.convert_map_from(R.base_ring())    # indirect doctest sage: f(Qp(2).one()*3) (1 + 2 + O(2^20)) sage: (Qp(2).one()*3)*t (1 + 2 + O(2^20))*t """ assert domain is codomain.base_ring(), "domain must be basering" Morphism.__init__(self, domain, codomain) self._an_element = codomain.gen() self._repr_type_str = "Polynomial base injection" if domain is codomain: # some rings are base rings of themselves! return try: one = codomain._element_constructor_(domain.one_element()) except (AttributeError, NotImplementedError, TypeError): # perhaps it uses the old model? try: one = codomain._coerce_c(domain.one_element()) except (AttributeError, NotImplementedError, TypeError): return try: one_rmul_ = one._rmul_ except AttributeError: return # For the p-adic fields, _lmul_ and _rmul_ return None!!! # To work around, we need to test its sanity before we try # to use it. try: if one_rmul_(domain.one_element()) is None: return self._one_rmul_ = one_rmul_ except TypeError: pass cpdef Element _call_(self, x): """ Polynomial base injection morphism: From: Integer Ring To:   Univariate Polynomial Ring in x over Integer Ring sage: m(2) # implicit doctest sage: m(2) # indirect doctest 2 sage: parent(m(2)) Univariate Polynomial Ring in x over Integer Ring """ if self._one_rmul_ is not None: return self._one_rmul_(x) return self._an_element._new_constant_poly(x) cpdef Element _call_with_args(self, x, args=(), kwds={}): TESTS: sage: from sage.rings.polynomial.polynomial_element import PolynomialBaseringInjection sage: m = PolynomialBaseringInjection(Qp(5), Qp(5)['x']) sage: m(1 + O(5^11), absprec = 5) sage: m(1 + O(5^11), absprec = 5)   # indirect doctest (1 + O(5^11)) """ return self._codomain._element_constructor(x, *args, **kwds)
• ## sage/rings/polynomial/polynomial_element_generic.py

`diff --git a/sage/rings/polynomial/polynomial_element_generic.py b/sage/rings/polynomial/polynomial_element_generic.py`
 a output.__normalize() return output def _rmul_(self, left): r""" EXAMPLES:: sage: R. = PolynomialRing(ZZ, sparse=True) sage: (x^100000 - x^50000) * (x^100000 + x^50000) x^200000 - x^100000 sage: 7 * (x^100000 - x^50000)   # indirect doctest 7*x^100000 - 7*x^50000 AUTHOR: - Simon King (2011-03-31) """ output = {} for (index, coeff) in self.__coeffs.iteritems(): output[index] = left * coeff output = self.parent()(output, check=False) output.__normalize() return output def _lmul_(self, right): r""" EXAMPLES:: sage: R. = PolynomialRing(ZZ, sparse=True) sage: (x^100000 - x^50000) * (x^100000 + x^50000) x^200000 - x^100000 sage: (x^100000 - x^50000) * 7   # indirect doctest 7*x^100000 - 7*x^50000 AUTHOR: - Simon King (2011-03-31) """ output = {} for (index, coeff) in self.__coeffs.iteritems(): output[index] = coeff * right output = self.parent()(output, check=False) output.__normalize() return output def shift(self, n): r""" Returns this polynomial multiplied by the power `x^n`. If `n` is negative,
• ## sage/rings/polynomial/polynomial_ring.py

`diff --git a/sage/rings/polynomial/polynomial_ring.py b/sage/rings/polynomial/polynomial_ring.py`
 a category = categories.IntegralDomains() else: category = categories.CommutativeRings() sage.algebras.algebra.Algebra.__init__(self, base_ring, names=name, normalize=True, category=category) self.__is_sparse = sparse if element_class: self._polynomial_class = element_class else: from sage.rings.polynomial import polynomial_element self._polynomial_class = polynomial_element.Polynomial_generic_dense self.__generator = self._polynomial_class(self, [0,1], is_gen=True) self.__cyclopoly_cache = {} self._has_singular = False # Algebra.__init__ also calls __init_extra__ of Algebras(...).parent_class, which # tries to provide a conversion from the base ring, if it does not exist. # This is for algebras that only do the generic stuff in their initialisation. # But here, we want to use PolynomialBaseringInjection. Hence, we need to # wipe the memory and construct the conversion from scratch. sage.algebras.algebra.Algebra.__init__(self, base_ring, names=name, normalize=True, category=category) self.__generator = self._polynomial_class(self, [0,1], is_gen=True) base_inject = PolynomialBaseringInjection(base_ring, self) self._unset_coercions_used() self._populate_coercion_lists_( coerce_list = [PolynomialBaseringInjection(base_ring, self)], convert_list = [list], coerce_list = [base_inject], convert_list = [list, base_inject], convert_method_name = '_polynomial_')
• ## sage/rings/qqbar.py

`diff --git a/sage/rings/qqbar.py b/sage/rings/qqbar.py`
 a R. = QQbar[] v1 = AA(2) v2 = QQbar(sqrt(v1)) v3 = QQbar(3) v4 = sqrt(v3) v5 = v2*v4 v6 = (1 - v2)*(1 - v4) - 1 - v5 v7 = QQbar(sqrt(v1)) v8 = sqrt(v3) si1 = v7*v8 cp = AA.common_polynomial(x^2 + ((1 - v7)*(1 + v8) - 1 + si1)*x - si1) v9 = QQbar.polynomial_root(cp, RIF(-RR(1.7320508075688774), -RR(1.7320508075688772))) v10 = 1 - v9 v11 = v6 + (v10 - 1) v12 = -1 - v4 - QQbar.polynomial_root(cp, RIF(-RR(1.7320508075688774), -RR(1.7320508075688772))) v13 = 1 + v12 v14 = v10*(v6 + v5) - (v6 - v5*v9) si2 = v5*v9 AA.polynomial_root(AA.common_polynomial(x^4 + (v11 + (v13 - 1))*x^3 + (v14 + (v13*v11 - v11))*x^2 + (v13*(v14 - si2) - (v14 - si2*v12))*x - si2*v12), RIF(RR(0.99999999999999989), RR(1.0000000000000002))) v3 = sqrt(QQbar(3)) v4 = v2*v3 v5 = (1 - v2)*(1 - v3) - 1 - v4 v6 = QQbar(sqrt(v1)) si1 = v6*v3 cp = AA.common_polynomial(x^2 + ((1 - v6)*(1 + v3) - 1 + si1)*x - si1) v7 = QQbar.polynomial_root(cp, RIF(-RR(1.7320508075688774), -RR(1.7320508075688772))) v8 = 1 - v7 v9 = v5 + (v8 - 1) v10 = -1 - v3 - QQbar.polynomial_root(cp, RIF(-RR(1.7320508075688774), -RR(1.7320508075688772))) v11 = 1 + v10 v12 = v8*(v5 + v4) - (v5 - v4*v7) si2 = v4*v7 AA.polynomial_root(AA.common_polynomial(x^4 + (v9 + (v11 - 1))*x^3 + (v12 + (v11*v9 - v9))*x^2 + (v11*(v12 - si2) - (v12 - si2*v10))*x - si2*v10), RIF(RR(0.99999999999999989), RR(1.0000000000000002))) sage: one 1
• ## sage/rings/ring.pyx

`diff --git a/sage/rings/ring.pyx b/sage/rings/ring.pyx`
 a """ Generic ring class. """ # Unfortunately, ParentWithGens inherits from sage.structure.parent_old.Parent. # Its __init__ method does *not* call Parent.__init__, since this would somehow # yield an infinite recursion. But when we call it from here, it works. # This is done in order to ensure that __init_extra__ is called. def __init__(self, base, names=None, normalize=True, category = None): ParentWithGens.__init__(self, base, names=names, normalize=normalize) Parent.__init__(self, category=category) def __iter__(self): r""" Return an iterator through the elements of self. Not implemented in general.