def __init__(self, parent, matrix, inhomogeneous, is_zero=lambda p: False, relations=[]): ## Checking the input of matrix and vector if (not SAGE_element.is_Matrix(matrix)): matrix = Matrix(matrix) if (not SAGE_element.is_Vector(inhomogeneous)): inhomogeneous = vector(inhomogeneous) if (isinstance(parent, Wrap_w_Sequence_Ring)): parent = parent.base() ## Building the parent of the matrix and the vector pmatrix = matrix.parent().base() pinhom = inhomogeneous.parent().base() ## Setting the variables for the matrix, the parent and the vector self.__parent = pushout(pmatrix, pinhom) self.__matrix = matrix.change_ring(self.__parent) self.__inhomogeneous = inhomogeneous.change_ring(self.__parent) ## Setting the parent for the solutions if (not pushout(parent, self.__parent) == parent): try: self.__matrix = self.__matrix.change_ring(parent) self.__inhomogeneous = self.__inhomogeneous.change_ring(parent) self.__parent = parent except: raise TypeError( "The parent for the solutions must be an extension of the parent for the input" ) self.__solution_parent = parent ## Setting other variables if (relations is None): relations = [] self.__relations = [self.__parent(el) for el in relations] try: self.__gb = ideal(self.parent(), self.__relations).groebner_basis() except AttributeError: try: self.__gb = [ideal(self.parent(), self.__relations).gen()] except: self.__gb = [0] self.__is_zero = is_zero ## Creating the variables for the echelon form self.__echelon = None self.__transformation = None ## Creating the variables for the solutions self.__solution = None self.__syzygy = None
def __call__(self, K) : r""" Apply a ``self`` to a coefficient domain `A`. INPUT: - `A` -- A ring or module. The domain of coefficients for a ring or module of equivariant monoid power series. OUTPUT: An instance of :class:`~from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_ambient.EquivariantMonoidPowerSeriesAmbient_abstract`. A ring if the representation's extension by `A` is a ring, a module if this extension is a module. TESTS:: sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_functor import EquivariantMonoidPowerSeriesModuleFunctor sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_basicmonoids import NNMonoid, TrivialRepresentation, TrivialCharacterMonoid sage: F = EquivariantMonoidPowerSeriesModuleFunctor(NNMonoid(), TrivialCharacterMonoid("1", ZZ), TrivialRepresentation("1", FreeModule(ZZ, 1))) sage: emps = F(QQ) sage: emps.action() == NNMonoid() True sage: emps.characters() Character monoid over Trivial monoid sage: F = EquivariantMonoidPowerSeriesModuleFunctor(NNMonoid(), TrivialCharacterMonoid("1", ZZ), TrivialRepresentation("1", FreeModule(ZZ, 3))) sage: emps = F(QQ) sage: emps.coefficient_domain() Vector space of dimension 3 over Rational Field """ if not self.__R.base_ring().has_coerce_map_from(K) : R = self.__R.base_extend( pushout(self.__R.base_ring(), K) ) else : R = self.__R from monoidpowerseries_module import EquivariantMonoidPowerSeriesModule return EquivariantMonoidPowerSeriesModule( self.__O, self.__C, R )
def local_diffop(self): # ? r""" TESTS:: sage: from ore_algebra import DifferentialOperators sage: from ore_algebra.analytic.path import Point sage: Dops, x, Dx = DifferentialOperators() sage: Point(1, x*Dx - 1).local_diffop() (x + 1)*Dx - 1 sage: Point(RBF(1/2), x*Dx - 1).local_diffop() (x + 1/2)*Dx - 1 """ Pols_dop = self.dop.base_ring() # NOTE: pushout(QQ[x], K) doesn't handle embeddings well, and creates # an L equal but not identical to K. But then other constructors like # PolynomialRing(L, x) sometimes return objects over K found in cache, # leading to endless headaches with slow coercions. But the version here # may be closer to what I really want in any case. # XXX: This seems to work in the usual trivial case where we are looking # for a scalar domain containing QQ and QQ[i], but probably won't be # enough if we really have two different number fields with embeddings ex = self.exact() Scalars = pushout.pushout(Pols_dop.base_ring(), ex.value.parent()) Pols = Pols_dop.change_ring(Scalars) A, B = self.dop.base_ring().base_ring(), ex.value.parent() C = Pols.base_ring() assert C is A or C != A assert C is B or C != B dop_P = self.dop.change_ring(Pols) return dop_P.annihilator_of_composition(Pols([ex.value, 1]))
def __call__(self, P) : r""" Map a monoid power series to a symmetrisation extending the associated representation. INPUT: - `P` -- An instance of :class:`~from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_ambient.MonoidPowerSeriesAmbient_abstract`. TESTS:: sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_functor import MonoidPowerSeriesSymmetrisationModuleFunctor, MonoidPowerSeriesModuleFunctor sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_basicmonoids import NNMonoid, TrivialRepresentation, TrivialCharacterMonoid sage: mps = MonoidPowerSeriesModuleFunctor(ZZ, NNMonoid(False))(FreeModule(QQ, 1)) sage: F = MonoidPowerSeriesSymmetrisationModuleFunctor(NNMonoid(), TrivialCharacterMonoid("1", ZZ), TrivialRepresentation("1", FreeModule(ZZ, 1))) sage: emps = F(mps) sage: emps.representation() == TrivialRepresentation("1", FreeModule(QQ, 1)) True sage: mps = MonoidPowerSeriesModuleFunctor(ZZ, NNMonoid(False))(FreeModule(ZZ, 1)) sage: F = MonoidPowerSeriesSymmetrisationModuleFunctor(NNMonoid(), TrivialCharacterMonoid("1", ZZ), TrivialRepresentation("1", FreeModule(QQ, 1))) sage: emps = F(mps) sage: emps.representation() == TrivialRepresentation("1", FreeModule(QQ, 1)) True """ if self.__O.monoid() != P.monoid() : raise ValueError( "Action has to be defined on the monoid associated to P." ) PR = self.__R.from_module(P.coefficient_domain()) if not self.__R.base_ring().has_coerce_map_from(PR.base_ring()) : R = self.__R.base_extend( pushout(self.__R.base_ring(), PR.base_ring()) ) else : R = self.__R from monoidpowerseries_module import EquivariantMonoidPowerSeriesModule return EquivariantMonoidPowerSeriesModule( self.__O, self.__C, R )
def fourier_ring(self): r""" The ring that Fourier expansions of all elements of this ring will be contained in. OUTPUT: An ambient of (equivariant) monoid power series. TESTS:: sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_basicmonoids import * sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_ring import * sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_element import * sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import DegreeGrading sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_ring import * sage: mps = MonoidPowerSeriesRing(ZZ, NNMonoid(False)) sage: ger = GradedExpansionRing_class(None, Sequence([mps(1)]), PolynomialRing(QQ, 'a').ideal(0), DegreeGrading((1,))) sage: ger.fourier_ring() Ring of monoid power series over NN sage: ger = GradedExpansionRing_class(Sequence([MonoidPowerSeries(mps, {1 : 1}, mps.monoid().filter(2))]), Sequence([MonoidPowerSeries(mps, {1 : 1, 2 : 3}, mps.monoid().filter(4))]), PolynomialRing(ZZ, ['a', 'b']).ideal(0), DegreeGrading((1,2))) sage: ger.fourier_ring() Ring of monoid power series over NN """ if self.__gen_expansions.universe().base_ring() == self.base_ring(): return self.__gen_expansions.universe() else: from sage.categories.pushout import pushout if self.nbasegens() == 0: scalar_ring = self.base_ring() else: scalar_ring = self.base_ring().base_ring() return pushout(self.__gen_expansions.universe(), scalar_ring)
def __call__(self, P) : r""" Map a monoid power series to a symmetrisation extending the associated representation. INPUT: - `P` -- An instance of :class:`~from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_ambient.MonoidPowerSeriesAmbient_abstract`. TESTS:: sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_functor import MonoidPowerSeriesSymmetrisationModuleFunctor, MonoidPowerSeriesModuleFunctor sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_basicmonoids import NNMonoid, TrivialRepresentation, TrivialCharacterMonoid sage: mps = MonoidPowerSeriesModuleFunctor(ZZ, NNMonoid(False))(FreeModule(QQ, 1)) sage: F = MonoidPowerSeriesSymmetrisationModuleFunctor(NNMonoid(), TrivialCharacterMonoid("1", ZZ), TrivialRepresentation("1", FreeModule(ZZ, 1))) sage: emps = F(mps) sage: emps.representation() == TrivialRepresentation("1", FreeModule(QQ, 1)) True sage: mps = MonoidPowerSeriesModuleFunctor(ZZ, NNMonoid(False))(FreeModule(ZZ, 1)) sage: F = MonoidPowerSeriesSymmetrisationModuleFunctor(NNMonoid(), TrivialCharacterMonoid("1", ZZ), TrivialRepresentation("1", FreeModule(QQ, 1))) sage: emps = F(mps) sage: emps.representation() == TrivialRepresentation("1", FreeModule(QQ, 1)) True """ if self.__O.monoid() != P.monoid() : raise ValueError( "Action has to be defined on the monoid associated to P." ) PR = self.__R.from_module(P.coefficient_domain()) if not self.__R.base_ring().has_coerce_map_from(PR.base_ring()) : R = self.__R.base_extend( pushout(self.__R.base_ring(), PR.base_ring()) ) else : R = self.__R from .monoidpowerseries_module import EquivariantMonoidPowerSeriesModule return EquivariantMonoidPowerSeriesModule( self.__O, self.__C, R )
def __call__(self, K): r""" Apply a ``self`` to a coefficient domain `A`. INPUT: - `A` -- A ring or module. The domain of coefficients for a ring or module of equivariant monoid power series. OUTPUT: An instance of :class:`~from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_ambient.EquivariantMonoidPowerSeriesAmbient_abstract`. A ring if the representation's extension by `A` is a ring, a module if this extension is a module. TESTS:: sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_functor import EquivariantMonoidPowerSeriesModuleFunctor sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_basicmonoids import NNMonoid, TrivialRepresentation, TrivialCharacterMonoid sage: F = EquivariantMonoidPowerSeriesModuleFunctor(NNMonoid(), TrivialCharacterMonoid("1", ZZ), TrivialRepresentation("1", FreeModule(ZZ, 1))) sage: emps = F(QQ) sage: emps.action() == NNMonoid() True sage: emps.characters() Character monoid over Trivial monoid sage: F = EquivariantMonoidPowerSeriesModuleFunctor(NNMonoid(), TrivialCharacterMonoid("1", ZZ), TrivialRepresentation("1", FreeModule(ZZ, 3))) sage: emps = F(QQ) sage: emps.coefficient_domain() Vector space of dimension 3 over Rational Field """ if not self.__R.base_ring().has_coerce_map_from(K): R = self.__R.base_extend(pushout(self.__R.base_ring(), K)) else: R = self.__R from monoidpowerseries_module import EquivariantMonoidPowerSeriesModule return EquivariantMonoidPowerSeriesModule(self.__O, self.__C, R)
def scale(self,left): r""" Scales the moments of the distribution by `left` INPUT: - ``left`` -- scalar OUTPUT: - Scales the moments by `left` EXAMPLES:: sage: from sage.modular.pollack_stevens.distributions import Distributions sage: D = Distributions(5, 7, 15) sage: v = D([1,2,3,4,5]); v (1 + O(7^5), 2 + O(7^4), 3 + O(7^3), 4 + O(7^2), 5 + O(7)) sage: v.scale(2) (2 + O(7^5), 4 + O(7^4), 6 + O(7^3), 1 + 7 + O(7^2), 3 + O(7)) """ if isinstance(self, Dist_long) and isinstance(left, (Integer, pAdicCappedRelativeElement, pAdicCappedAbsoluteElement, pAdicFixedModElement)): return self._lmul_(left) R = left.parent() base = self.parent().base_ring() if base is R: return self._lmul_(left) elif base.has_coerce_map_from(R): return self._lmul_(base(left)) else: from sage.categories.pushout import pushout new_base = pushout(base, R) V = self.parent().change_ring(new_base) scalar = new_base(left) return V([scalar * new_base(self.moment(i)) for i in range(self.precision_absolute())])
def _element_constructor_(self, x) : """ TESTS:: sage: from psage.modform.fourier_expansion_framework.modularforms.modularform_ambient import * sage: from psage.modform.fourier_expansion_framework.modularforms.modularform_testtype import * sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_basicmonoids import * sage: ma = ModularFormsAmbient( QQ, ModularFormTestType_scalar(), NNFilter(5) ) sage: ma2 = ModularFormsAmbient( ZZ, ModularFormTestType_scalar(), NNFilter(5) ) sage: ma(ma2.0) Graded expansion g1 """ if isinstance(x, Element) : P = x.parent() if isinstance(P, ModularFormsRing_generic) : try : if self.type().is_vector_valued() and \ self.type().non_vector_values() == P.type() and \ self.base_ring().has_coerce_map_from(P.base_ring()) : if self.base_ring() != P.base_ring() : from sage.categories.pushout import pushout x = pushout(P, self.base_ring())(x) return self._element_class( self, self.relations().ring()( x.polynomial(). \ subs(self.type()._hom_to_vector_valued(self.base_ring())) ) ) except NotImplementedError : pass return ModularFormsAmbient_abstract._element_constructor_(self, x)
def mypushout(X, Y): if X.has_coerce_map_from(Y): return X elif Y.has_coerce_map_from(X): return Y else: Z = pushout(X, Y) assert (is_NumberField(Z) if is_NumberField(X) and is_NumberField(Y) else True) return Z
def __call__(self, R): r""" The graded expansion ring with the given generators over the base ring `R`. INPUT: - `R` -- A ring. OUTPUT: An instance of :class:~`fourier_expansion_framework.gradedexpansions.gradedexpansion_ambient.GradedExpansion_abstract`. NOTE: The ring of Fourier expansions given by the generators associated with this functor must admit a base extension to `R`. TESTS:: sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_basicmonoids import * sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_ring import * sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_module import * sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_element import * sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import DegreeGrading sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_functor import * sage: K.<rho> = CyclotomicField(6) sage: mps = MonoidPowerSeriesRing(QQ, NNMonoid(False)) sage: funct = GradedExpansionFunctor(Sequence([MonoidPowerSeries(mps, {1: 1}, mps.monoid().filter(4))]), Sequence([MonoidPowerSeries(mps, {1: 1, 2: 3}, mps.monoid().filter(4))]), PolynomialRing(QQ, ['a', 'b']).ideal(0), DegreeGrading((1,2))) sage: funct(K) Graded expansion ring with generators b sage: m = FreeModule(QQ, 3) sage: mpsm = MonoidPowerSeriesModule(m, NNMonoid(False)) sage: mps = mpsm.base_ring() sage: funct = GradedExpansionFunctor(None, Sequence([MonoidPowerSeries(mpsm, {1 : m([1,2,3]), 2 : m([3,-3,2])}, mpsm.monoid().filter(4)), MonoidPowerSeries(mpsm, {1 : m([2,-1,-1]), 2 : m([1,0,0])}, mpsm.monoid().filter(4))]), PolynomialRing(QQ, ['a', 'b']).ideal(0), DegreeGrading((1,2))) sage: funct(K) Graded expansion module with generators a, b """ from gradedexpansion_ring import GradedExpansionRing_class from gradedexpansion_module import GradedExpansionModule_class R = pushout(self.__relations.ring(), R) rel = R.ideal(self.__relations.gens()) if isinstance(self.__gen_expansions.universe(), Algebra): return GradedExpansionRing_class(self.__base_gen_expansions, self.__gen_expansions, rel, self.__grading, self.__all_relations, self.__reduce_before_evaluating) elif isinstance(self.__gen_expansions.universe(), Module): return GradedExpansionModule_class(self.__base_gen_expansions, self.__gen_expansions, rel, self.__grading, self.__all_relations, self.__reduce_before_evaluating) raise RuntimeError( "The generators' universe must be an algebra or a module.")
def matrix_of_dMovement(M, v, D, cols): r''' Method to construct a matrix where the columns are iterative derivations of a vector. When we have a vector space `V` over a differential field `(F,D)`, we can define "derivations" over `V` as those maps `\partial: V \rightarrow V` such that for all `v, w \in V` and all `f \in F`: * `\partial(v+w) = \partial(v) + \partial(w)`. * `\partial(fv) = D(f)v + f\partial(v)`. It can be shown (see Appendix A of the thesis by the author of the file) that, fixed a basis, this derivation is uniquely determined by a matrix `M` in such a way that, for all `v \in V`, the derivation can be computed with a matrix-vector multiplication: .. MATH:: \partial(v) = Mv + \kappa_D(v), where this `\kappa_D` is the terminwise derivation of the coordinates. This method computes several derivations of a particular vector and arrange the return as a matrix where these derivatives are in the columns of the matrix. INPUT: * ``M``: matrix defining the specific derivation for the fixed basis. * ``v``: starig vector which will be derivated several times. * ``D``: derivation over the ground field. * ``cols``: number of columns (i.e., derivations) that we will return. OUTPUT: A matrix `N=(n_{i,j})` where the columns are the derivations of `v`, i.e., `(n_{0,j},n_{1,j},\ldots) = \partial^j(v)`. EXAMPLES:: sage: from ajpastor.misc.matrix import * sage: M = vandermonde_matrix(QQ[x], [1,x,x^2]); v = vector(QQ[x], [1+x, 1, x^2]) sage: D = lambda p : p.derivative(x) sage: matrix_of_dMovement(M, v, D, 3) [ x + 1 x^2 + x + 3 x^6 + x^4 + 2*x^2 + 8*x + 6] [ 1 x^4 + 2*x + 1 x^8 + x^5 + x^4 + 7*x^3 + 4*x^2 + 2*x + 5] [ x^2 x^6 + x^2 + 3*x + 1 x^10 + 2*x^6 + 9*x^5 + x^4 + 2*x^3 + 2*x^2 + 3*x + 6] We can check that the second column of that matrix is the derivative of the vector `v`:: sage: [matrix_of_dMovement(M, v, D, 3)[i][1] for i in range(3)] == [el for el in vector_derivative(M, v, D)] True ''' from sage.categories.pushout import pushout parent = pushout(M.parent().base(), v.parent().base()) res = [v] for _ in range(1, cols): res += [vector_derivative(M, res[-1], D)] return Matrix(parent, res).transpose()
def EquivariantMonoidPowerSeriesRing(O, C, R): r""" Return the globally unique ring of equivariant monoid power over the monoid with action `O` with coefficients in the codomain `R` with a representation and a set of virtual characters `C`. INPUT: - `O` -- A monoid with an action of a group; As implemented in :class:~`fourier_expansion_framework.monoidpowerseries.NNMonoid`. - `C` -- A monoid of characters; As implemented in ::class:~`fourier_expansion_framework.monoidpowerseries.CharacterMonoid_class`. - `R` -- A representation on an algebra; As implemented in :class:~`fourier_expansion_framework.monoidpowerseries.TrivialRepresentation`. EXAMPLES:: sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_basicmonoids import * sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_ring import EquivariantMonoidPowerSeriesRing sage: emps = EquivariantMonoidPowerSeriesRing(NNMonoid(True), TrivialCharacterMonoid("1", QQ), TrivialRepresentation("1", QQ)) sage: emps is EquivariantMonoidPowerSeriesRing(NNMonoid(True), TrivialCharacterMonoid("1", QQ), TrivialRepresentation("1", QQ)) True """ ## TODO: Implement optional checking of the relations of the characters if O.group() != C.group(): raise ValueError( "The action on S and the characters must have the same group") if R.base_ring() != C.codomain(): if C.codomain().has_coerce_map_from(R.base_ring()): K = C.codomain() R = R.base_extend(K) elif R.base_ring().has_coerce_map_from(C.codomain()): K = R.base_ring() else: from sage.categories.pushout import pushout try: K = pushout(C.codomain(), R.base_ring()) R = R.base_extend(K) except: raise ValueError( "character codomain and representation base ring have no common extension" ) global _equivariantmonoidpowerseries_ring_cache key = (O, C, R) try: return _equivariantmonoidpowerseries_ring_cache[key] except KeyError: P = EquivariantMonoidPowerSeriesRing_generic(O, C, R) _equivariantmonoidpowerseries_ring_cache[key] = P return P
def EquivariantMonoidPowerSeriesRing(O, C, R) : """ Return the globally unique ring of equivariant monoid power over the monoid with action `O` with coefficients in the codomain `R` with a representation and a set of virtual characters `C`. INPUT: - `O` -- A monoid with an action of a group; As implemented in :class:~`fourier_expansion_framework.monoidpowerseries.NNMonoid`. - `C` -- A monoid of characters; As implemented in ::class:~`fourier_expansion_framework.monoidpowerseries.CharacterMonoid_class`. - `R` -- A representation on an algebra; As implemented in :class:~`fourier_expansion_framework.monoidpowerseries.TrivialRepresentation`. EXAMPLES:: sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_basicmonoids import * sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_ring import EquivariantMonoidPowerSeriesRing sage: emps = EquivariantMonoidPowerSeriesRing(NNMonoid(True), TrivialCharacterMonoid("1", QQ), TrivialRepresentation("1", QQ)) sage: emps is EquivariantMonoidPowerSeriesRing(NNMonoid(True), TrivialCharacterMonoid("1", QQ), TrivialRepresentation("1", QQ)) True """ ## TODO: Implement optional checking of the relations of the characters if O.group() != C.group() : raise ValueError, "The action on S and the characters must have the same group" if R.base_ring() != C.codomain() : if C.codomain().has_coerce_map_from(R.base_ring()) : K = C.codomain() R = R.base_extend(K) elif R.base_ring().has_coerce_map_from(C.codomain()) : K = R.base_ring() else : from sage.categories.pushout import pushout try : K = pushout(C.codomain(), R.base_ring()) R = R.base_extend(K) except : raise ValueError, "character codomain and representation base ring have no common extension" global _equivariantmonoidpowerseries_ring_cache key = (O, C, R) try : return _equivariantmonoidpowerseries_ring_cache[key] except KeyError : P = EquivariantMonoidPowerSeriesRing_generic(O, C, R) _equivariantmonoidpowerseries_ring_cache[key] = P return P
def _pushout_(self, R): """ Return the pushout of this Tate algebra with ``R``. This is only implemented when ``R`` is a p-adic ring or a p-adic field. EXAMPLES:: sage: from sage.categories.pushout import pushout sage: R = Zp(2) sage: R1.<a> = Zq(4) sage: R2.<pi> = R.extension(x^2 - 2) sage: A.<u,v> = TateAlgebra(R, log_radii=[1,2]) sage: A1 = pushout(A, R1); A1 Tate Algebra in u (val >= -1), v (val >= -2) over 2-adic Unramified Extension Field in a defined by x^2 + x + 1 sage: A2 = pushout(A, R2); A2 Tate Algebra in u (val >= -2), v (val >= -4) over 2-adic Eisenstein Extension Field in pi defined by x^2 - 2 sage: Ao = A.integer_ring() sage: pushout(Ao, R1) Integer ring of the Tate Algebra in u (val >= -1), v (val >= -2) over 2-adic Unramified Extension Field in a defined by x^2 + x + 1 sage: pushout(Ao, R2.fraction_field()) Tate Algebra in u (val >= -2), v (val >= -4) over 2-adic Eisenstein Extension Field in pi defined by x^2 - 2 TESTS:: sage: a*u (a + O(2^20))*u sage: (a*u).parent() is A1 True sage: pi*v (pi + O(pi^41))*v sage: (pi*v).parent() is A2 True """ if isinstance(R, pAdicGeneric): base = pushout(self._base, R) ratio = base.absolute_e() // self._base.absolute_e() cap = ratio * self._cap log_radii = [ratio * r for r in self._log_radii] A = TateAlgebra(base, cap, log_radii, self._names, self._order) if base.is_field(): return A else: return A.integer_ring()
def evaluate_at_poly(self, P, R=None, depth=None): r""" Evaluate ``self`` at a polynomial. The polynomial can be defined over ZZ or Zp. By default, this function picks the ring R to be a ring that coerces to both the base ring of the polynomial and the Bianchi distribution. You can specify depth, but this currently does nothing at all. EXAMPLES:: sage: from darmonpoints.ocbianchi import BianchiDistributions sage: D = BianchiDistributions(11,4) sage: x,y = D.analytic_vars() sage: mu = D.basis_vector((2,1)) + D.basis_vector((1,0)) sage: mu(x^2*y) 1 sage: mu(y) 0 sage: mu(x^2*y + x) 2 """ p = self._parent._p ## Currently empty functionality if depth is None: depth = self._depth # Define the ring R, if not specified if R is None: try: R = pushout(P.parent().base_ring(), self.parent().base_ring()) except AttributeError: R = self.parent().base_ring() ## Attempt to coerce the input into a form we can evaluate P = self.parent().analytic_functions()(P) ## For each monomial x^iy^j in the polynomial, multip] ly the coefficient of X^iY^j (in mu) by the ## coefficient of x^iy^j (in f) and take the sum. This is our final value ## --> P.coefficients is a dictionary which has monomials as keys; we generate monomials using exponents. ## --> self._moments takes as input an index and spits out the cofficient. So generate the index from the exponent. coefficient_list = [] for polx in P.padded_list(self._depth): coefficient_list.extend(polx.padded_list(self._depth)) return ZZ((Matrix(coefficient_list) * self.moments())[0, 0])
def SiegelModularFormG2SatohBracket(f, g, f_weight=None, g_weight=None): r""" INPUT: - `f` -- Fourier expansion of a classical Siegel modular form. - `g` -- Fourier expansion of a classical Siegel modular form. - ``f_weight`` -- the weight of `f` (default: ``None``). - ``g_weight`` -- the weight of `g` (default: ``None``). OUTPUT: - The Fourier expansion of a vector-valued Siegel modular form. """ if f_weight is None: f_weight = f.weight() if g_weight is None: g_weight = g.weight() if not isinstance(f.parent(), EquivariantMonoidPowerSeriesAmbient_abstract): f = f.fourier_expansion() if not isinstance(g.parent(), EquivariantMonoidPowerSeriesAmbient_abstract): g = g.fourier_expansion() if f.parent() != g.parent(): if f.parent().has_coerce_map_from(g.parent()): g = f.parent()(g) elif g.parent().has_coerce_map_from(f.parent()): f = g.parent()(f) else: from sage.categories.pushout import pushout parent = pushout(f.parent(), g.parent()) f = parent(f) g = parent(g) precision = min(f.precision(), g.precision()) expansion_ring = SiegelModularFormG2VVFourierExpansionRing( f.parent().coefficient_domain().fraction_field()) coefficients_factory = DelayedFactory_SMFG2_satohbracket( f, g, f_weight, g_weight, expansion_ring.coefficient_domain()) return EquivariantMonoidPowerSeries_lazy(expansion_ring, precision, coefficients_factory.getcoeff)
def __call__(self, R) : r""" The graded expansion ring with the given generators over the base ring `R`. INPUT: - `R` -- A ring. OUTPUT: An instance of :class:~`fourier_expansion_framework.gradedexpansions.gradedexpansion_ambient.GradedExpansion_abstract`. NOTE: The ring of Fourier expansions given by the generators associated with this functor must admit a base extension to `R`. TESTS:: sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_basicmonoids import * sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_ring import * sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_module import * sage: from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_element import * sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import DegreeGrading sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_functor import * sage: K.<rho> = CyclotomicField(6) sage: mps = MonoidPowerSeriesRing(QQ, NNMonoid(False)) sage: funct = GradedExpansionFunctor(Sequence([MonoidPowerSeries(mps, {1: 1}, mps.monoid().filter(4))]), Sequence([MonoidPowerSeries(mps, {1: 1, 2: 3}, mps.monoid().filter(4))]), PolynomialRing(QQ, ['a', 'b']).ideal(0), DegreeGrading((1,2))) sage: funct(K) Graded expansion ring with generators b sage: m = FreeModule(QQ, 3) sage: mpsm = MonoidPowerSeriesModule(m, NNMonoid(False)) sage: mps = mpsm.base_ring() sage: funct = GradedExpansionFunctor(None, Sequence([MonoidPowerSeries(mpsm, {1 : m([1,2,3]), 2 : m([3,-3,2])}, mpsm.monoid().filter(4)), MonoidPowerSeries(mpsm, {1 : m([2,-1,-1]), 2 : m([1,0,0])}, mpsm.monoid().filter(4))]), PolynomialRing(QQ, ['a', 'b']).ideal(0), DegreeGrading((1,2))) sage: funct(K) Graded expansion module with generators a, b """ from gradedexpansion_ring import GradedExpansionRing_class from gradedexpansion_module import GradedExpansionModule_class R = pushout(self.__relations.ring(), R) rel = R.ideal(self.__relations.gens()) if isinstance(self.__gen_expansions.universe(), Algebra) : return GradedExpansionRing_class( self.__base_gen_expansions, self.__gen_expansions, rel, self.__grading, self.__all_relations, self.__reduce_before_evaluating ) elif isinstance(self.__gen_expansions.universe(), Module) : return GradedExpansionModule_class( self.__base_gen_expansions, self.__gen_expansions, rel, self.__grading, self.__all_relations, self.__reduce_before_evaluating ) raise RuntimeError( "The generators' universe must be an algebra or a module." )
def SiegelModularFormG2SatohBracket(f, g, f_weight = None, g_weight = None) : r""" INPUT: - `f` -- Fourier expansion of a classical Siegel modular form. - `g` -- Fourier expansion of a classical Siegel modular form. - ``f_weight`` -- the weight of `f` (default: ``None``). - ``g_weight`` -- the weight of `g` (default: ``None``). OUTPUT: - The Fourier expansion of a vector-valued Siegel modular form. """ if f_weight is None : f_weight = f.weight() if g_weight is None : g_weight = g.weight() if not isinstance(f.parent(), EquivariantMonoidPowerSeriesAmbient_abstract) : f = f.fourier_expansion() if not isinstance(g.parent(), EquivariantMonoidPowerSeriesAmbient_abstract) : g = g.fourier_expansion() if f.parent() != g.parent() : if f.parent().has_coerce_map_from(g.parent()) : g = f.parent()(g) elif g.parent().has_coerce_map_from(f.parent()) : f = g.parent()(f) else : from sage.categories.pushout import pushout parent = pushout(f.parent(), g.parent()) f = parent(f) g = parent(g) precision = min(f.precision(), g.precision()) expansion_ring = SiegelModularFormG2VVFourierExpansionRing( f.parent().coefficient_domain().fraction_field() ) coefficients_factory = DelayedFactory_SMFG2_satohbracket( f, g, f_weight, g_weight, expansion_ring.coefficient_domain() ) return EquivariantMonoidPowerSeries_lazy( expansion_ring, precision, coefficients_factory.getcoeff )
def direct_sum(*matrices): r''' Method to compute the direct sum of several matrices. This method computes the direct sum of several matrices, i.e., it computes a diagonal matrix where the elements in the diagonal are the matrices that we have as input. This method is just a generic alias for the method :func:`diagonal_matrix`. However, it performs the checking of compatibility between the parents of the matrices. ''' from sage.categories.pushout import pushout #Computation of a common parent try: R = reduce(lambda p, q: pushout(p, q), [el.parent().base() for el in matrices]) except AttributeError: raise TypeError("All the elements must be matrices") return diagonal_matrix(R, matrices)
def evaluate_at_poly(self,P,R = None,depth = None): r""" Evaluate ``self`` at a polynomial """ p = self._parent._p if R is None: try: R = pushout(P.parent().base_ring(),self.parent().base_ring()) except AttributeError: R = self.parent().base_ring() if depth is None and hasattr(P,'degree'): try: depth = min([P.degree()+1,self._depth]) return sum(R(self._val[ii,0])*P[ii] for ii in xrange(depth)) except NotImplementedError: pass return R(self._val[0,0])*P else: return sum(R(self._val[ii,0])*P[ii] for ii in xrange(depth))
def evaluate_at_poly(self, P, R=None, depth=None): r""" Evaluate ``self`` at a polynomial """ p = self._parent._p if R is None: try: R = pushout(P.parent().base_ring(), self.parent().base_ring()) except AttributeError: R = self.parent().base_ring() if depth is None and hasattr(P, 'degree'): try: depth = min([P.degree() + 1, self._depth]) return sum(R(self._val[ii, 0]) * P[ii] for ii in xrange(depth)) except NotImplementedError: pass return R(self._val[0, 0]) * P else: return sum(R(self._val[ii, 0]) * P[ii] for ii in xrange(depth))
def shift(self, delta): r""" TESTS:: sage: from ore_algebra import DifferentialOperators sage: from ore_algebra.analytic.path import Point sage: from ore_algebra.analytic.differential_operator import DifferentialOperator sage: Dops, x, Dx = DifferentialOperators() sage: DifferentialOperator(x*Dx - 1).shift(Point(1)) (x + 1)*Dx - 1 sage: dop = DifferentialOperator(x*Dx - 1) sage: dop.shift(Point(RBF(1/2), dop)) (2*x + 1)*Dx - 2 """ Pols_dop = self.base_ring() # NOTE: pushout(QQ[x], K) doesn't handle embeddings well, and creates # an L equal but not identical to K. But then other constructors like # PolynomialRing(L, x) sometimes return objects over K found in cache, # leading to endless headaches with slow coercions. But the version here # may be closer to what I really want in any case. # XXX: This seems to work in the usual trivial case where we are looking # for a scalar domain containing QQ and QQ[i], but probably won't be # enough if we really have two different number fields with embeddings ex = delta.exact() Scalars = pushout.pushout(Pols_dop.base_ring(), ex.value.parent()) Pols = Pols_dop.change_ring(Scalars) A, B = self.base_ring().base_ring(), ex.value.parent() C = Pols.base_ring() assert C is A or C != A assert C is B or C != B dop_P = self.change_ring(Pols) # Gcd-avoiding shift by an algebraic delta deg = dop_P.degree() den = ex.value.denominator() num = den*ex.value lin = Pols([num, den]) x = Pols.gen() def shift_poly(pol): pol = (pol.reverse(deg)(den*x)).reverse(deg) return pol(lin) shifted = dop_P.map_coefficients(shift_poly) return ShiftedDifferentialOperator(shifted, self, delta)
def evaluate_at_poly(self,P): r""" EXAMPLES: This example illustrates ... :: """ p = self._parent._R.prime() try: R = pushout(P.parent().base_ring(),self.parent().base_ring()) except AttributeError: R = self.parent().base_ring() if hasattr(P,'degree'): try: r = min([P.degree()+1,self._depth]) return sum([R(self._val[ii,0])*P[ii] for ii in range(r)]) except NotImplementedError: pass return R(self._val[0,0])*P
def log_series(ini, bwrec, order): Coeffs = pushout(bwrec.base_ring.base_ring(), ini.universe) log_prec = sum(len(v) for v in ini.shift.itervalues()) precomp_len = max(1, bwrec.order) # hack for recurrences of order zero bwrec_nplus = collections.deque( (bwrec.eval_series(Coeffs, i, log_prec) for i in xrange(precomp_len)), maxlen=precomp_len) series = [] for n in xrange(order): new_term = vector(Coeffs, log_prec) mult = len(ini.shift.get(n, ())) for p in xrange(log_prec - mult - 1, -1, -1): combin = sum(bwrec_nplus[0][i][j] * series[-i][p + j] for j in xrange(log_prec - p) for i in xrange(min(bwrec.order, n), 0, -1)) combin += sum(bwrec_nplus[0][0][j] * new_term[p + j] for j in xrange(mult + 1, log_prec - p)) new_term[mult + p] = -~bwrec_nplus[0][0][mult] * combin for p in xrange(mult - 1, -1, -1): new_term[p] = ini.shift[n][p] series.append(new_term) bwrec_nplus.append(bwrec.eval_series(Coeffs, n + precomp_len, log_prec)) return series
def _pushout_(self, other): r""" Construct the pushout of this and the other growth group. This is called by :func:`sage.categories.pushout.pushout`. TESTS:: sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: from sage.categories.pushout import pushout sage: cm = sage.structure.element.get_coercion_model() sage: A = GrowthGroup('QQ^x * x^ZZ') sage: B = GrowthGroup('x^ZZ * log(x)^ZZ') sage: A._pushout_(B) Growth Group QQ^x * x^ZZ * log(x)^ZZ sage: pushout(A, B) Growth Group QQ^x * x^ZZ * log(x)^ZZ sage: cm.discover_coercion(A, B) ((map internal to coercion system -- copy before use) Conversion map: From: Growth Group QQ^x * x^ZZ To: Growth Group QQ^x * x^ZZ * log(x)^ZZ, (map internal to coercion system -- copy before use) Conversion map: From: Growth Group x^ZZ * log(x)^ZZ To: Growth Group QQ^x * x^ZZ * log(x)^ZZ) sage: cm.common_parent(A, B) Growth Group QQ^x * x^ZZ * log(x)^ZZ :: sage: C = GrowthGroup('QQ^x * x^QQ * y^ZZ') sage: D = GrowthGroup('x^ZZ * log(x)^QQ * QQ^z') sage: C._pushout_(D) Growth Group QQ^x * x^QQ * log(x)^QQ * y^ZZ * QQ^z sage: cm.common_parent(C, D) Growth Group QQ^x * x^QQ * log(x)^QQ * y^ZZ * QQ^z sage: A._pushout_(D) Growth Group QQ^x * x^ZZ * log(x)^QQ * QQ^z sage: cm.common_parent(A, D) Growth Group QQ^x * x^ZZ * log(x)^QQ * QQ^z sage: cm.common_parent(B, D) Growth Group x^ZZ * log(x)^QQ * QQ^z sage: cm.common_parent(A, C) Growth Group QQ^x * x^QQ * y^ZZ sage: E = GrowthGroup('log(x)^ZZ * y^ZZ') sage: cm.common_parent(A, E) Traceback (most recent call last): ... TypeError: no common canonical parent for objects with parents: 'Growth Group QQ^x * x^ZZ' and 'Growth Group log(x)^ZZ * y^ZZ' :: sage: F = GrowthGroup('z^QQ') sage: pushout(C, F) Growth Group QQ^x * x^QQ * y^ZZ * z^QQ :: sage: pushout(GrowthGroup('QQ^x * x^ZZ'), GrowthGroup('ZZ^x * x^QQ')) Growth Group QQ^x * x^QQ sage: cm.common_parent(GrowthGroup('QQ^x * x^ZZ'), GrowthGroup('ZZ^x * x^QQ')) Growth Group QQ^x * x^QQ :: sage: pushout(GrowthGroup('QQ^n * n^QQ'), GrowthGroup('SR^n')) Growth Group SR^n * n^QQ """ from growth_group import GenericGrowthGroup, AbstractGrowthGroupFunctor from misc import merge_overlapping from misc import underlying_class Sfactors = self.cartesian_factors() if isinstance(other, GenericProduct): Ofactors = other.cartesian_factors() elif isinstance(other, GenericGrowthGroup): Ofactors = (other, ) elif (other.construction() is not None and isinstance( other.construction()[0], AbstractGrowthGroupFunctor)): Ofactors = (other, ) else: return def pushout_univariate_factors(self, other, var, Sfactors, Ofactors): try: return merge_overlapping( Sfactors, Ofactors, lambda f: (underlying_class(f), f._var_.var_repr)) except ValueError: pass cm = sage.structure.element.get_coercion_model() try: Z = cm.common_parent(*Sfactors + Ofactors) return (Z, ), (Z, ) except TypeError: pass def subfactors(F): for f in F: if isinstance(f, GenericProduct): for g in subfactors(f.cartesian_factors()): yield g else: yield f try: return merge_overlapping( tuple(subfactors(Sfactors)), tuple(subfactors(Ofactors)), lambda f: (underlying_class(f), f._var_.var_repr)) except ValueError: pass from sage.structure.coerce_exceptions import CoercionException raise CoercionException( 'Cannot construct the pushout of %s and %s: The factors ' 'with variables %s are not overlapping, ' 'no common parent was found, and ' 'splitting the factors was unsuccessful.' % (self, other, var)) class it: def __init__(self, it): self.it = it self.var = None self.factors = None def next(self): try: self.var, factors = next(self.it) self.factors = tuple(factors) except StopIteration: self.var = None self.factors = tuple() from itertools import groupby S = it(groupby(Sfactors, key=lambda k: k.variable_names())) O = it(groupby(Ofactors, key=lambda k: k.variable_names())) newS = [] newO = [] S.next() O.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() elif O.var is not None and S.var > O.var: newS.extend(O.factors) newO.extend(O.factors) O.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() assert (len(newS) == len(newO)) if (len(Sfactors) == len(newS) and len(Ofactors) == len(newO)): # We had already all factors in each of self and # other, thus splitting it in subproblems (one for # each factor) is the strategy to use. If a pushout is # possible :func:`sage.categories.pushout.pushout` # will manage this by itself. return from sage.categories.pushout import pushout from sage.categories.cartesian_product import cartesian_product return pushout(cartesian_product(newS), cartesian_product(newO))
def _tate(self, proof = None, globally = False): r""" Tate's algorithm for an elliptic curve over a number field. Computes both local reduction data at a prime ideal and a local minimal model. The model is not required to be integral on input. If `P` is principal, uses a generator as uniformizer, so it will not affect integrality or minimality at other primes. If `P` is not principal, the minimal model returned will preserve integrality at other primes, but not minimality. The optional argument globally, when set to True, tells the algorithm to use the generator of the prime ideal if it is principal. Otherwise just any uniformizer will be used. .. note:: Called only by ``EllipticCurveLocalData.__init__()``. OUTPUT: (tuple) ``(Emin, p, val_disc, fp, KS, cp)`` where: - ``Emin`` (EllipticCurve) is a model (integral and) minimal at P - ``p`` (int) is the residue characteristic - ``val_disc`` (int) is the valuation of the local minimal discriminant - ``fp`` (int) is the valuation of the conductor - ``KS`` (string) is the Kodaira symbol - ``cp`` (int) is the Tamagawa number EXAMPLES (this raised a type error in sage prior to 4.4.4, see :trac:`7930`) :: sage: E = EllipticCurve('99d1') sage: R.<X> = QQ[] sage: K.<t> = NumberField(X^3 + X^2 - 2*X - 1) sage: L.<s> = NumberField(X^3 + X^2 - 36*X - 4) sage: EK = E.base_extend(K) sage: toK = EK.torsion_order() sage: da = EK.local_data() # indirect doctest sage: EL = E.base_extend(L) sage: da = EL.local_data() # indirect doctest EXAMPLES: The following example shows that the bug at :trac:`9324` is fixed:: sage: K.<a> = NumberField(x^2-x+6) sage: E = EllipticCurve([0,0,0,-53160*a-43995,-5067640*a+19402006]) sage: E.conductor() # indirect doctest Fractional ideal (18, 6*a) The following example shows that the bug at :trac:`9417` is fixed:: sage: K.<a> = NumberField(x^2+18*x+1) sage: E = EllipticCurve(K, [0, -36, 0, 320, 0]) sage: E.tamagawa_number(K.ideal(2)) 4 This is to show that the bug :trac:`11630` is fixed. (The computation of the class group would produce a warning):: sage: K.<t> = NumberField(x^7-2*x+177) sage: E = EllipticCurve([0,1,0,t,t]) sage: P = K.ideal(2,t^3 + t + 1) sage: E.local_data(P).kodaira_symbol() II """ E = self._curve P = self._prime K = E.base_ring() OK = K.maximal_order() t = verbose("Running Tate's algorithm with P = %s"%P, level=1) F = OK.residue_field(P) p = F.characteristic() # In case P is not principal we mostly use a uniformiser which # is globally integral (with positive valuation at some other # primes); for this to work, it is essential that we can # reduce (mod P) elements of K which are not integral (but are # P-integral). However, if the model is non-minimal and we # end up dividing a_i by pi^i then at that point we use a # uniformiser pi which has non-positive valuation at all other # primes, so that we can divide by it without losing # integrality at other primes. if globally: principal_flag = P.is_principal() else: principal_flag = False if (K is QQ) or principal_flag : pi = P.gens_reduced()[0] verbose("P is principal, generator pi = %s"%pi, t, 1) else: pi = K.uniformizer(P, 'positive') verbose("uniformizer pi = %s"%pi, t, 1) pi2 = pi*pi; pi3 = pi*pi2; pi4 = pi*pi3 pi_neg = None prime = pi if K is QQ else P pval = lambda x: x.valuation(prime) pdiv = lambda x: x.is_zero() or pval(x) > 0 # Since ResidueField is cached in a way that # does not care much about embeddings of number # fields, it can happen that F.p.ring() is different # from K. This is a problem: If F.p.ring() has no # embedding but K has, then there is no coercion # from F.p.ring().maximal_order() to K. But it is # no problem to do an explicit conversion in that # case (Simon King, trac ticket #8800). from sage.categories.pushout import pushout, CoercionException try: if hasattr(F.p.ring(), 'maximal_order'): # it is not ZZ _tmp_ = pushout(F.p.ring().maximal_order(),K) pinv = lambda x: F.lift(~F(x)) proot = lambda x,e: F.lift(F(x).nth_root(e, extend = False, all = True)[0]) preduce = lambda x: F.lift(F(x)) except CoercionException: # the pushout does not exist, we need conversion pinv = lambda x: K(F.lift(~F(x))) proot = lambda x,e: K(F.lift(F(x).nth_root(e, extend = False, all = True)[0])) preduce = lambda x: K(F.lift(F(x))) def _pquadroots(a, b, c): r""" Local function returning True iff `ax^2 + bx + c` has roots modulo `P` """ (a, b, c) = (F(a), F(b), F(c)) if a == 0: return (b != 0) or (c == 0) elif p == 2: return len(PolynomialRing(F, "x")([c,b,a]).roots()) > 0 else: return (b**2 - 4*a*c).is_square() def _pcubicroots(b, c, d): r""" Local function returning the number of roots of `x^3 + b*x^2 + c*x + d` modulo `P`, counting multiplicities """ return sum([rr[1] for rr in PolynomialRing(F, 'x')([F(d), F(c), F(b), F(1)]).roots()],0) if p == 2: halfmodp = OK(Integer(0)) else: halfmodp = pinv(Integer(2)) A = E.a_invariants() A = [0, A[0], A[1], A[2], A[3], 0, A[4]] indices = [1,2,3,4,6] if min([pval(a) for a in A if a != 0]) < 0: verbose("Non-integral model at P: valuations are %s; making integral"%([pval(a) for a in A if a != 0]), t, 1) e = 0 for i in range(7): if A[i] != 0: e = max(e, (-pval(A[i])/i).ceil()) pie = pi**e for i in range(7): if A[i] != 0: A[i] *= pie**i verbose("P-integral model is %s, with valuations %s"%([A[i] for i in indices], [pval(A[i]) for i in indices]), t, 1) split = None # only relevant for multiplicative reduction (a1, a2, a3, a4, a6) = (A[1], A[2], A[3], A[4], A[6]) while True: C = EllipticCurve([a1, a2, a3, a4, a6]); (b2, b4, b6, b8) = C.b_invariants() (c4, c6) = C.c_invariants() delta = C.discriminant() val_disc = pval(delta) if val_disc == 0: ## Good reduction already cp = 1 fp = 0 KS = KodairaSymbol("I0") break #return # Otherwise, we change coordinates so that p | a3, a4, a6 if p == 2: if pdiv(b2): r = proot(a4, 2) t = proot(((r + a2)*r + a4)*r + a6, 2) else: temp = pinv(a1) r = temp * a3 t = temp * (a4 + r*r) elif p == 3: if pdiv(b2): r = proot(-b6, 3) else: r = -pinv(b2) * b4 t = a1 * r + a3 else: if pdiv(c4): r = -pinv(12) * b2 else: r = -pinv(12*c4) * (c6 + b2 * c4) t = -halfmodp * (a1 * r + a3) r = preduce(r) t = preduce(t) verbose("Before first transform C = %s"%C) verbose("[a1,a2,a3,a4,a6] = %s"%([a1, a2, a3, a4, a6])) C = C.rst_transform(r, 0, t) (a1, a2, a3, a4, a6) = C.a_invariants() (b2, b4, b6, b8) = C.b_invariants() if min([pval(a) for a in (a1, a2, a3, a4, a6) if a != 0]) < 0: raise RuntimeError("Non-integral model after first transform!") verbose("After first transform %s\n, [a1,a2,a3,a4,a6] = %s\n, valuations = %s"%([r, 0, t], [a1, a2, a3, a4, a6], [pval(a1), pval(a2), pval(a3), pval(a4), pval(a6)]), t, 2) if pval(a3) == 0: raise RuntimeError("p does not divide a3 after first transform!") if pval(a4) == 0: raise RuntimeError("p does not divide a4 after first transform!") if pval(a6) == 0: raise RuntimeError("p does not divide a6 after first transform!") # Now we test for Types In, II, III, IV # NB the c invariants never change. if not pdiv(c4): # Multiplicative reduction: Type In (n = val_disc) split = False if _pquadroots(1, a1, -a2): cp = val_disc split = True elif Integer(2).divides(val_disc): cp = 2 else: cp = 1 KS = KodairaSymbol("I%s"%val_disc) fp = 1 break #return # Additive reduction if pval(a6) < 2: ## Type II KS = KodairaSymbol("II") fp = val_disc cp = 1 break #return if pval(b8) < 3: ## Type III KS = KodairaSymbol("III") fp = val_disc - 1 cp = 2 break #return if pval(b6) < 3: ## Type IV cp = 1 a3t = preduce(a3/pi) a6t = preduce(a6/pi2) if _pquadroots(1, a3t, -a6t): cp = 3 KS = KodairaSymbol("IV") fp = val_disc - 2 break #return # If our curve is none of these types, we change coords so that # p | a1, a2; p^2 | a3, a4; p^3 | a6 if p == 2: s = proot(a2, 2) # so s^2=a2 (mod pi) t = pi*proot(a6/pi2, 2) # so t^2=a6 (mod pi^3) elif p == 3: s = a1 # so a1'=2s+a1=3a1=0 (mod pi) t = a3 # so a3'=2t+a3=3a3=0 (mod pi^2) else: s = -a1*halfmodp # so a1'=2s+a1=0 (mod pi) t = -a3*halfmodp # so a3'=2t+a3=0 (mod pi^2) C = C.rst_transform(0, s, t) (a1, a2, a3, a4, a6) = C.a_invariants() (b2, b4, b6, b8) = C.b_invariants() verbose("After second transform %s\n[a1, a2, a3, a4, a6] = %s\nValuations: %s"%([0, s, t], [a1,a2,a3,a4,a6],[pval(a1),pval(a2),pval(a3),pval(a4),pval(a6)]), t, 2) if pval(a1) == 0: raise RuntimeError("p does not divide a1 after second transform!") if pval(a2) == 0: raise RuntimeError("p does not divide a2 after second transform!") if pval(a3) < 2: raise RuntimeError("p^2 does not divide a3 after second transform!") if pval(a4) < 2: raise RuntimeError("p^2 does not divide a4 after second transform!") if pval(a6) < 3: raise RuntimeError("p^3 does not divide a6 after second transform!") if min(pval(a1), pval(a2), pval(a3), pval(a4), pval(a6)) < 0: raise RuntimeError("Non-integral model after second transform!") # Analyze roots of the cubic T^3 + bT^2 + cT + d = 0 mod P, where # b = a2/p, c = a4/p^2, d = a6/p^3 b = preduce(a2/pi) c = preduce(a4/pi2) d = preduce(a6/pi3) bb = b*b cc = c*c bc = b*c w = 27*d*d - bb*cc + 4*b*bb*d - 18*bc*d + 4*c*cc x = 3*c - bb if pdiv(w): if pdiv(x): sw = 3 else: sw = 2 else: sw = 1 verbose("Analyzing roots of cubic T^3 + %s*T^2 + %s*T + %s, case %s"%(b, c, d, sw), t, 1) if sw == 1: ## Three distinct roots - Type I*0 verbose("Distinct roots", t, 1) KS = KodairaSymbol("I0*") cp = 1 + _pcubicroots(b, c, d) fp = val_disc - 4 break #return elif sw == 2: ## One double root - Type I*m for some m verbose("One double root", t, 1) ## Change coords so that the double root is T = 0 mod p if p == 2: r = proot(c, 2) elif p == 3: r = c * pinv(b) else: r = (bc - 9*d)*pinv(2*x) r = pi * preduce(r) C = C.rst_transform(r, 0, 0) (a1, a2, a3, a4, a6) = C.a_invariants() (b2, b4, b6, b8) = C.b_invariants() # The rest of this branch is just to compute cp, fp, KS. # We use pi to keep transforms integral. ix = 3; iy = 3; mx = pi2; my = mx while True: a2t = preduce(a2 / pi) a3t = preduce(a3 / my) a4t = preduce(a4 / (pi*mx)) a6t = preduce(a6 / (mx*my)) if pdiv(a3t*a3t + 4*a6t): if p == 2: t = my*proot(a6t, 2) else: t = my*preduce(-a3t*halfmodp) C = C.rst_transform(0, 0, t) (a1, a2, a3, a4, a6) = C.a_invariants() (b2, b4, b6, b8) = C.b_invariants() my *= pi iy += 1 a2t = preduce(a2 / pi) a3t = preduce(a3/my) a4t = preduce(a4/(pi*mx)) a6t = preduce(a6/(mx*my)) if pdiv(a4t*a4t - 4*a6t*a2t): if p == 2: r = mx*proot(a6t*pinv(a2t), 2) else: r = mx*preduce(-a4t*pinv(2*a2t)) C = C.rst_transform(r, 0, 0) (a1, a2, a3, a4, a6) = C.a_invariants() (b2, b4, b6, b8) = C.b_invariants() mx *= pi ix += 1 # and stay in loop else: if _pquadroots(a2t, a4t, a6t): cp = 4 else: cp = 2 break # exit loop else: if _pquadroots(1, a3t, -a6t): cp = 4 else: cp = 2 break KS = KodairaSymbol("I%s*"%(ix+iy-5)) fp = val_disc - ix - iy + 1 break #return else: # sw == 3 ## The cubic has a triple root verbose("Triple root", t, 1) ## First we change coordinates so that T = 0 mod p if p == 2: r = b elif p == 3: r = proot(-d, 3) else: r = -b * pinv(3) r = pi*preduce(r) C = C.rst_transform(r, 0, 0) (a1, a2, a3, a4, a6) = C.a_invariants() (b2, b4, b6, b8) = C.b_invariants() verbose("After third transform %s\n[a1,a2,a3,a4,a6] = %s\nValuations: %s"%([r,0,0],[a1,a2,a3,a4,a6],[pval(ai) for ai in [a1,a2,a3,a4,a6]]), t, 2) if min(pval(ai) for ai in [a1,a2,a3,a4,a6]) < 0: raise RuntimeError("Non-integral model after third transform!") if pval(a2) < 2 or pval(a4) < 3 or pval(a6) < 4: raise RuntimeError("Cubic after transform does not have a triple root at 0") a3t = preduce(a3/pi2) a6t = preduce(a6/pi4) # We test for Type IV* if not pdiv(a3t*a3t + 4*a6t): cp = 3 if _pquadroots(1, a3t, -a6t) else 1 KS = KodairaSymbol("IV*") fp = val_disc - 6 break #return # Now change coordinates so that p^3|a3, p^5|a6 if p==2: t = -pi2*proot(a6t, 2) else: t = pi2*preduce(-a3t*halfmodp) C = C.rst_transform(0, 0, t) (a1, a2, a3, a4, a6) = C.a_invariants() (b2, b4, b6, b8) = C.b_invariants() # We test for types III* and II* if pval(a4) < 4: ## Type III* KS = KodairaSymbol("III*") fp = val_disc - 7 cp = 2 break #return if pval(a6) < 6: ## Type II* KS = KodairaSymbol("II*") fp = val_disc - 8 cp = 1 break #return if pi_neg is None: if principal_flag: pi_neg = pi else: pi_neg = K.uniformizer(P, 'negative') pi_neg2 = pi_neg*pi_neg pi_neg3 = pi_neg*pi_neg2 pi_neg4 = pi_neg*pi_neg3 pi_neg6 = pi_neg4*pi_neg2 a1 /= pi_neg a2 /= pi_neg2 a3 /= pi_neg3 a4 /= pi_neg4 a6 /= pi_neg6 verbose("Non-minimal equation, dividing out...\nNew model is %s"%([a1, a2, a3, a4, a6]), t, 1) return (C, p, val_disc, fp, KS, cp, split)
def _pushout_(self, other): r""" Construct the pushout of this and the other growth group. This is called by :func:`sage.categories.pushout.pushout`. TESTS:: sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: from sage.categories.pushout import pushout sage: cm = sage.structure.element.get_coercion_model() sage: A = GrowthGroup('QQ^x * x^ZZ') sage: B = GrowthGroup('x^ZZ * log(x)^ZZ') sage: A._pushout_(B) Growth Group QQ^x * x^ZZ * log(x)^ZZ sage: pushout(A, B) Growth Group QQ^x * x^ZZ * log(x)^ZZ sage: cm.discover_coercion(A, B) ((map internal to coercion system -- copy before use) Conversion map: From: Growth Group QQ^x * x^ZZ To: Growth Group QQ^x * x^ZZ * log(x)^ZZ, (map internal to coercion system -- copy before use) Conversion map: From: Growth Group x^ZZ * log(x)^ZZ To: Growth Group QQ^x * x^ZZ * log(x)^ZZ) sage: cm.common_parent(A, B) Growth Group QQ^x * x^ZZ * log(x)^ZZ :: sage: C = GrowthGroup('QQ^x * x^QQ * y^ZZ') sage: D = GrowthGroup('x^ZZ * log(x)^QQ * QQ^z') sage: C._pushout_(D) Growth Group QQ^x * x^QQ * log(x)^QQ * y^ZZ * QQ^z sage: cm.common_parent(C, D) Growth Group QQ^x * x^QQ * log(x)^QQ * y^ZZ * QQ^z sage: A._pushout_(D) Growth Group QQ^x * x^ZZ * log(x)^QQ * QQ^z sage: cm.common_parent(A, D) Growth Group QQ^x * x^ZZ * log(x)^QQ * QQ^z sage: cm.common_parent(B, D) Growth Group x^ZZ * log(x)^QQ * QQ^z sage: cm.common_parent(A, C) Growth Group QQ^x * x^QQ * y^ZZ sage: E = GrowthGroup('log(x)^ZZ * y^ZZ') sage: cm.common_parent(A, E) Traceback (most recent call last): ... TypeError: no common canonical parent for objects with parents: 'Growth Group QQ^x * x^ZZ' and 'Growth Group log(x)^ZZ * y^ZZ' :: sage: F = GrowthGroup('z^QQ') sage: pushout(C, F) Growth Group QQ^x * x^QQ * y^ZZ * z^QQ :: sage: pushout(GrowthGroup('QQ^x * x^ZZ'), GrowthGroup('ZZ^x * x^QQ')) Growth Group QQ^x * x^QQ sage: cm.common_parent(GrowthGroup('QQ^x * x^ZZ'), GrowthGroup('ZZ^x * x^QQ')) Growth Group QQ^x * x^QQ """ from growth_group import GenericGrowthGroup, AbstractGrowthGroupFunctor from misc import merge_overlapping from misc import underlying_class if isinstance(other, GenericProduct): Ofactors = other.cartesian_factors() elif isinstance(other, GenericGrowthGroup): Ofactors = (other,) elif other.construction() is not None and isinstance(other.construction()[0], AbstractGrowthGroupFunctor): Ofactors = (other,) else: return def pushout_univariate_factors(self, other, var, Sfactors, Ofactors): try: return merge_overlapping(Sfactors, Ofactors, lambda f: (underlying_class(f), f._var_.var_repr)) except ValueError: pass cm = sage.structure.element.get_coercion_model() try: Z = cm.common_parent(*Sfactors + Ofactors) return (Z,), (Z,) except TypeError: pass def subfactors(F): for f in F: if isinstance(f, GenericProduct): for g in subfactors(f.cartesian_factors()): yield g else: yield f try: return merge_overlapping( tuple(subfactors(Sfactors)), tuple(subfactors(Ofactors)), lambda f: (underlying_class(f), f._var_.var_repr), ) except ValueError: pass from sage.structure.coerce_exceptions import CoercionException raise CoercionException( "Cannot construct the pushout of %s and %s: The factors " "with variables %s are not overlapping, " "no common parent was found, and " "splitting the factors was unsuccessful." % (self, other, var) ) class it: def __init__(self, it): self.it = it self.var = None self.factors = None def next(self): try: self.var, factors = next(self.it) self.factors = tuple(factors) except StopIteration: self.var = None self.factors = tuple() from itertools import groupby S = it(groupby(self.cartesian_factors(), key=lambda k: k.variable_names())) O = it(groupby(Ofactors, key=lambda k: k.variable_names())) newS = [] newO = [] S.next() O.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() elif O.var is not None and S.var > O.var: newS.extend(O.factors) newO.extend(O.factors) O.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() assert len(newS) == len(newO) if len(self.cartesian_factors()) == len(newS) and len(other.cartesian_factors()) == len(newO): # We had already all factors in each of the self and # other, thus splitting it in subproblems (one for # each factor) is the strategy to use. If a pushout is # possible :func:`sage.categories.pushout.pushout` # will manage this by itself. return from sage.categories.pushout import pushout from sage.categories.cartesian_product import cartesian_product return pushout(cartesian_product(newS), cartesian_product(newO))
def _tate(self, proof = None, globally = False): r""" Tate's algorithm for an elliptic curve over a number field. Computes both local reduction data at a prime ideal and a local minimal model. The model is not required to be integral on input. If `P` is principal, uses a generator as uniformizer, so it will not affect integrality or minimality at other primes. If `P` is not principal, the minimal model returned will preserve integrality at other primes, but not minimality. The optional argument globally, when set to True, tells the algorithm to use the generator of the prime ideal if it is principal. Otherwise just any uniformizer will be used. .. note:: Called only by ``EllipticCurveLocalData.__init__()``. OUTPUT: (tuple) ``(Emin, p, val_disc, fp, KS, cp)`` where: - ``Emin`` (EllipticCurve) is a model (integral and) minimal at P - ``p`` (int) is the residue characteristic - ``val_disc`` (int) is the valuation of the local minimal discriminant - ``fp`` (int) is the valuation of the conductor - ``KS`` (string) is the Kodaira symbol - ``cp`` (int) is the Tamagawa number EXAMPLES (this raised a type error in sage prior to 4.4.4, see :trac:`7930`) :: sage: E = EllipticCurve('99d1') sage: R.<X> = QQ[] sage: K.<t> = NumberField(X^3 + X^2 - 2*X - 1) sage: L.<s> = NumberField(X^3 + X^2 - 36*X - 4) sage: EK = E.base_extend(K) sage: toK = EK.torsion_order() sage: da = EK.local_data() # indirect doctest sage: EL = E.base_extend(L) sage: da = EL.local_data() # indirect doctest EXAMPLES: The following example shows that the bug at :trac:`9324` is fixed:: sage: K.<a> = NumberField(x^2-x+6) sage: E = EllipticCurve([0,0,0,-53160*a-43995,-5067640*a+19402006]) sage: E.conductor() # indirect doctest Fractional ideal (18, 6*a) The following example shows that the bug at :trac:`9417` is fixed:: sage: K.<a> = NumberField(x^2+18*x+1) sage: E = EllipticCurve(K, [0, -36, 0, 320, 0]) sage: E.tamagawa_number(K.ideal(2)) 4 This is to show that the bug :trac: `11630` is fixed. (The computation of the class group would produce a warning):: sage: K.<t> = NumberField(x^7-2*x+177) sage: E = EllipticCurve([0,1,0,t,t]) sage: P = K.ideal(2,t^3 + t + 1) sage: E.local_data(P).kodaira_symbol() II """ E = self._curve P = self._prime K = E.base_ring() OK = K.maximal_order() t = verbose("Running Tate's algorithm with P = %s"%P, level=1) F = OK.residue_field(P) p = F.characteristic() # In case P is not principal we mostly use a uniformiser which # is globally integral (with positive valuation at some other # primes); for this to work, it is essential that we can # reduce (mod P) elements of K which are not integral (but are # P-integral). However, if the model is non-minimal and we # end up dividing a_i by pi^i then at that point we use a # uniformiser pi which has non-positive valuation at all other # primes, so that we can divide by it without losing # integrality at other primes. if globally: principal_flag = P.is_principal() else: principal_flag = False if (K is QQ) or principal_flag : pi = P.gens_reduced()[0] verbose("P is principal, generator pi = %s"%pi, t, 1) else: pi = K.uniformizer(P, 'positive') verbose("uniformizer pi = %s"%pi, t, 1) pi2 = pi*pi; pi3 = pi*pi2; pi4 = pi*pi3 pi_neg = None prime = pi if K is QQ else P pval = lambda x: x.valuation(prime) pdiv = lambda x: x.is_zero() or pval(x) > 0 # Since ResidueField is cached in a way that # does not care much about embeddings of number # fields, it can happen that F.p.ring() is different # from K. This is a problem: If F.p.ring() has no # embedding but K has, then there is no coercion # from F.p.ring().maximal_order() to K. But it is # no problem to do an explicit conversion in that # case (Simon King, trac ticket #8800). from sage.categories.pushout import pushout, CoercionException try: if hasattr(F.p.ring(), 'maximal_order'): # it is not ZZ _tmp_ = pushout(F.p.ring().maximal_order(),K) pinv = lambda x: F.lift(~F(x)) proot = lambda x,e: F.lift(F(x).nth_root(e, extend = False, all = True)[0]) preduce = lambda x: F.lift(F(x)) except CoercionException: # the pushout does not exist, we need conversion pinv = lambda x: K(F.lift(~F(x))) proot = lambda x,e: K(F.lift(F(x).nth_root(e, extend = False, all = True)[0])) preduce = lambda x: K(F.lift(F(x))) def _pquadroots(a, b, c): r""" Local function returning True iff `ax^2 + bx + c` has roots modulo `P` """ (a, b, c) = (F(a), F(b), F(c)) if a == 0: return (b != 0) or (c == 0) elif p == 2: return len(PolynomialRing(F, "x")([c,b,a]).roots()) > 0 else: return (b**2 - 4*a*c).is_square() def _pcubicroots(b, c, d): r""" Local function returning the number of roots of `x^3 + b*x^2 + c*x + d` modulo `P`, counting multiplicities """ return sum([rr[1] for rr in PolynomialRing(F, 'x')([F(d), F(c), F(b), F(1)]).roots()],0) if p == 2: halfmodp = OK(Integer(0)) else: halfmodp = pinv(Integer(2)) A = E.a_invariants() A = [0, A[0], A[1], A[2], A[3], 0, A[4]] indices = [1,2,3,4,6] if min([pval(a) for a in A if a != 0]) < 0: verbose("Non-integral model at P: valuations are %s; making integral"%([pval(a) for a in A if a != 0]), t, 1) e = 0 for i in range(7): if A[i] != 0: e = max(e, (-pval(A[i])/i).ceil()) pie = pi**e for i in range(7): if A[i] != 0: A[i] *= pie**i verbose("P-integral model is %s, with valuations %s"%([A[i] for i in indices], [pval(A[i]) for i in indices]), t, 1) split = None # only relevant for multiplicative reduction (a1, a2, a3, a4, a6) = (A[1], A[2], A[3], A[4], A[6]) while True: C = EllipticCurve([a1, a2, a3, a4, a6]); (b2, b4, b6, b8) = C.b_invariants() (c4, c6) = C.c_invariants() delta = C.discriminant() val_disc = pval(delta) if val_disc == 0: ## Good reduction already cp = 1 fp = 0 KS = KodairaSymbol("I0") break #return # Otherwise, we change coordinates so that p | a3, a4, a6 if p == 2: if pdiv(b2): r = proot(a4, 2) t = proot(((r + a2)*r + a4)*r + a6, 2) else: temp = pinv(a1) r = temp * a3 t = temp * (a4 + r*r) elif p == 3: if pdiv(b2): r = proot(-b6, 3) else: r = -pinv(b2) * b4 t = a1 * r + a3 else: if pdiv(c4): r = -pinv(12) * b2 else: r = -pinv(12*c4) * (c6 + b2 * c4) t = -halfmodp * (a1 * r + a3) r = preduce(r) t = preduce(t) verbose("Before first transform C = %s"%C) verbose("[a1,a2,a3,a4,a6] = %s"%([a1, a2, a3, a4, a6])) C = C.rst_transform(r, 0, t) (a1, a2, a3, a4, a6) = C.a_invariants() (b2, b4, b6, b8) = C.b_invariants() if min([pval(a) for a in (a1, a2, a3, a4, a6) if a != 0]) < 0: raise RuntimeError("Non-integral model after first transform!") verbose("After first transform %s\n, [a1,a2,a3,a4,a6] = %s\n, valuations = %s"%([r, 0, t], [a1, a2, a3, a4, a6], [pval(a1), pval(a2), pval(a3), pval(a4), pval(a6)]), t, 2) if pval(a3) == 0: raise RuntimeError("p does not divide a3 after first transform!") if pval(a4) == 0: raise RuntimeError("p does not divide a4 after first transform!") if pval(a6) == 0: raise RuntimeError("p does not divide a6 after first transform!") # Now we test for Types In, II, III, IV # NB the c invariants never change. if not pdiv(c4): # Multiplicative reduction: Type In (n = val_disc) split = False if _pquadroots(1, a1, -a2): cp = val_disc split = True elif Integer(2).divides(val_disc): cp = 2 else: cp = 1 KS = KodairaSymbol("I%s"%val_disc) fp = 1 break #return # Additive reduction if pval(a6) < 2: ## Type II KS = KodairaSymbol("II") fp = val_disc cp = 1 break #return if pval(b8) < 3: ## Type III KS = KodairaSymbol("III") fp = val_disc - 1 cp = 2 break #return if pval(b6) < 3: ## Type IV cp = 1 a3t = preduce(a3/pi) a6t = preduce(a6/pi2) if _pquadroots(1, a3t, -a6t): cp = 3 KS = KodairaSymbol("IV") fp = val_disc - 2 break #return # If our curve is none of these types, we change coords so that # p | a1, a2; p^2 | a3, a4; p^3 | a6 if p == 2: s = proot(a2, 2) # so s^2=a2 (mod pi) t = pi*proot(a6/pi2, 2) # so t^2=a6 (mod pi^3) elif p == 3: s = a1 # so a1'=2s+a1=3a1=0 (mod pi) t = a3 # so a3'=2t+a3=3a3=0 (mod pi^2) else: s = -a1*halfmodp # so a1'=2s+a1=0 (mod pi) t = -a3*halfmodp # so a3'=2t+a3=0 (mod pi^2) C = C.rst_transform(0, s, t) (a1, a2, a3, a4, a6) = C.a_invariants() (b2, b4, b6, b8) = C.b_invariants() verbose("After second transform %s\n[a1, a2, a3, a4, a6] = %s\nValuations: %s"%([0, s, t], [a1,a2,a3,a4,a6],[pval(a1),pval(a2),pval(a3),pval(a4),pval(a6)]), t, 2) if pval(a1) == 0: raise RuntimeError("p does not divide a1 after second transform!") if pval(a2) == 0: raise RuntimeError("p does not divide a2 after second transform!") if pval(a3) < 2: raise RuntimeError("p^2 does not divide a3 after second transform!") if pval(a4) < 2: raise RuntimeError("p^2 does not divide a4 after second transform!") if pval(a6) < 3: raise RuntimeError("p^3 does not divide a6 after second transform!") if min(pval(a1), pval(a2), pval(a3), pval(a4), pval(a6)) < 0: raise RuntimeError("Non-integral model after second transform!") # Analyze roots of the cubic T^3 + bT^2 + cT + d = 0 mod P, where # b = a2/p, c = a4/p^2, d = a6/p^3 b = preduce(a2/pi) c = preduce(a4/pi2) d = preduce(a6/pi3) bb = b*b cc = c*c bc = b*c w = 27*d*d - bb*cc + 4*b*bb*d - 18*bc*d + 4*c*cc x = 3*c - bb if pdiv(w): if pdiv(x): sw = 3 else: sw = 2 else: sw = 1 verbose("Analyzing roots of cubic T^3 + %s*T^2 + %s*T + %s, case %s"%(b, c, d, sw), t, 1) if sw == 1: ## Three distinct roots - Type I*0 verbose("Distinct roots", t, 1) KS = KodairaSymbol("I0*") cp = 1 + _pcubicroots(b, c, d) fp = val_disc - 4 break #return elif sw == 2: ## One double root - Type I*m for some m verbose("One double root", t, 1) ## Change coords so that the double root is T = 0 mod p if p == 2: r = proot(c, 2) elif p == 3: r = c * pinv(b) else: r = (bc - 9*d)*pinv(2*x) r = pi * preduce(r) C = C.rst_transform(r, 0, 0) (a1, a2, a3, a4, a6) = C.a_invariants() (b2, b4, b6, b8) = C.b_invariants() # The rest of this branch is just to compute cp, fp, KS. # We use pi to keep transforms integral. ix = 3; iy = 3; mx = pi2; my = mx while True: a2t = preduce(a2 / pi) a3t = preduce(a3 / my) a4t = preduce(a4 / (pi*mx)) a6t = preduce(a6 / (mx*my)) if pdiv(a3t*a3t + 4*a6t): if p == 2: t = my*proot(a6t, 2) else: t = my*preduce(-a3t*halfmodp) C = C.rst_transform(0, 0, t) (a1, a2, a3, a4, a6) = C.a_invariants() (b2, b4, b6, b8) = C.b_invariants() my *= pi iy += 1 a2t = preduce(a2 / pi) a3t = preduce(a3/my) a4t = preduce(a4/(pi*mx)) a6t = preduce(a6/(mx*my)) if pdiv(a4t*a4t - 4*a6t*a2t): if p == 2: r = mx*proot(a6t*pinv(a2t), 2) else: r = mx*preduce(-a4t*pinv(2*a2t)) C = C.rst_transform(r, 0, 0) (a1, a2, a3, a4, a6) = C.a_invariants() (b2, b4, b6, b8) = C.b_invariants() mx *= pi ix += 1 # and stay in loop else: if _pquadroots(a2t, a4t, a6t): cp = 4 else: cp = 2 break # exit loop else: if _pquadroots(1, a3t, -a6t): cp = 4 else: cp = 2 break KS = KodairaSymbol("I%s*"%(ix+iy-5)) fp = val_disc - ix - iy + 1 break #return else: # sw == 3 ## The cubic has a triple root verbose("Triple root", t, 1) ## First we change coordinates so that T = 0 mod p if p == 2: r = b elif p == 3: r = proot(-d, 3) else: r = -b * pinv(3) r = pi*preduce(r) C = C.rst_transform(r, 0, 0) (a1, a2, a3, a4, a6) = C.a_invariants() (b2, b4, b6, b8) = C.b_invariants() verbose("After third transform %s\n[a1,a2,a3,a4,a6] = %s\nValuations: %s"%([r,0,0],[a1,a2,a3,a4,a6],[pval(ai) for ai in [a1,a2,a3,a4,a6]]), t, 2) if min(pval(ai) for ai in [a1,a2,a3,a4,a6]) < 0: raise RuntimeError("Non-integral model after third transform!") if pval(a2) < 2 or pval(a4) < 3 or pval(a6) < 4: raise RuntimeError("Cubic after transform does not have a triple root at 0") a3t = preduce(a3/pi2) a6t = preduce(a6/pi4) # We test for Type IV* if not pdiv(a3t*a3t + 4*a6t): cp = 3 if _pquadroots(1, a3t, -a6t) else 1 KS = KodairaSymbol("IV*") fp = val_disc - 6 break #return # Now change coordinates so that p^3|a3, p^5|a6 if p==2: t = -pi2*proot(a6t, 2) else: t = pi2*preduce(-a3t*halfmodp) C = C.rst_transform(0, 0, t) (a1, a2, a3, a4, a6) = C.a_invariants() (b2, b4, b6, b8) = C.b_invariants() # We test for types III* and II* if pval(a4) < 4: ## Type III* KS = KodairaSymbol("III*") fp = val_disc - 7 cp = 2 break #return if pval(a6) < 6: ## Type II* KS = KodairaSymbol("II*") fp = val_disc - 8 cp = 1 break #return if pi_neg is None: if principal_flag: pi_neg = pi else: pi_neg = K.uniformizer(P, 'negative') pi_neg2 = pi_neg*pi_neg pi_neg3 = pi_neg*pi_neg2 pi_neg4 = pi_neg*pi_neg3 pi_neg6 = pi_neg4*pi_neg2 a1 /= pi_neg a2 /= pi_neg2 a3 /= pi_neg3 a4 /= pi_neg4 a6 /= pi_neg6 verbose("Non-minimal equation, dividing out...\nNew model is %s"%([a1, a2, a3, a4, a6]), t, 1) return (C, p, val_disc, fp, KS, cp, split)
def eval(self, element, *args, **kwds): r''' Method to evaluate elements in the ring of differential polynomials. Since the differential polynomials have an intrinsic meaning (namely, the variables are related by derivation), evaluating a differential polynomial is a straight-forward computation once the objects for the ``*_0`` term is given. This method evaluates elements in ``self`` following that rule. INPUT: * ``element``: element (that must be in ``self``) to be evaluated * ``args``: list of arguments that will be linearly related with the generators of ``self`` (like they are given by ``self.gens()``) * ``kwds``: dictionary for providing values to the generators of ``self``. The name of the keys must be the names of the generators (as they can be got using the attribute ``_name``). We allow a mixed used of ``args`` and ``kwds`` but an error will be raised if * There are too many arguments in ``args``, * An input in ``kwds`` is not a valid name of a generator, * There are duplicate entries for a generator. OUTPUT: The resulting element after evaluating the variable `\alpha_n = \partial^n(\alpha)`, where `\alpha` is the name of the generator. EXAMPLES:: sage: from dalgebra.differential_polynomial.differential_polynomial_ring import * sage: R.<y> = DiffPolynomialRing(QQ['x']); x = R.base().gens()[0] sage: R.eval(y[1], 0) 0 sage: R.eval(y[0] + y[1], x) x + 1 sage: R.eval(y[0] + y[1], y=x) x + 1 This method commutes with the use of :func:`derivation`:: sage: R.eval(R.derivation(x^2*y[1]^2 - y[2]*y[1]), y=x) == R.derivation(R.eval(x^2*y[1]^2 - y[2]*y[1], y=x)) True This evaluation also works naturally with several infinite variables:: sage: S = DiffPolynomialRing(R, 'a'); a,y = S.gens() sage: S.eval(a[1] + y[0]*a[0], a=x, y=x^2) x^3 + 1 sage: in_eval = S.eval(a[1] + y[0]*a[0], y=x); in_eval a_1 + x*a_0 sage: parent(in_eval) Ring of differential polynomials in (a) over [Univariate Polynomial Ring in x over Rational Field] ''' if (not element in self): raise TypeError("Impossible to evaluate %s as an element of %s" % (element, self)) g = self.gens() final_input = {} names = [el._name for el in g] if (len(args) > self.ngens()): raise ValueError( "Too many argument for evaluation: given %d, expected (at most) %d" % (len(args), self.ngens())) for i in range(len(args)): final_input[g[i]] = args[i] for key in kwds: if (not key in names): raise TypeError("Invalid name for argument %s" % key) gen = g[names.index(key)] if (gen in final_input): raise TypeError("Duplicated value for generator %s" % gen) final_input[gen] = kwds[key] max_derivation = {gen: 0 for gen in final_input} for v in element.variables(): for gen in max_derivation: if (gen.contains(v)): max_derivation[gen] = max(max_derivation[gen], gen.index(v)) break to_evaluate = {} for gen in max_derivation: for i in range(max_derivation[gen] + 1): to_evaluate[str(gen[i])] = diff(final_input[gen], i) ## Computing the final ring values = list(final_input.values()) R = parent(values[0]) for i in range(1, len(values)): R = pushout(R, parent(values[i])) poly = element.polynomial() ## Adding all the non-appearing variables on element if (len(final_input) == len(g)): for v in poly.parent().gens(): if (v not in poly.variables()) and (not str(v) in to_evaluate): to_evaluate[str(v)] = 0 else: left_gens = [gen for gen in g if gen not in final_input] R = DiffPolynomialRing(R, [el._name for el in left_gens]) for v in poly.parent().gens(): if (not any(gen.contains(v) for gen in left_gens)) and (not str(v) in to_evaluate): to_evaluate[str(v)] = 0 return R(poly(**to_evaluate))
def jet(self, Intervals): base_ring = (Intervals if self.is_numeric else pushout(self.pt.parent(), Intervals)) Pol = PolynomialRing(base_ring, 'eta') return Pol([self.pt, 1]).truncate(self.jet_order)