def test_add_commutes(trials, verbose=False): r""" This is a simple demonstration of the :func:`random_testing` decorator and its recommended usage. We test that addition is commutative over rationals. EXAMPLES:: sage: from sage.misc.random_testing import test_add_commutes sage: test_add_commutes(2, verbose=True, seed=0) a == -4, b == 0 ... Passes! a == -1/2, b == -1/95 ... Passes! sage: test_add_commutes(10) sage: test_add_commutes(1000) # long time """ from sage.rings.all import QQ for _ in xrange(trials): a = QQ.random_element() b = QQ.random_element() if verbose: print "a == %s, b == %s ..." % (a, b) assert(a+b == b+a) if verbose: print "Passes!"
def is_possible_j(j, S=[]): r""" Tests if the rational `j` is a possible `j`-invariant of an elliptic curve with good reduction outside `S`. .. note:: The condition used is necessary but not sufficient unless S contains both 2 and 3. EXAMPLES:: sage: from sage.schemes.elliptic_curves.ell_egros import is_possible_j sage: is_possible_j(0,[]) False sage: is_possible_j(1728,[]) True sage: is_possible_j(-4096/11,[11]) True """ j = QQ(j) return (j.is_zero() and 3 in S) \ or (j==1728) \ or (j.is_S_integral(S) \ and j.prime_to_S_part(S).is_nth_power(3) \ and (j-1728).prime_to_S_part(S).abs().is_square())
def _element_constructor_(self, x): r""" Create an element in this group from ``x``. INPUT: - ``x`` -- a rational number TESTS:: sage: from sage.rings.valuation.value_group import DiscreteValueSemigroup sage: DiscreteValueSemigroup([])(0) 0 sage: DiscreteValueSemigroup([])(1) Traceback (most recent call last): ... ValueError: `1` is not in Trivial Additive Abelian Semigroup. sage: DiscreteValueSemigroup([1])(1) 1 sage: DiscreteValueSemigroup([1])(-1) Traceback (most recent call last): ... ValueError: `-1` is not in Additive Abelian Semigroup generated by 1. """ x = QQ.coerce(x) if x in self._generators or self._solve_linear_program(x) is not None: return x raise ValueError("`{0}` is not in {1}.".format(x,self))
def __add__(self, other): r""" Return the subgroup of `\QQ` generated by this group and ``other``. INPUT: - ``other`` -- a discrete value group or a rational number EXAMPLES:: sage: D = DiscreteValueGroup(1/2) sage: D + 1/3 DiscreteValueGroup(1/6) sage: D + D DiscreteValueGroup(1/2) sage: D + 1 DiscreteValueGroup(1/2) sage: DiscreteValueGroup(2/7) + DiscreteValueGroup(4/9) DiscreteValueGroup(2/63) """ if not isinstance(other, DiscreteValueGroup): from sage.structure.element import is_Element if is_Element(other) and QQ.has_coerce_map_from(other.parent()): return self + DiscreteValueGroup(other, category=self.category()) raise ValueError("`other` must be a DiscreteValueGroup or a rational number") if self.category() is not other.category(): raise ValueError("`other` must be in the same category") return DiscreteValueGroup(self._generator.gcd(other._generator), category=self.category())
def create_key(self, base, s): r""" Create a key which uniquely identifies a valuation. TESTS:: sage: 3*ZZ.valuation(2) is 2*(3/2*ZZ.valuation(2)) # indirect doctest True """ from sage.rings.all import infinity, QQ if s is infinity or s not in QQ or s <= 0: # for these values we can not return a TrivialValuation() in # create_object() because that would override that instance's # _factory_data and lead to pickling errors raise ValueError("s must be a positive rational") if base.is_trivial(): # for the same reason we can not accept trivial valuations here raise ValueError("base must not be trivial") s = QQ.coerce(s) if s == 1: # we would override the _factory_data of base if we just returned # it in create_object() so we just refuse to do so raise ValueError("s must not be 1") if isinstance(base, ScaledValuation_generic): return self.create_key(base._base_valuation, s*base._scale) return base, s
def b(tableau, star=0): r""" The column projection operator corresponding to the Young tableau ``tableau`` (which is supposed to contain every integer from `1` to its size precisely once, but may and may not be standard). This is the signed sum (in the group algebra of the relevant symmetric group over `\QQ`) of all the permutations which preserve the column of ``tableau`` (where the signs are the usual signs of the permutations). It is called `b_{\text{tableau}}` in [EtRT]_, Section 4.2. EXAMPLES:: sage: from sage.combinat.symmetric_group_algebra import b sage: b([[1,2]]) [1, 2] sage: b([[1],[2]]) [1, 2] - [2, 1] sage: b([]) [] sage: b([[1, 2, 4], [5, 3]]) [1, 2, 3, 4, 5] - [1, 3, 2, 4, 5] - [5, 2, 3, 4, 1] + [5, 3, 2, 4, 1] With the `l2r` setting for multiplication, the unnormalized Young symmetrizer ``e(tableau)`` should be the product ``b(tableau) * a(tableau)`` for every ``tableau``. Let us check this on the standard tableaux of size 5:: sage: from sage.combinat.symmetric_group_algebra import a, b, e sage: all( e(t) == b(t) * a(t) for t in StandardTableaux(5) ) True """ t = Tableau(tableau) if star: t = t.restrict(t.size()-star) cs = t.column_stabilizer().list() n = t.size() # This all should be over ZZ, not over QQ, but symmetric group # algebras don't seem to preserve coercion (the one over ZZ # doesn't coerce into the one over QQ even for the same n), # and the QQ version of this method is more important, so let # me stay with QQ. # TODO: Fix this. sgalg = SymmetricGroupAlgebra(QQ, n) one = QQ.one() P = permutation.Permutation # Ugly hack for the case of an empty tableau, due to the # annoyance of Permutation(Tableau([]).row_stabilizer()[0]) # being [1] rather than [] (which seems to have its origins in # permutation group code). # TODO: Fix this. if len(tableau) == 0: return sgalg.one() cd = dict((P(v), v.sign()*one) for v in cs) return sgalg._from_dict(cd)
def _element_constructor_(self, x): r""" Create an element from ``x``. INPUT: - ``x`` -- a rational number or `\infty` TESTS:: sage: from sage.rings.valuation.value_group import DiscreteValuationCodomain sage: DiscreteValuationCodomain()(0) 0 sage: DiscreteValuationCodomain()(infinity) +Infinity sage: DiscreteValuationCodomain()(-infinity) -Infinity """ if x is infinity: return x if x is -infinity: return x if x not in QQ: raise ValueError("must be a rational number or infinity") return QQ.coerce(x)
def _coerce_map_from_(self, S): r""" Coercion from a parent ``S``. There is a coercion from ``S`` if ``S`` has a coerce map to `\Q` or if `S = \Q/m\Z` for `m` a multiple of `n`. TESTS:: sage: G2 = QQ/(2*ZZ) sage: G3 = QQ/(3*ZZ) sage: G4 = QQ/(4*ZZ) sage: G2.has_coerce_map_from(QQ) True sage: G2.has_coerce_map_from(ZZ) True sage: G2.has_coerce_map_from(ZZ['x']) False sage: G2.has_coerce_map_from(G3) False sage: G2.has_coerce_map_from(G4) True sage: G4.has_coerce_map_from(G2) False """ if QQ.has_coerce_map_from(S): return True if isinstance(S, QmodnZ) and (S.n / self.n in ZZ): return True
def get_embedding(self,prec): r""" Returns an embedding of the quaternion algebra into the algebra of 2x2 matrices with coefficients in `\QQ_p`. INPUT: - prec -- Integer. The precision of the splitting. """ if self.F == QQ and self.discriminant == 1: R = Qp(self.p,prec) self._F_to_local = QQ.hom([R(1)]) def iota(q): return q.change_ring(R) self._prec = prec else: I,J,K = self.local_splitting(prec) mats = [1,I,J,K] def iota(q): R=I.parent() try: q = q.coefficient_tuple() except AttributeError: q = q.list() return sum(self._F_to_local(a)*b for a,b in zip(q,mats)) return iota
def random_element(self): r""" Return a random element of `\Q/n\Z`. The denominator is selected using the ``1/n`` distribution on integers, modified to return a positive value. The numerator is then selected uniformly. EXAMPLES:: sage: G = QQ/(6*ZZ) sage: G.random_element() 47/16 sage: G.random_element() 1 sage: G.random_element() 3/5 """ if self.n == 0: return self(QQ.random_element()) d = ZZ.random_element() if d >= 0: d = 2 * d + 1 else: d = -2 * d n = ZZ.random_element((self.n * d).ceil()) return self(n / d)
def _element_constructor_(self, x): r""" Create an element in this group from ``x``. INPUT: - ``x`` -- a rational number TESTS:: sage: from sage.rings.valuation.value_group import DiscreteValueGroup sage: DiscreteValueGroup(0)(0) 0 sage: DiscreteValueGroup(0)(1) Traceback (most recent call last): ... ValueError: `1` is not in Trivial Additive Abelian Group. sage: DiscreteValueGroup(1)(1) 1 sage: DiscreteValueGroup(1)(1/2) Traceback (most recent call last): ... ValueError: `1/2` is not in Additive Abelian Group generated by 1. """ x = QQ.coerce(x) if x == 0 or (self._generator != 0 and x/self._generator in ZZ): return x raise ValueError("`{0}` is not in {1}.".format(x,self))
def __add__(self, other): r""" Return the subsemigroup of `\QQ` generated by this semigroup and ``other``. INPUT: - ``other`` -- a discrete value (semi-)group or a rational number EXAMPLES:: sage: from sage.rings.valuation.value_group import DiscreteValueSemigroup, DiscreteValueGroup sage: D = DiscreteValueSemigroup(1/2) sage: D + 1/3 Additive Abelian Semigroup generated by 1/3, 1/2 sage: D + D Additive Abelian Semigroup generated by 1/2 sage: D + 1 Additive Abelian Semigroup generated by 1/2 sage: DiscreteValueGroup(2/7) + DiscreteValueSemigroup(4/9) Additive Abelian Semigroup generated by -2/7, 2/7, 4/9 """ if isinstance(other, DiscreteValueSemigroup): return DiscreteValueSemigroup(self._generators + other._generators) if isinstance(other, DiscreteValueGroup): return DiscreteValueSemigroup(self._generators + (other._generator, -other._generator)) from sage.structure.element import is_Element if is_Element(other) and QQ.has_coerce_map_from(other.parent()): return self + DiscreteValueSemigroup(other) raise ValueError("`other` must be a DiscreteValueGroup, a DiscreteValueSemigroup or a rational number")
def a(tableau, star=0): r""" The row projection operator corresponding to the Young tableau ``tableau`` (which is supposed to contain every integer from `1` to its size precisely once, but may and may not be standard). This is the sum (in the group algebra of the relevant symmetric group over `\QQ`) of all the permutations which preserve the rows of ``tableau``. It is called `a_{\text{tableau}}` in [EtRT]_, Section 4.2. REFERENCES: .. [EtRT] Pavel Etingof, Oleg Golberg, Sebastian Hensel, Tiankai Liu, Alex Schwendner, Dmitry Vaintrob, Elena Yudovina, "Introduction to representation theory", :arXiv:`0901.0827v5`. EXAMPLES:: sage: from sage.combinat.symmetric_group_algebra import a sage: a([[1,2]]) [1, 2] + [2, 1] sage: a([[1],[2]]) [1, 2] sage: a([]) [] sage: a([[1, 5], [2, 3], [4]]) [1, 2, 3, 4, 5] + [1, 3, 2, 4, 5] + [5, 2, 3, 4, 1] + [5, 3, 2, 4, 1] """ t = Tableau(tableau) if star: t = t.restrict(t.size()-star) rs = t.row_stabilizer().list() n = t.size() # This all should be over ZZ, not over QQ, but symmetric group # algebras don't seem to preserve coercion (the one over ZZ # doesn't coerce into the one over QQ even for the same n), # and the QQ version of this method is more important, so let # me stay with QQ. # TODO: Fix this. sgalg = SymmetricGroupAlgebra(QQ, n) one = QQ.one() P = permutation.Permutation # Ugly hack for the case of an empty tableau, due to the # annoyance of Permutation(Tableau([]).row_stabilizer()[0]) # being [1] rather than [] (which seems to have its origins in # permutation group code). # TODO: Fix this. if len(tableau) == 0: return sgalg.one() rd = dict((P(h), one) for h in rs) return sgalg._from_dict(rd)
def some_elements(self): """ Return some elements, for use in testing. TESTS:: sage: L = (QQ/ZZ).some_elements() sage: len(L) 92 """ return list(set(self(x) for x in QQ.some_elements()))
def some_elements(self): r""" Return some typical elements in this group. EXAMPLES:: sage: from sage.rings.valuation.value_group import DiscreteValueGroup sage: DiscreteValueGroup(-3/8).some_elements() [3/8, -3/8, 0, 42, 3/2, -3/2, 9/8, -9/8] """ return [self._generator, -self._generator] + [x for x in QQ.some_elements() if x in self]
def coeff(p, q): ret = QQ.one() last = 0 for val in p: count = 0 s = 0 while s != val: s += q[last+count] count += 1 ret /= factorial(count) last += count return ret
def rotation_matrix_angle(r, check=False): r""" Return the angle of the rotation matrix ``r`` divided by ``2 pi``. EXAMPLES:: sage: from flatsurf.geometry.matrix_2x2 import rotation_matrix_angle sage: def rot_matrix(p, q): ....: z = QQbar.zeta(q) ** p ....: c = z.real() ....: s = z.imag() ....: return matrix(AA, 2, [c,-s,s,c]) sage: [rotation_matrix_angle(rot_matrix(i, 5)) for i in range(1,5)] [1/5, 2/5, 3/5, 4/5] sage: [rotation_matrix_angle(rot_matrix(i,7)) for i in range(1,7)] [1/7, 2/7, 3/7, 4/7, 5/7, 6/7] Some random tests:: sage: for _ in range(100): ....: r = QQ.random_element(x=0,y=500) ....: r -= r.floor() ....: m = rot_matrix(r.numerator(), r.denominator()) ....: assert rotation_matrix_angle(m) == r .. NOTE:: This is using floating point arithmetic and might be wrong. """ e0,e1 = r.change_ring(CDF).eigenvalues() m0 = (e0.log() / 2 / CDF.pi()).imag() m1 = (e1.log() / 2 / CDF.pi()).imag() r0 = RR(m0).nearby_rational(max_denominator=10000) r1 = RR(m1).nearby_rational(max_denominator=10000) if r0 != -r1: raise RuntimeError r0 = r0.abs() if r[0][1] > 0: return QQ.one() - r0 else: return r0 if check: e = r.change_ring(AA).eigenvalues()[0] if e.minpoly() != ZZ['x'].cyclotomic_polynomial()(r.denominator()): raise RuntimeError z = QQbar.zeta(r.denominator()) if z**r.numerator() != e: raise RuntimeError return r
def coeff(p, q): ret = QQ.one() last = 0 for val in p: count = 0 s = 0 while s != val: s += q[last+count] count += 1 ret /= count last += count if (len(q) - len(p)) % 2 == 1: ret = -ret return ret
def __classcall__(cls, generator): r""" Normalizes ``generator`` to a positive rational so that this is a unique parent. TESTS:: sage: from sage.rings.valuation.value_group import DiscreteValueGroup sage: DiscreteValueGroup(1) is DiscreteValueGroup(-1) True """ generator = QQ.coerce(generator) generator = generator.abs() return super(DiscreteValueGroup, cls).__classcall__(cls, generator)
def ConstantFormsSpaceFunctor(group): r""" Construction functor for the space of constant forms. When determining a common parent between a ring and a forms ring or space this functor is first applied to the ring. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.functors import (ConstantFormsSpaceFunctor, FormsSpaceFunctor) sage: ConstantFormsSpaceFunctor(4) == FormsSpaceFunctor("holo", 4, 0, 1) True sage: ConstantFormsSpaceFunctor(4) ModularFormsFunctor(n=4, k=0, ep=1) """ return FormsSpaceFunctor("holo", group, QQ.zero(), ZZ.one())
def _compute_padic_splitting(self,prec): verbose('Entering compute_padic_splitting') prime = self.p if self.seed is not None: self.magma.eval('SetSeed(%s)'%self.seed) R = Qp(prime,prec+10) #Zmod(prime**prec) # B_magma = self.Gn._B_magma a,b = self.Gn.B.invariants() if self._matrix_group: self._II = matrix(R,2,2,[1,0,0,-1]) self._JJ = matrix(R,2,2,[0,1,1,0]) goodroot = self.F.gen().minpoly().change_ring(R).roots()[0][0] self._F_to_local = self.F.hom([goodroot]) else: verbose('Calling magma pMatrixRing') if self.F == QQ: _,f = self.magma.pMatrixRing(self.Gn._O_magma,prime*self.Gn._O_magma.BaseRing(),Precision = 20,nvals = 2) self._F_to_local = QQ.hom([R(1)]) else: _,f = self.magma.pMatrixRing(self.Gn._O_magma,sage_F_ideal_to_magma(self.Gn._F_magma,self.ideal_p),Precision = 20,nvals = 2) try: self._goodroot = R(f.Image(B_magma(B_magma.BaseRing().gen(1))).Vector()[1]._sage_()) except SyntaxError: raise SyntaxError("Magma has trouble finding local splitting") self._F_to_local = None for o,_ in self.F.gen().minpoly().change_ring(R).roots(): if (o - self._goodroot).valuation() > 5: self._F_to_local = self.F.hom([o]) break assert self._F_to_local is not None verbose('Initializing II,JJ,KK') v = f.Image(B_magma.gen(1)).Vector() self._II = matrix(R,2,2,[v[i+1]._sage_() for i in xrange(4)]) v = f.Image(B_magma.gen(2)).Vector() self._JJ = matrix(R,2,2,[v[i+1]._sage_() for i in xrange(4)]) v = f.Image(B_magma.gen(3)).Vector() self._KK = matrix(R,2,2,[v[i+1]._sage_() for i in xrange(4)]) self._II , self._JJ = lift_padic_splitting(self._F_to_local(a),self._F_to_local(b),self._II,self._JJ,prime,prec) self.Gn._F_to_local = self._F_to_local if not self.use_shapiro(): self.Gpn._F_to_local = self._F_to_local self._KK = self._II * self._JJ self._prec = prec return self._II, self._JJ, self._KK
def some_elements(self): r""" Return some typical elements in this semigroup. EXAMPLES:: sage: from sage.rings.valuation.value_group import DiscreteValueSemigroup sage: list(DiscreteValueSemigroup([-3/8,1/2]).some_elements()) [0, -3/8, 1/2, ...] """ yield self(0) if self.is_trivial(): return for g in self._generators: yield g from sage.rings.all import ZZ for x in (ZZ**len(self._generators)).some_elements(): yield QQ.coerce(sum([abs(c)*g for c,g in zip(x,self._generators)]))
def __classcall__(cls, generators): r""" Normalize ``generators``. TESTS: We do not find minimal generators or something like that but just sort the generators and drop generators that are trivially contained in the semigroup generated by the remaining generators:: sage: from sage.rings.valuation.value_group import DiscreteValueSemigroup sage: DiscreteValueSemigroup([1,2]) is DiscreteValueSemigroup([1]) True In this case, the normalization is not sufficient to determine that these are the same semigroup:: sage: DiscreteValueSemigroup([1,-1,1/3]) is DiscreteValueSemigroup([1/3,-1/3]) False """ if generators in QQ: generators = [generators] generators = list(set([QQ.coerce(g) for g in generators if g != 0])) generators.sort() simplified_generators = generators # this is not very efficient but there should never be more than a # couple of generators for g in generators: for h in generators: if g == h: continue from sage.rings.all import NN if h/g in NN: simplified_generators.remove(h) break return super(DiscreteValueSemigroup, cls).__classcall__(cls, tuple(simplified_generators))
def _mul_(self, other, switch_sides=False): r""" Return the semigroup generated by ``other`` times the generators of this semigroup. INPUT: - ``other`` -- a rational number EXAMPLES:: sage: from sage.rings.valuation.value_group import DiscreteValueSemigroup sage: D = DiscreteValueSemigroup(1/2) sage: 1/2 * D Additive Abelian Semigroup generated by 1/4 sage: D * (1/2) Additive Abelian Semigroup generated by 1/4 sage: D * 0 Trivial Additive Abelian Semigroup """ other = QQ.coerce(other) return DiscreteValueSemigroup([g*other for g in self._generators])
def __init__(self, a, b=None, parent=None, check=True): r""" Create the cusp a/b in `\mathbb{P}^1(\QQ)`, where if b=0 this is the cusp at infinity. When present, b must either be Infinity or coercible to an Integer. EXAMPLES:: sage: Cusp(2,3) 2/3 sage: Cusp(3,6) 1/2 sage: Cusp(1,0) Infinity sage: Cusp(infinity) Infinity sage: Cusp(5) 5 sage: Cusp(1/2) 1/2 sage: Cusp(1.5) 3/2 sage: Cusp(int(7)) 7 sage: Cusp(1, 2, check=False) 1/2 sage: Cusp('sage', 2.5, check=False) # don't do this! sage/2.50000000000000 :: sage: I**2 -1 sage: Cusp(I) Traceback (most recent call last): ... TypeError: unable to convert I to a cusp :: sage: a = Cusp(2,3) sage: loads(a.dumps()) == a True :: sage: Cusp(1/3,0) Infinity sage: Cusp((1,0)) Infinity TESTS:: sage: Cusp("1/3", 5) 1/15 sage: Cusp(Cusp(3/5), 7) 3/35 sage: Cusp(5/3, 0) Infinity sage: Cusp(3,oo) 0 sage: Cusp((7,3), 5) 7/15 sage: Cusp(int(5), 7) 5/7 :: sage: Cusp(0,0) Traceback (most recent call last): ... TypeError: unable to convert (0, 0) to a cusp :: sage: Cusp(oo,oo) Traceback (most recent call last): ... TypeError: unable to convert (+Infinity, +Infinity) to a cusp :: sage: Cusp(Cusp(oo),oo) Traceback (most recent call last): ... TypeError: unable to convert (Infinity, +Infinity) to a cusp """ if parent is None: parent = Cusps Element.__init__(self, parent) if not check: self.__a = a self.__b = b return if b is None: if isinstance(a, Integer): self.__a = a self.__b = ZZ.one() elif isinstance(a, Rational): self.__a = a.numer() self.__b = a.denom() elif is_InfinityElement(a): self.__a = ZZ.one() self.__b = ZZ.zero() elif isinstance(a, Cusp): self.__a = a.__a self.__b = a.__b elif isinstance(a, (int, long)): self.__a = ZZ(a) self.__b = ZZ.one() elif isinstance(a, (tuple, list)): if len(a) != 2: raise TypeError("unable to convert %r to a cusp" % a) if ZZ(a[1]) == 0: self.__a = ZZ.one() self.__b = ZZ.zero() return try: r = QQ((a[0], a[1])) self.__a = r.numer() self.__b = r.denom() except (ValueError, TypeError): raise TypeError("unable to convert %r to a cusp" % a) else: try: r = QQ(a) self.__a = r.numer() self.__b = r.denom() except (ValueError, TypeError): raise TypeError("unable to convert %r to a cusp" % a) return if is_InfinityElement(b): if is_InfinityElement(a) or (isinstance(a, Cusp) and a.is_infinity()): raise TypeError("unable to convert (%r, %r) to a cusp" % (a, b)) self.__a = ZZ.zero() self.__b = ZZ.one() return elif not b: if not a: raise TypeError("unable to convert (%r, %r) to a cusp" % (a, b)) self.__a = ZZ.one() self.__b = ZZ.zero() return if isinstance(a, Integer) or isinstance(a, Rational): r = a / ZZ(b) elif is_InfinityElement(a): self.__a = ZZ.one() self.__b = ZZ.zero() return elif isinstance(a, Cusp): if a.__b: r = a.__a / (a.__b * b) else: self.__a = ZZ.one() self.__b = ZZ.zero() return elif isinstance(a, (int, long)): r = ZZ(a) / b elif isinstance(a, (tuple, list)): if len(a) != 2: raise TypeError("unable to convert (%r, %r) to a cusp" % (a, b)) r = ZZ(a[0]) / (ZZ(a[1]) * b) else: try: r = QQ(a) / b except (ValueError, TypeError): raise TypeError("unable to convert (%r, %r) to a cusp" % (a, b)) self.__a = r.numer() self.__b = r.denom()
def coordinate_vector(self, v): r""" Return the coordinate vector of ``v`` with respect to the basis ``self.gens()``. INPUT: - ``v`` -- An element of ``self``. OUTPUT: An element of ``self.module()``, namely the corresponding coordinate vector of ``v`` with respect to the basis ``self.gens()``. The module is the free module over the coefficient ring of ``self`` with the dimension of ``self``. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.space import QuasiCuspForms sage: MF = QuasiCuspForms(n=6, k=20, ep=1) sage: MF.dimension() 12 sage: el = MF(MF.E4()^2*MF.Delta() + MF.E4()*MF.E2()^2*MF.Delta()) sage: el 2*q + 120*q^2 + 3402*q^3 + 61520*q^4 + O(q^5) sage: vec = el.coordinate_vector() # long time sage: vec # long time (1, 13/(18*d), 103/(432*d^2), 0, 0, 1, 1/(2*d), 0, 0, 0, 0, 0) sage: vec.parent() # long time Vector space of dimension 12 over Fraction Field of Univariate Polynomial Ring in d over Integer Ring sage: vec.parent() == MF.module() # long time True sage: el == MF(sum([vec[l]*MF.gen(l) for l in range(0,12)])) # long time True sage: el == MF.element_from_coordinates(vec) # long time True sage: MF.gen(1).coordinate_vector() == vector([0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) # long time True sage: MF = QuasiCuspForms(n=infinity, k=10, ep=-1) sage: el2 = MF(MF.E4()*MF.f_inf()*(MF.f_i() - MF.E2())) sage: el2.coordinate_vector() (1, -1) sage: el2 == MF.element_from_coordinates(el2.coordinate_vector()) True """ (x,y,z,d) = self.pol_ring().gens() k = self._weight rmax = QQ(k / ZZ(2)).floor() partlist = v.rat().numerator().polynomial(z).list() denom = self.coeff_ring()(v.rat().denominator()) partlist = [part/denom for part in partlist] parts = partlist + [0]*(rmax + 1 - len(partlist)) E2 = self.E2() coord_vector = [] for r in range(ZZ(0), rmax + 1): gens = [v/E2**r for v in self.quasi_part_gens(r)] if len(gens) > 0: ambient_space = self.graded_ring().reduce_type("cusp", degree=(gens[0].weight(), gens[0].ep())) subspace = ambient_space.subspace(gens) vector_part_in_subspace = subspace(parts[r]) coord_part = [v for v in vector_part_in_subspace.coordinate_vector() ] coord_vector += coord_part return self._module(vector(self.coeff_ring(), coord_vector))
def test_add_is_mul(trials, verbose=False): r""" This example demonstrates a failing :func:`random_testing` test, and shows how to reproduce the error. DO NOT USE THIS AS AN EXAMPLE OF HOW TO USE :func:`random_testing`! Instead, look at :func:`sage.misc.random_testing.test_add_commutes`. We test that ``a+b == a*b``, for *a*, *b* rational. This is of course false, so the test will almost always fail. EXAMPLES:: sage: from sage.misc.random_testing import test_add_is_mul We start by testing that we get reproducible results when setting *seed* to 0. :: sage: test_add_is_mul(2, verbose=True, seed=0) a == -4, b == 0 ... Random testing has revealed a problem in test_add_is_mul Please report this bug! You may be the first person in the world to have seen this problem. Please include this random seed in your bug report: Random seed: 0 AssertionError() Normally in a ``@random_testing`` doctest, we would leave off the ``verbose=True`` and the ``# random``. We put it in here so that we can verify that we are seeing the exact same error when we reproduce the error below. :: sage: test_add_is_mul(10, verbose=True) # random a == -2/7, b == 1 ... Random testing has revealed a problem in test_add_is_mul Please report this bug! You may be the first person in the world to have seen this problem. Please include this random seed in your bug report: Random seed: 216390410596009428782506007128692114173 AssertionError() OK, now assume that some user has reported a :func:`test_add_is_mul` failure. We can specify the same *random_seed* that was found in the bug report, and we will get the exact same failure so that we can debug the "problem". :: sage: test_add_is_mul(10, verbose=True, seed=216390410596009428782506007128692114173) a == -2/7, b == 1 ... Random testing has revealed a problem in test_add_is_mul Please report this bug! You may be the first person in the world to have seen this problem. Please include this random seed in your bug report: Random seed: 216390410596009428782506007128692114173 AssertionError() """ from sage.rings.all import QQ for _ in xrange(trials): a = QQ.random_element() b = QQ.random_element() if verbose: print "a == %s, b == %s ..." % (a, b) assert(a+b == a*b) if verbose: print "Passes!"
def to_sage(self): """ EXAMPLES:: sage: macaulay2(ZZ).to_sage() # optional - macaulay2 Integer Ring sage: macaulay2(QQ).to_sage() # optional - macaulay2 Rational Field sage: macaulay2(2).to_sage() # optional - macaulay2 2 sage: macaulay2(1/2).to_sage() # optional - macaulay2 1/2 sage: macaulay2(2/1).to_sage() # optional - macaulay2 2 sage: _.parent() # optional - macaulay2 Rational Field sage: macaulay2([1,2,3]).to_sage() # optional - macaulay2 [1, 2, 3] sage: m = matrix([[1,2],[3,4]]) sage: macaulay2(m).to_sage() # optional - macaulay2 [1 2] [3 4] sage: macaulay2(QQ['x,y']).to_sage() # optional - macaulay2 Multivariate Polynomial Ring in x, y over Rational Field sage: macaulay2(QQ['x']).to_sage() # optional - macaulay2 Univariate Polynomial Ring in x over Rational Field sage: macaulay2(GF(7)['x,y']).to_sage() # optional - macaulay2 Multivariate Polynomial Ring in x, y over Finite Field of size 7 sage: macaulay2(GF(7)).to_sage() # optional - macaulay2 Finite Field of size 7 sage: macaulay2(GF(49, 'a')).to_sage() # optional - macaulay2 Finite Field in a of size 7^2 sage: R.<x,y> = QQ[] sage: macaulay2(x^2+y^2+1).to_sage() # optional - macaulay2 x^2 + y^2 + 1 sage: R = macaulay2("QQ[x,y]") # optional - macaulay2 sage: I = macaulay2("ideal (x,y)") # optional - macaulay2 sage: I.to_sage() # optional - macaulay2 Ideal (x, y) of Multivariate Polynomial Ring in x, y over Rational Field sage: X = R/I # optional - macaulay2 sage: X.to_sage() # optional - macaulay2 Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x, y) sage: R = macaulay2("QQ^2") # optional - macaulay2 sage: R.to_sage() # optional - macaulay2 Vector space of dimension 2 over Rational Field sage: m = macaulay2('"hello"') # optional - macaulay2 sage: m.to_sage() # optional - macaulay2 'hello' """ repr_str = str(self) cls_str = str(self.cls()) cls_cls_str = str(self.cls().cls()) if repr_str == "ZZ": from sage.rings.all import ZZ return ZZ elif repr_str == "QQ": from sage.rings.all import QQ return QQ if cls_cls_str == "Type": if cls_str == "List": return [entry.to_sage() for entry in self] elif cls_str == "Matrix": from sage.matrix.all import matrix base_ring = self.ring().to_sage() entries = self.entries().to_sage() return matrix(base_ring, entries) elif cls_str == "Ideal": parent = self.ring().to_sage() gens = self.gens().entries().flatten().to_sage() return parent.ideal(*gens) elif cls_str == "QuotientRing": #Handle the ZZ/n case if "ZZ" in repr_str and "--" in repr_str: from sage.rings.all import ZZ, GF external_string = self.external_string() zz, n = external_string.split("/") #Note that n must be prime since it is #coming from Macaulay 2 return GF(ZZ(n)) ambient = self.ambient().to_sage() ideal = self.ideal().to_sage() return ambient.quotient(ideal) elif cls_str == "PolynomialRing": from sage.rings.all import PolynomialRing from sage.rings.polynomial.term_order import inv_macaulay2_name_mapping #Get the base ring base_ring = self.coefficientRing().to_sage() #Get a string list of generators gens = str(self.gens())[1:-1] # Check that we are dealing with default degrees, i.e. 1's. if self.degrees().any("x -> x != {1}").to_sage(): raise ValueError("cannot convert Macaulay2 polynomial ring with non-default degrees to Sage") #Handle the term order external_string = self.external_string() order = None if "MonomialOrder" not in external_string: order = "degrevlex" else: for order_name in inv_macaulay2_name_mapping: if order_name in external_string: order = inv_macaulay2_name_mapping[order_name] if len(gens) > 1 and order is None: raise ValueError("cannot convert Macaulay2's term order to a Sage term order") return PolynomialRing(base_ring, order=order, names=gens) elif cls_str == "GaloisField": from sage.rings.all import ZZ, GF gf, n = repr_str.split(" ") n = ZZ(n) if n.is_prime(): return GF(n) else: gen = str(self.gens())[1:-1] return GF(n, gen) elif cls_str == "Boolean": if repr_str == "true": return True elif repr_str == "false": return False elif cls_str == "String": return str(repr_str) elif cls_str == "Module": from sage.modules.all import FreeModule if self.isFreeModule().to_sage(): ring = self.ring().to_sage() rank = self.rank().to_sage() return FreeModule(ring, rank) else: #Handle the integers and rationals separately if cls_str == "ZZ": from sage.rings.all import ZZ return ZZ(repr_str) elif cls_str == "QQ": from sage.rings.all import QQ repr_str = self.external_string() if "/" not in repr_str: repr_str = repr_str + "/1" return QQ(repr_str) m2_parent = self.cls() parent = m2_parent.to_sage() if cls_cls_str == "PolynomialRing": from sage.misc.sage_eval import sage_eval gens_dict = parent.gens_dict() return sage_eval(self.external_string(), gens_dict) from sage.misc.sage_eval import sage_eval try: return sage_eval(repr_str) except Exception: raise NotImplementedError("cannot convert %s to a Sage object"%repr_str)
def __init__(self, a, b=None, parent=None, check=True): r""" Create the cusp a/b in `\mathbb{P}^1(\QQ)`, where if b=0 this is the cusp at infinity. When present, b must either be Infinity or coercible to an Integer. EXAMPLES:: sage: Cusp(2,3) 2/3 sage: Cusp(3,6) 1/2 sage: Cusp(1,0) Infinity sage: Cusp(infinity) Infinity sage: Cusp(5) 5 sage: Cusp(1/2) 1/2 sage: Cusp(1.5) 3/2 sage: Cusp(int(7)) 7 sage: Cusp(1, 2, check=False) 1/2 sage: Cusp('sage', 2.5, check=False) # don't do this! sage/2.50000000000000 :: sage: I**2 -1 sage: Cusp(I) Traceback (most recent call last): ... TypeError: Unable to convert I to a Cusp :: sage: a = Cusp(2,3) sage: loads(a.dumps()) == a True :: sage: Cusp(1/3,0) Infinity sage: Cusp((1,0)) Infinity TESTS:: sage: Cusp("1/3", 5) 1/15 sage: Cusp(Cusp(3/5), 7) 3/35 sage: Cusp(5/3, 0) Infinity sage: Cusp(3,oo) 0 sage: Cusp((7,3), 5) 7/15 sage: Cusp(int(5), 7) 5/7 :: sage: Cusp(0,0) Traceback (most recent call last): ... TypeError: Unable to convert (0, 0) to a Cusp :: sage: Cusp(oo,oo) Traceback (most recent call last): ... TypeError: Unable to convert (+Infinity, +Infinity) to a Cusp :: sage: Cusp(Cusp(oo),oo) Traceback (most recent call last): ... TypeError: Unable to convert (Infinity, +Infinity) to a Cusp """ if parent is None: parent = Cusps Element.__init__(self, parent) if not check: self.__a = a; self.__b = b return if b is None: if isinstance(a, Integer): self.__a = a self.__b = ZZ(1) elif isinstance(a, Rational): self.__a = a.numer() self.__b = a.denom() elif is_InfinityElement(a): self.__a = ZZ(1) self.__b = ZZ(0) elif isinstance(a, Cusp): self.__a = a.__a self.__b = a.__b elif isinstance(a, (int, long)): self.__a = ZZ(a) self.__b = ZZ(1) elif isinstance(a, (tuple, list)): if len(a) != 2: raise TypeError, "Unable to convert %s to a Cusp"%a if ZZ(a[1]) == 0: self.__a = ZZ(1) self.__b = ZZ(0) return try: r = QQ((a[0], a[1])) self.__a = r.numer() self.__b = r.denom() except (ValueError, TypeError): raise TypeError, "Unable to convert %s to a Cusp"%a else: try: r = QQ(a) self.__a = r.numer() self.__b = r.denom() except (ValueError, TypeError): raise TypeError, "Unable to convert %s to a Cusp"%a return if is_InfinityElement(b): if is_InfinityElement(a) or (isinstance(a, Cusp) and a.is_infinity()): raise TypeError, "Unable to convert (%s, %s) to a Cusp"%(a, b) self.__a = ZZ(0) self.__b = ZZ(1) return elif not b: if not a: raise TypeError, "Unable to convert (%s, %s) to a Cusp"%(a, b) self.__a = ZZ(1) self.__b = ZZ(0) return if isinstance(a, Integer) or isinstance(a, Rational): r = a / ZZ(b) elif is_InfinityElement(a): self.__a = ZZ(1) self.__b = ZZ(0) return elif isinstance(a, Cusp): if a.__b: r = a.__a / (a.__b * b) else: self.__a = ZZ(1) self.__b = ZZ(0) return elif isinstance(a, (int, long)): r = ZZ(a) / b elif isinstance(a, (tuple, list)): if len(a) != 2: raise TypeError, "Unable to convert (%s, %s) to a Cusp"%(a, b) r = ZZ(a[0]) / (ZZ(a[1]) * b) else: try: r = QQ(a) / b except (ValueError, TypeError): raise TypeError, "Unable to convert (%s, %s) to a Cusp"%(a, b) self.__a = r.numer() self.__b = r.denom()
def normal_cone(self): r""" Return the (closure of the) normal cone of the triangulation. Recall that a regular triangulation is one that equals the "crease lines" of a convex piecewise-linear function. This support function is not unique, for example, you can scale it by a positive constant. The set of all piecewise-linear functions with fixed creases forms an open cone. This cone can be interpreted as the cone of normal vectors at a point of the secondary polytope, which is why we call it normal cone. See [GKZ1994]_ Section 7.1 for details. OUTPUT: The closure of the normal cone. The `i`-th entry equals the value of the piecewise-linear function at the `i`-th point of the configuration. For an irregular triangulation, the normal cone is empty. In this case, a single point (the origin) is returned. EXAMPLES:: sage: triangulation = polytopes.hypercube(2).triangulate(engine='internal') sage: triangulation (<0,1,3>, <1,2,3>) sage: N = triangulation.normal_cone(); N 4-d cone in 4-d lattice sage: N.rays() ( 0, 0, 0, -1), ( 0, 0, 1, 1), ( 0, 0, -1, -1), ( 1, 0, 0, 1), (-1, 0, 0, -1), ( 0, 1, 0, -1), ( 0, -1, 0, 1) in Ambient free module of rank 4 over the principal ideal domain Integer Ring sage: N.dual().rays() (1, -1, 1, -1) in Ambient free module of rank 4 over the principal ideal domain Integer Ring TESTS:: sage: polytopes.simplex(2).triangulate().normal_cone() 3-d cone in 3-d lattice sage: _.dual().is_trivial() True """ if not self.point_configuration().base_ring().is_subring(QQ): raise NotImplementedError( 'Only base rings ZZ and QQ are supported') from ppl import Constraint_System, Linear_Expression, C_Polyhedron from sage.matrix.constructor import matrix from sage.arith.all import lcm pc = self.point_configuration() cs = Constraint_System() for facet in self.interior_facets(): s0, s1 = self._boundary_simplex_dictionary()[facet] p = set(s0).difference(facet).pop() q = set(s1).difference(facet).pop() origin = pc.point(p).reduced_affine_vector() base_indices = [i for i in s0 if i != p] base = matrix([ pc.point(i).reduced_affine_vector() - origin for i in base_indices ]) sol = base.solve_left(pc.point(q).reduced_affine_vector() - origin) relation = [0] * pc.n_points() relation[p] = sum(sol) - 1 relation[q] = 1 for i, base_i in enumerate(base_indices): relation[base_i] = -sol[i] rel_denom = lcm([QQ(r).denominator() for r in relation]) relation = [ZZ(r * rel_denom) for r in relation] ex = Linear_Expression(relation, 0) cs.insert(ex >= 0) from sage.modules.free_module import FreeModule ambient = FreeModule(ZZ, self.point_configuration().n_points()) if cs.empty(): cone = C_Polyhedron(ambient.dimension(), 'universe') else: cone = C_Polyhedron(cs) from sage.geometry.cone import _Cone_from_PPL return _Cone_from_PPL(cone, lattice=ambient)
def lift_map(target): """ Create a lift map, to be used for lifting the cross ratios of a matroid representation. .. SEEALSO:: :meth:`lift_cross_ratios() <sage.matroids.utilities.lift_cross_ratios>` INPUT: - ``target`` -- a string describing the target (partial) field. OUTPUT: - a dictionary Depending on the value of ``target``, the following lift maps will be created: - "reg": a lift map from `\GF3` to the regular partial field `(\ZZ, <-1>)`. - "sru": a lift map from `\GF7` to the sixth-root-of-unity partial field `(\QQ(z), <z>)`, where `z` is a sixth root of unity. The map sends 3 to `z`. - "dyadic": a lift map from `\GF{11}` to the dyadic partial field `(\QQ, <-1, 2>)`. - "gm": a lift map from `\GF{19}` to the golden mean partial field `(\QQ(t), <-1,t>)`, where `t` is a root of `t^2-t-1`. The map sends `5` to `t`. The example below shows that the latter map satisfies three necessary conditions stated in :meth:`lift_cross_ratios() <sage.matroids.utilities.lift_cross_ratios>` EXAMPLES:: sage: from sage.matroids.utilities import lift_map sage: lm = lift_map('gm') sage: for x in lm: ....: if (x == 1) is not (lm[x] == 1): ....: print 'not a proper lift map' ....: for y in lm: ....: if (x+y == 0) and not (lm[x]+lm[y] == 0): ....: print 'not a proper lift map' ....: if (x+y == 1) and not (lm[x]+lm[y] == 1): ....: print 'not a proper lift map' ....: for z in lm: ....: if (x*y==z) and not (lm[x]*lm[y]==lm[z]): ....: print 'not a proper lift map' """ if target == "reg": R = GF(3) return {R(1): ZZ(1)} if target == "sru": R = GF(7) z = ZZ['z'].gen() S = NumberField(z * z - z + 1, 'z') return {R(1): S(1), R(3): S(z), R(3)**(-1): S(z)**5} if target == "dyadic": R = GF(11) return {R(1): QQ(1), R(-1): QQ(-1), R(2): QQ(2), R(6): QQ(1 / 2)} if target == "gm": R = GF(19) t = QQ['t'].gen() G = NumberField(t * t - t - 1, 't') return { R(1): G(1), R(5): G(t), R(1) / R(5): G(1) / G(t), R(-5): G(-t), R(-5)**(-1): G(-t)**(-1), R(5)**2: G(t)**2, R(5)**(-2): G(t)**(-2) } raise NotImplementedError(target)
def Polyhedron(vertices=None, rays=None, lines=None, ieqs=None, eqns=None, ambient_dim=None, base_ring=None, minimize=True, verbose=False, backend=None): """ Construct a polyhedron object. You may either define it with vertex/ray/line or inequalities/equations data, but not both. Redundant data will automatically be removed (unless ``minimize=False``), and the complementary representation will be computed. INPUT: - ``vertices`` -- list of point. Each point can be specified as any iterable container of ``base_ring`` elements. If ``rays`` or ``lines`` are specified but no ``vertices``, the origin is taken to be the single vertex. - ``rays`` -- list of rays. Each ray can be specified as any iterable container of ``base_ring`` elements. - ``lines`` -- list of lines. Each line can be specified as any iterable container of ``base_ring`` elements. - ``ieqs`` -- list of inequalities. Each line can be specified as any iterable container of ``base_ring`` elements. An entry equal to ``[-1,7,3,4]`` represents the inequality `7x_1+3x_2+4x_3\geq 1`. - ``eqns`` -- list of equalities. Each line can be specified as any iterable container of ``base_ring`` elements. An entry equal to ``[-1,7,3,4]`` represents the equality `7x_1+3x_2+4x_3= 1`. - ``base_ring`` -- either ``QQ`` or ``RDF``. The field over which the polyhedron will be defined. For ``QQ``, exact arithmetic will be used. For ``RDF``, floating point numbers will be used. Floating point arithmetic is faster but might give the wrong result for degenerate input. - ``ambient_dim`` -- integer. The ambient space dimension. Usually can be figured out automatically from the H/Vrepresentation dimensions. - ``backend`` -- string or ``None`` (default). The backend to use. Valid choices are * ``'cdd'``: use cdd (:mod:`~sage.geometry.polyhedron.backend_cdd`) with `\QQ` or `\RDF` coefficients depending on ``base_ring``. * ``'ppl'``: use ppl (:mod:`~sage.geometry.polyhedron.backend_ppl`) with `\ZZ` or `\QQ` coefficients depending on ``base_ring``. Some backends support further optional arguments: - ``minimize`` -- boolean (default: ``True``). Whether to immediately remove redundant H/V-representation data. Currently not used. - ``verbose`` -- boolean (default: ``False``). Whether to print verbose output for debugging purposes. Only supported by the cdd backends. OUTPUT: The polyhedron defined by the input data. EXAMPLES: Construct some polyhedra:: sage: square_from_vertices = Polyhedron(vertices = [[1, 1], [1, -1], [-1, 1], [-1, -1]]) sage: square_from_ieqs = Polyhedron(ieqs = [[1, 0, 1], [1, 1, 0], [1, 0, -1], [1, -1, 0]]) sage: list(square_from_ieqs.vertex_generator()) [A vertex at (1, -1), A vertex at (1, 1), A vertex at (-1, 1), A vertex at (-1, -1)] sage: list(square_from_vertices.inequality_generator()) [An inequality (1, 0) x + 1 >= 0, An inequality (0, 1) x + 1 >= 0, An inequality (-1, 0) x + 1 >= 0, An inequality (0, -1) x + 1 >= 0] sage: p = Polyhedron(vertices = [[1.1, 2.2], [3.3, 4.4]], base_ring=RDF) sage: p.n_inequalities() 2 The same polyhedron given in two ways:: sage: p = Polyhedron(ieqs = [[0,1,0,0],[0,0,1,0]]) sage: p.Vrepresentation() (A line in the direction (0, 0, 1), A ray in the direction (1, 0, 0), A ray in the direction (0, 1, 0), A vertex at (0, 0, 0)) sage: q = Polyhedron(vertices=[[0,0,0]], rays=[[1,0,0],[0,1,0]], lines=[[0,0,1]]) sage: q.Hrepresentation() (An inequality (1, 0, 0) x + 0 >= 0, An inequality (0, 1, 0) x + 0 >= 0) Finally, a more complicated example. Take `\mathbb{R}_{\geq 0}^6` with coordinates `a, b, \dots, f` and * The inequality `e+b \geq c+d` * The inequality `e+c \geq b+d` * The equation `a+b+c+d+e+f = 31` :: sage: positive_coords = Polyhedron(ieqs=[ ... [0, 1, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0], ... [0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 0, 1]]) sage: P = Polyhedron(ieqs=positive_coords.inequalities() + ( ... [0,0,1,-1,-1,1,0], [0,0,-1,1,-1,1,0]), eqns=[[-31,1,1,1,1,1,1]]) sage: P A 5-dimensional polyhedron in QQ^6 defined as the convex hull of 7 vertices sage: P.dim() 5 sage: P.Vrepresentation() (A vertex at (31, 0, 0, 0, 0, 0), A vertex at (0, 0, 0, 0, 0, 31), A vertex at (0, 0, 0, 0, 31, 0), A vertex at (0, 0, 31/2, 0, 31/2, 0), A vertex at (0, 31/2, 31/2, 0, 0, 0), A vertex at (0, 31/2, 0, 0, 31/2, 0), A vertex at (0, 0, 0, 31/2, 31/2, 0)) .. NOTE:: * Once constructed, a ``Polyhedron`` object is immutable. * Although the option ``field=RDF`` allows numerical data to be used, it might not give the right answer for degenerate input data - the results can depend upon the tolerance setting of cdd. """ # Clean up the arguments vertices = _make_listlist(vertices) rays = _make_listlist(rays) lines = _make_listlist(lines) ieqs = _make_listlist(ieqs) eqns = _make_listlist(eqns) got_Vrep = (len(vertices+rays+lines) > 0) got_Hrep = (len(ieqs+eqns) > 0) if got_Vrep and got_Hrep: raise ValueError('You cannot specify both H- and V-representation.') elif got_Vrep: deduced_ambient_dim = _common_length_of(vertices, rays, lines)[1] elif got_Hrep: deduced_ambient_dim = _common_length_of(ieqs, eqns)[1] - 1 else: if ambient_dim is None: deduced_ambient_dim = 0 else: deduced_ambient_dim = ambient_dim if base_ring is None: base_ring = ZZ # set ambient_dim if ambient_dim is not None and deduced_ambient_dim!=ambient_dim: raise ValueError('Ambient space dimension mismatch. Try removing the "ambient_dim" parameter.') ambient_dim = deduced_ambient_dim # figure out base_ring from sage.misc.flatten import flatten values = flatten(vertices+rays+lines+ieqs+eqns) if base_ring is not None: try: convert = not all(x.parent() is base_ring for x in values) except AttributeError: # No x.parent() method? convert = True else: from sage.rings.integer import is_Integer from sage.rings.rational import is_Rational from sage.rings.real_double import is_RealDoubleElement if all(is_Integer(x) for x in values): if got_Vrep: base_ring = ZZ else: # integral inequalities usually do not determine a latice polytope! base_ring = QQ convert=False elif all(is_Rational(x) for x in values): base_ring = QQ convert=False elif all(is_RealDoubleElement(x) for x in values): base_ring = RDF convert=False else: try: map(ZZ, values) if got_Vrep: base_ring = ZZ else: base_ring = QQ convert = True except TypeError: from sage.structure.sequence import Sequence values = Sequence(values) if QQ.has_coerce_map_from(values.universe()): base_ring = QQ convert = True else: base_ring = RDF convert = True # Add the origin if necesarry if got_Vrep and len(vertices)==0: vertices = [ [0]*ambient_dim ] # Specific backends can override the base_ring from sage.geometry.polyhedron.parent import Polyhedra parent = Polyhedra(base_ring, ambient_dim, backend=backend) base_ring = parent.base_ring() # Convert into base_ring if necessary def convert_base_ring(lstlst): return [ [base_ring(x) for x in lst] for lst in lstlst] Hrep = Vrep = None if got_Hrep: Hrep = [ieqs, eqns] if got_Vrep: Vrep = [vertices, rays, lines] # finally, construct the Polyhedron return parent(Vrep, Hrep, convert=convert)
def _rad(self, center): return RBF.one() << QQ(center).valuation(2)
def enumerate_totallyreal_fields_all(n, B, verbose=0, return_seqs=False, return_pari_objects=True): r""" Enumerates *all* totally real fields of degree ``n`` with discriminant at most ``B``, primitive or otherwise. INPUT: - ``n`` -- integer, the degree - ``B`` -- integer, the discriminant bound - ``verbose`` -- boolean or nonnegative integer or string (default: 0) give a verbose description of the computations being performed. If ``verbose`` is set to ``2`` or more then it outputs some extra information. If ``verbose`` is a string then it outputs to a file specified by ``verbose`` - ``return_seqs`` -- (boolean, default False) If ``True``, then return the polynomials as sequences (for easier exporting to a file). This also returns a list of four numbers, as explained in the OUTPUT section below. - ``return_pari_objects`` -- (boolean, default: True) if both ``return_seqs`` and ``return_pari_objects`` are ``False`` then it returns the elements as Sage objects; otherwise it returns pari objects. EXAMPLES:: sage: enumerate_totallyreal_fields_all(4, 2000) [[725, x^4 - x^3 - 3*x^2 + x + 1], [1125, x^4 - x^3 - 4*x^2 + 4*x + 1], [1600, x^4 - 6*x^2 + 4], [1957, x^4 - 4*x^2 - x + 1], [2000, x^4 - 5*x^2 + 5]] sage: enumerate_totallyreal_fields_all(1, 10) [[1, x - 1]] TESTS: Each of the outputs must be elements of Sage if ``return_pari_objects`` is set to ``False``:: sage: enumerate_totallyreal_fields_all(2, 10) [[5, x^2 - x - 1], [8, x^2 - 2]] sage: type(enumerate_totallyreal_fields_all(2, 10)[0][1]) <type 'cypari2.gen.Gen'> sage: enumerate_totallyreal_fields_all(2, 10, return_pari_objects=False)[0][1].parent() Univariate Polynomial Ring in x over Rational Field In practice most of these will be found by :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim`, which is guaranteed to return all primitive fields but often returns many non-primitive ones as well. For instance, only one of the five fields in the example above is primitive, but :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim` finds four out of the five (the exception being `x^4 - 6x^2 + 4`). The following was fixed in :trac:`13101`:: sage: enumerate_totallyreal_fields_all(8, 10^6) # long time (about 2 s) [] """ S = [] counts = [0, 0, 0, 0] if len(divisors(n)) > 4: raise ValueError("Only implemented for n = p*q with p,q prime") for d in divisors(n): if 1 < d < n: Sds = enumerate_totallyreal_fields_prim( d, int(math.floor((1. * B)**(1. * d / n))), verbose=verbose) for i in range(len(Sds)): if verbose: print("=" * 80) print("Taking F =", Sds[i][1]) F = NumberField(ZZx(Sds[i][1]), 't') T = enumerate_totallyreal_fields_rel(F, n / d, B, verbose=verbose, return_seqs=return_seqs) if return_seqs: for i in range(4): counts[i] += T[0][i] S += [[t[0], pari(t[1]).Polrev()] for t in T[1]] else: S += [[t[0], t[1]] for t in T] for E in enumerate_totallyreal_fields_prim( n / d, int( math.floor((1. * B)**(1. / d) / (1. * Sds[i][0])**(n * 1. / d**2)))): for EF in F.composite_fields(NumberField(ZZx(E[1]), 'u')): if EF.degree() == n and EF.disc() <= B: S.append( [EF.disc(), pari(EF.absolute_polynomial())]) S += enumerate_totallyreal_fields_prim(n, B, verbose=verbose) S.sort(key=lambda x: (x[0], [QQ(cf) for cf in x[1].polrecip().Vec()])) weed_fields(S) # Output. if verbose: saveout = sys.stdout if isinstance(verbose, str): fsock = open(verbose, 'w') sys.stdout = fsock # Else, print to screen print("=" * 80) print("Polynomials tested: {}".format(counts[0])) print("Polynomials with discriminant with large enough square" " divisor: {}".format(counts[1])) print("Irreducible polynomials: {}".format(counts[2])) print("Polynomials with nfdisc <= B: {}".format(counts[3])) for i in range(len(S)): print(S[i]) if isinstance(verbose, str): fsock.close() sys.stdout = saveout # Make sure to return elements that belong to Sage if return_seqs: return [[ZZ(_) for _ in counts], [[ZZ(s[0]), [QQ(_) for _ in s[1].polrecip().Vec()]] for s in S]] elif return_pari_objects: return S else: Px = PolynomialRing(QQ, 'x') return [[ZZ(s[0]), Px([QQ(_) for _ in s[1].list()])] for s in S]
def Polyhedron(vertices=None, rays=None, lines=None, ieqs=None, eqns=None, ambient_dim=None, base_ring=None, minimize=True, verbose=False, backend=None): """ Construct a polyhedron object. You may either define it with vertex/ray/line or inequalities/equations data, but not both. Redundant data will automatically be removed (unless ``minimize=False``), and the complementary representation will be computed. INPUT: - ``vertices`` -- list of point. Each point can be specified as any iterable container of ``base_ring`` elements. If ``rays`` or ``lines`` are specified but no ``vertices``, the origin is taken to be the single vertex. - ``rays`` -- list of rays. Each ray can be specified as any iterable container of ``base_ring`` elements. - ``lines`` -- list of lines. Each line can be specified as any iterable container of ``base_ring`` elements. - ``ieqs`` -- list of inequalities. Each line can be specified as any iterable container of ``base_ring`` elements. An entry equal to ``[-1,7,3,4]`` represents the inequality `7x_1+3x_2+4x_3\geq 1`. - ``eqns`` -- list of equalities. Each line can be specified as any iterable container of ``base_ring`` elements. An entry equal to ``[-1,7,3,4]`` represents the equality `7x_1+3x_2+4x_3= 1`. - ``base_ring`` -- a sub-field of the reals implemented in Sage. The field over which the polyhedron will be defined. For ``QQ`` and algebraic extensions, exact arithmetic will be used. For ``RDF``, floating point numbers will be used. Floating point arithmetic is faster but might give the wrong result for degenerate input. - ``ambient_dim`` -- integer. The ambient space dimension. Usually can be figured out automatically from the H/Vrepresentation dimensions. - ``backend`` -- string or ``None`` (default). The backend to use. Valid choices are * ``'cdd'``: use cdd (:mod:`~sage.geometry.polyhedron.backend_cdd`) with `\QQ` or `\RDF` coefficients depending on ``base_ring``. * ``'ppl'``: use ppl (:mod:`~sage.geometry.polyhedron.backend_ppl`) with `\ZZ` or `\QQ` coefficients depending on ``base_ring``. * ``'field'``: use python implementation (:mod:`~sage.geometry.polyhedron.backend_field`) for any field Some backends support further optional arguments: - ``minimize`` -- boolean (default: ``True``). Whether to immediately remove redundant H/V-representation data. Currently not used. - ``verbose`` -- boolean (default: ``False``). Whether to print verbose output for debugging purposes. Only supported by the cdd backends. OUTPUT: The polyhedron defined by the input data. EXAMPLES: Construct some polyhedra:: sage: square_from_vertices = Polyhedron(vertices = [[1, 1], [1, -1], [-1, 1], [-1, -1]]) sage: square_from_ieqs = Polyhedron(ieqs = [[1, 0, 1], [1, 1, 0], [1, 0, -1], [1, -1, 0]]) sage: list(square_from_ieqs.vertex_generator()) [A vertex at (1, -1), A vertex at (1, 1), A vertex at (-1, 1), A vertex at (-1, -1)] sage: list(square_from_vertices.inequality_generator()) [An inequality (1, 0) x + 1 >= 0, An inequality (0, 1) x + 1 >= 0, An inequality (-1, 0) x + 1 >= 0, An inequality (0, -1) x + 1 >= 0] sage: p = Polyhedron(vertices = [[1.1, 2.2], [3.3, 4.4]], base_ring=RDF) sage: p.n_inequalities() 2 The same polyhedron given in two ways:: sage: p = Polyhedron(ieqs = [[0,1,0,0],[0,0,1,0]]) sage: p.Vrepresentation() (A line in the direction (0, 0, 1), A ray in the direction (1, 0, 0), A ray in the direction (0, 1, 0), A vertex at (0, 0, 0)) sage: q = Polyhedron(vertices=[[0,0,0]], rays=[[1,0,0],[0,1,0]], lines=[[0,0,1]]) sage: q.Hrepresentation() (An inequality (1, 0, 0) x + 0 >= 0, An inequality (0, 1, 0) x + 0 >= 0) Finally, a more complicated example. Take `\mathbb{R}_{\geq 0}^6` with coordinates `a, b, \dots, f` and * The inequality `e+b \geq c+d` * The inequality `e+c \geq b+d` * The equation `a+b+c+d+e+f = 31` :: sage: positive_coords = Polyhedron(ieqs=[ ... [0, 1, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0], ... [0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 0, 1]]) sage: P = Polyhedron(ieqs=positive_coords.inequalities() + ( ... [0,0,1,-1,-1,1,0], [0,0,-1,1,-1,1,0]), eqns=[[-31,1,1,1,1,1,1]]) sage: P A 5-dimensional polyhedron in QQ^6 defined as the convex hull of 7 vertices sage: P.dim() 5 sage: P.Vrepresentation() (A vertex at (31, 0, 0, 0, 0, 0), A vertex at (0, 0, 0, 0, 0, 31), A vertex at (0, 0, 0, 0, 31, 0), A vertex at (0, 0, 31/2, 0, 31/2, 0), A vertex at (0, 31/2, 31/2, 0, 0, 0), A vertex at (0, 31/2, 0, 0, 31/2, 0), A vertex at (0, 0, 0, 31/2, 31/2, 0)) .. NOTE:: * Once constructed, a ``Polyhedron`` object is immutable. * Although the option ``field=RDF`` allows numerical data to be used, it might not give the right answer for degenerate input data - the results can depend upon the tolerance setting of cdd. """ # Clean up the arguments vertices = _make_listlist(vertices) rays = _make_listlist(rays) lines = _make_listlist(lines) ieqs = _make_listlist(ieqs) eqns = _make_listlist(eqns) got_Vrep = (len(vertices + rays + lines) > 0) got_Hrep = (len(ieqs + eqns) > 0) if got_Vrep and got_Hrep: raise ValueError('You cannot specify both H- and V-representation.') elif got_Vrep: deduced_ambient_dim = _common_length_of(vertices, rays, lines)[1] elif got_Hrep: deduced_ambient_dim = _common_length_of(ieqs, eqns)[1] - 1 else: if ambient_dim is None: deduced_ambient_dim = 0 else: deduced_ambient_dim = ambient_dim if base_ring is None: base_ring = ZZ # set ambient_dim if ambient_dim is not None and deduced_ambient_dim != ambient_dim: raise ValueError( 'Ambient space dimension mismatch. Try removing the "ambient_dim" parameter.' ) ambient_dim = deduced_ambient_dim # figure out base_ring from sage.misc.flatten import flatten values = flatten(vertices + rays + lines + ieqs + eqns) if base_ring is not None: try: convert = not all(x.parent() is base_ring for x in values) except AttributeError: # No x.parent() method? convert = True else: from sage.rings.integer import is_Integer from sage.rings.rational import is_Rational from sage.rings.real_double import is_RealDoubleElement if all(is_Integer(x) for x in values): if got_Vrep: base_ring = ZZ else: # integral inequalities usually do not determine a latice polytope! base_ring = QQ convert = False elif all(is_Rational(x) for x in values): base_ring = QQ convert = False elif all(is_RealDoubleElement(x) for x in values): base_ring = RDF convert = False else: try: map(ZZ, values) if got_Vrep: base_ring = ZZ else: base_ring = QQ convert = True except (TypeError, ValueError): from sage.structure.sequence import Sequence values = Sequence(values) common_ring = values.universe() if QQ.has_coerce_map_from(common_ring): base_ring = QQ convert = True elif common_ring is RR: # DWIM: replace with RDF base_ring = RDF convert = True else: base_ring = common_ring convert = True # Add the origin if necesarry if got_Vrep and len(vertices) == 0: vertices = [[0] * ambient_dim] # Specific backends can override the base_ring from sage.geometry.polyhedron.parent import Polyhedra parent = Polyhedra(base_ring, ambient_dim, backend=backend) base_ring = parent.base_ring() # finally, construct the Polyhedron Hrep = Vrep = None if got_Hrep: Hrep = [ieqs, eqns] if got_Vrep: Vrep = [vertices, rays, lines] return parent(Vrep, Hrep, convert=convert, verbose=verbose)
def e(tableau, star=0): """ The unnormalized Young projection operator corresponding to the Young tableau ``tableau`` (which is supposed to contain every integer from `1` to its size precisely once, but may and may not be standard). EXAMPLES:: sage: from sage.combinat.symmetric_group_algebra import e sage: e([[1,2]]) [1, 2] + [2, 1] sage: e([[1],[2]]) [1, 2] - [2, 1] sage: e([]) [] There are differing conventions for the order of the symmetrizers and antisymmetrizers. This example illustrates our conventions:: sage: e([[1,2],[3]]) [1, 2, 3] + [2, 1, 3] - [3, 1, 2] - [3, 2, 1] """ t = Tableau(tableau) if star: t = t.restrict(t.size()-star) mult = permutation_options['mult'] permutation_options['mult'] = 'l2r' if t in e_cache: res = e_cache[t] else: rs = t.row_stabilizer().list() cs = t.column_stabilizer().list() n = t.size() QSn = SymmetricGroupAlgebra(QQ, n) one = QQ.one() P = permutation.Permutation rd = dict((P(h), one) for h in rs) sym = QSn._from_dict(rd) cd = dict((P(v), v.sign()*one) for v in cs) antisym = QSn._from_dict(cd) res = antisym*sym # Ugly hack for the case of an empty tableau, due to the # annoyance of Permutation(Tableau([]).row_stabilizer()[0]) # being [1] rather than [] (which seems to have its origins in # permutation group code). # TODO: Fix this. if len(tableau) == 0: res = QSn.one() e_cache[t] = res permutation_options['mult'] = mult return res
def __call__(self, Q, P): """ Compute and return the :class:`ReductionData` corresponding to the genus 2 curve `y^2 + Q(x) y = P(x)`. EXAMPLES:: sage: x = polygen(QQ) sage: genus2reduction(x^3 - 2*x^2 - 2*x + 1, -5*x^5) Reduction data about this proper smooth genus 2 curve: y^2 + (x^3 - 2*x^2 - 2*x + 1)*y = -5*x^5 A Minimal Equation (away from 2): y^2 = x^6 - 240*x^4 - 2550*x^3 - 11400*x^2 - 24100*x - 19855 Minimal Discriminant (away from 2): 56675000 Conductor (away from 2): 1416875 Local Data: p=2 (potential) stable reduction: (II), j=1 p=5 (potential) stable reduction: (I) reduction at p: [V] page 156, (3), f=4 p=2267 (potential) stable reduction: (II), j=432 reduction at p: [I{1-0-0}] page 170, (1), f=1 :: sage: genus2reduction(x^2 + 1, -5*x^5) Reduction data about this proper smooth genus 2 curve: y^2 + (x^2 + 1)*y = -5*x^5 A Minimal Equation (away from 2): y^2 = -20*x^5 + x^4 + 2*x^2 + 1 Minimal Discriminant (away from 2): 48838125 Conductor: 32025 Local Data: p=3 (potential) stable reduction: (II), j=1 reduction at p: [I{1-0-0}] page 170, (1), f=1 p=5 (potential) stable reduction: (IV) reduction at p: [I{1-1-2}] page 182, (5), f=2 p=7 (potential) stable reduction: (II), j=4 reduction at p: [I{1-0-0}] page 170, (1), f=1 p=61 (potential) stable reduction: (II), j=57 reduction at p: [I{2-0-0}] page 170, (2), f=1 Verify that we fix :trac:`5573`:: sage: genus2reduction(x^3 + x^2 + x,-2*x^5 + 3*x^4 - x^3 - x^2 - 6*x - 2) Reduction data about this proper smooth genus 2 curve: y^2 + (x^3 + x^2 + x)*y = -2*x^5 + 3*x^4 - x^3 - x^2 - 6*x - 2 A Minimal Equation (away from 2): y^2 = x^6 + 18*x^3 + 36*x^2 - 27 Minimal Discriminant (away from 2): 1520984142 Conductor: 954 Local Data: p=2 (potential) stable reduction: (II), j=1 reduction at p: [I{1-0-0}] page 170, (1), f=1 p=3 (potential) stable reduction: (I) reduction at p: [II] page 155, (1), f=2 p=53 (potential) stable reduction: (II), j=12 reduction at p: [I{1-0-0}] page 170, (1), f=1 """ R = PolynomialRing(QQ, 'x') P = R(P) Q = R(Q) if P.degree() > 6: raise ValueError("P (=%s) must have degree at most 6"%P) if Q.degree() >=4: raise ValueError("Q (=%s) must have degree at most 3"%Q) res = pari.genus2red([P,Q]) conductor = ZZ(res[0]) minimal_equation = R(res[2]) minimal_disc = QQ(res[2].poldisc()).abs() if minimal_equation.degree() == 5: minimal_disc *= minimal_equation[5]**2 # Multiply with suitable power of 2 of the form 2^(2*(d-1) - 12) b = 2 * (minimal_equation.degree() - 1) k = QQ((12 - minimal_disc.valuation(2), b)).ceil() minimal_disc >>= 12 - b*k minimal_disc = ZZ(minimal_disc) local_data = {} for red in res[3]: p = ZZ(red[0]) t = red[1] data = "(potential) stable reduction: (%s)" % roman_numeral[int(t[0])] t = t[1] if len(t) == 1: data += ", j=%s" % t[0].lift() elif len(t) == 2: data += ", j1+j2=%s, j1*j2=%s" % (t[0].lift(), t[1].lift()) t = red[2] if t: data += "\nreduction at p: %s, " % str(t[0]).replace('"', '').replace("(tame) ", "") data += divisors_to_string(t[1]) + ", f=" + str(res[0].valuation(red[0])) local_data[p] = data prime_to_2_conductor_only = (-1 in res[1].component(2)) return ReductionData(res, P, Q, minimal_equation, minimal_disc, local_data, conductor, prime_to_2_conductor_only)
def descend_to(self, K, f=None): r""" Given a subfield `K` and an elliptic curve self defined over a field `L`, this function determines whether there exists an elliptic curve over `K` which is isomorphic over `L` to self. If one exists, it finds it. INPUT: - `K` -- a subfield of the base field of self. - `f` -- an embedding of `K` into the base field of self. OUTPUT: Either an elliptic curve defined over `K` which is isomorphic to self or None if no such curve exists. .. NOTE:: This only works over number fields and QQ. EXAMPLES:: sage: E = EllipticCurve([1,2,3,4,5]) sage: E.descend_to(ZZ) Traceback (most recent call last): ... TypeError: Input must be a field. :: sage: F.<b> = QuadraticField(23) sage: G.<a> = F.extension(x^3+5) sage: E = EllipticCurve(j=1728*b).change_ring(G) sage: E.descend_to(F) Elliptic Curve defined by y^2 = x^3 + (8957952*b-206032896)*x + (-247669456896*b+474699792384) over Number Field in b with defining polynomial x^2 - 23 :: sage: L.<a> = NumberField(x^4 - 7) sage: K.<b> = NumberField(x^2 - 7) sage: E = EllipticCurve([a^6,0]) sage: E.descend_to(K) Elliptic Curve defined by y^2 = x^3 + 1296/49*b*x over Number Field in b with defining polynomial x^2 - 7 :: sage: K.<a> = QuadraticField(17) sage: E = EllipticCurve(j = 2*a) sage: print E.descend_to(QQ) None """ if not K.is_field(): raise TypeError, "Input must be a field." if self.base_field()==K: return self j = self.j_invariant() from sage.rings.all import QQ if K == QQ: f = QQ.embeddings(self.base_field())[0] if j in QQ: jbase = QQ(j) else: return None elif f == None: embeddings = K.embeddings(self.base_field()) if len(embeddings) == 0: raise TypeError, "Input must be a subfield of the base field of the curve." for g in embeddings: try: jbase = g.preimage(j) f = g break except StandardError: pass if f == None: return None else: try: jbase = f.preimage(j) except StandardError: return None E = EllipticCurve(j=jbase) E2 = EllipticCurve(self.base_field(), [f(a) for a in E.a_invariants()]) if jbase==0: d = self.is_sextic_twist(E2) if d == 1: return E if d == 0: return None Etwist = E2.sextic_twist(d) elif jbase==1728: d = self.is_quartic_twist(E2) if d == 1: return E if d == 0: return None Etwist = E2.quartic_twist(d) else: d = self.is_quadratic_twist(E2) if d == 1: return E if d == 0: return None Etwist = E2.quadratic_twist(d) if Etwist.is_isomorphic(self): try: Eout = EllipticCurve(K, [f.preimage(a) for a in Etwist.a_invariants()]) except StandardError: return None else: return Eout
def kneading_sequence(theta): r""" Determines the kneading sequence for an angle theta in RR/ZZ which is periodic under doubling. We use the definition for the kneading sequence given in Definition 3.2 of [LS1994]_. INPUT: - ``theta`` -- a rational number with odd denominator OUTPUT: a string representing the kneading sequence of theta in RR/ZZ REFERENCES: [LS1994]_ EXAMPLES:: sage: kneading_sequence(0) '*' :: sage: kneading_sequence(1/3) '1*' Since 1/3 and 7/3 are the same in RR/ZZ, they have the same kneading sequence:: sage: kneading_sequence(7/3) '1*' We can also use (finite) decimal inputs, as long as the denominator in reduced form is odd:: sage: kneading_sequence(1.2) '110*' Since rationals with even denominator are not periodic under doubling, we have not implemented kneading sequences for such rationals:: sage: kneading_sequence(1/4) Traceback (most recent call last): ... ValueError: input must be a rational number with odd denominator """ if theta not in QQ: raise TypeError('input must be a rational number with odd denominator') elif QQ(theta).valuation(2) < 0: raise ValueError( 'input must be a rational number with odd denominator') else: theta = QQ(theta) theta = theta - floor(theta) KS = [] not_done = True left = theta / 2 right = (theta + 1) / 2 y = theta while not_done: if ((y < left) or (y > right)): KS.append('0') elif ((y > left) and (y < right)): KS.append('1') else: not_done = False y = 2 * y - floor(2 * y) KS_str = ''.join(KS) + '*' return KS_str
def enum_affine_rational_field(X, B): """ Enumerates affine rational points on scheme ``X`` up to bound ``B``. INPUT: - ``X`` - a scheme or set of abstract rational points of a scheme. - ``B`` - a positive integer bound. OUTPUT: - a list containing the affine points of ``X`` of height up to ``B``, sorted. EXAMPLES:: sage: A.<x,y,z> = AffineSpace(3, QQ) sage: from sage.schemes.affine.affine_rational_point import enum_affine_rational_field sage: enum_affine_rational_field(A(QQ), 1) [(-1, -1, -1), (-1, -1, 0), (-1, -1, 1), (-1, 0, -1), (-1, 0, 0), (-1, 0, 1), (-1, 1, -1), (-1, 1, 0), (-1, 1, 1), (0, -1, -1), (0, -1, 0), (0, -1, 1), (0, 0, -1), (0, 0, 0), (0, 0, 1), (0, 1, -1), (0, 1, 0), (0, 1, 1), (1, -1, -1), (1, -1, 0), (1, -1, 1), (1, 0, -1), (1, 0, 0), (1, 0, 1), (1, 1, -1), (1, 1, 0), (1, 1, 1)] :: sage: A.<w,x,y,z> = AffineSpace(4, QQ) sage: S = A.subscheme([x^2-y*z+1, w^3+z+y^2]) sage: enum_affine_rational_field(S(QQ), 1) [(0, 0, -1, -1)] sage: enum_affine_rational_field(S(QQ), 2) [(0, 0, -1, -1), (1, -1, -1, -2), (1, 1, -1, -2)] :: sage: A.<x,y> = AffineSpace(2, QQ) sage: C = Curve(x^2+y-x) sage: enum_affine_rational_field(C, 10) # long time (3 s) [(-2, -6), (-1, -2), (-2/3, -10/9), (-1/2, -3/4), (-1/3, -4/9), (0, 0), (1/3, 2/9), (1/2, 1/4), (2/3, 2/9), (1, 0), (4/3, -4/9), (3/2, -3/4), (5/3, -10/9), (2, -2), (3, -6)] AUTHORS: - David R. Kohel <*****@*****.**>: original version. - Charlie Turner (06-2010): small adjustments. - Raman Raghukul 2018: updated. """ from sage.schemes.affine.affine_space import is_AffineSpace if (is_Scheme(X)): if (not is_AffineSpace(X.ambient_space())): raise TypeError( "ambient space must be affine space over the rational field") X = X(X.base_ring()) else: if (not is_AffineSpace(X.codomain().ambient_space())): raise TypeError( "codomain must be affine space over the rational field") n = X.codomain().ambient_space().ngens() VR = X.value_ring() if VR is ZZ: R = [0] + [s * k for k in range(1, B + 1) for s in [1, -1]] iters = [iter(R) for _ in range(n)] else: # rational field iters = [QQ.range_by_height(B + 1) for _ in range(n)] pts = [] P = [0] * n try: pts.append(X(P)) except TypeError: pass for it in iters: next(it) i = 0 while i < n: try: a = VR(next(iters[i])) except StopIteration: if VR is ZZ: iters[i] = iter(R) else: # rational field iters[i] = QQ.range_by_height(B + 1) P[i] = next(iters[i]) # reset P[i] to 0 and increment i += 1 continue P[i] = a try: pts.append(X(P)) except TypeError: pass i = 0 pts.sort() return pts
def descend_to(self, K, f=None): r""" Given an elliptic curve self defined over a field `L` and a subfield `K` of `L`, return all elliptic curves over `K` which are isomorphic over `L` to self. INPUT: - `K` -- a field which embeds into the base field `L` of self. - `f` (optional) -- an embedding of `K` into `L`. Ignored if `K` is `\QQ`. OUTPUT: A list (possibly empty) of elliptic curves defined over `K` which are isomorphic to self over `L`, up to isomorphism over `K`. .. NOTE:: Currently only implemented over number fields. To extend to other fields of characteristic not 2 or 3, what is needed is a method giving the preimages in `K^*/(K^*)^m` of an element of the base field, for `m=2,4,6`. EXAMPLES:: sage: E = EllipticCurve([1,2,3,4,5]) sage: E.descend_to(ZZ) Traceback (most recent call last): ... TypeError: Input must be a field. :: sage: F.<b> = QuadraticField(23) sage: G.<a> = F.extension(x^3+5) sage: E = EllipticCurve(j=1728*b).change_ring(G) sage: EF = E.descend_to(F); EF [Elliptic Curve defined by y^2 = x^3 + (27*b-621)*x + (-1296*b+2484) over Number Field in b with defining polynomial x^2 - 23] sage: all([Ei.change_ring(G).is_isomorphic(E) for Ei in EF]) True :: sage: L.<a> = NumberField(x^4 - 7) sage: K.<b> = NumberField(x^2 - 7, embedding=a^2) sage: E = EllipticCurve([a^6,0]) sage: EK = E.descend_to(K); EK [Elliptic Curve defined by y^2 = x^3 + b*x over Number Field in b with defining polynomial x^2 - 7, Elliptic Curve defined by y^2 = x^3 + 7*b*x over Number Field in b with defining polynomial x^2 - 7] sage: all([Ei.change_ring(L).is_isomorphic(E) for Ei in EK]) True :: sage: K.<a> = QuadraticField(17) sage: E = EllipticCurve(j = 2*a) sage: E.descend_to(QQ) [] TESTS: Check that :trac:`16456` is fixed:: sage: K.<a> = NumberField(x^3-2) sage: E = EllipticCurve('11a1').quadratic_twist(2) sage: EK = E.change_ring(K) sage: EK2 = EK.change_weierstrass_model((a,a,a,a+1)) sage: EK2.descend_to(QQ) [Elliptic Curve defined by y^2 = x^3 + x^2 - 41*x - 199 over Rational Field] sage: k.<i> = QuadraticField(-1) sage: E = EllipticCurve(k,[0,0,0,1,0]) sage: E.descend_to(QQ) [Elliptic Curve defined by y^2 = x^3 + x over Rational Field, Elliptic Curve defined by y^2 = x^3 - 4*x over Rational Field] """ if not K.is_field(): raise TypeError("Input must be a field.") L = self.base_field() if L is K: return self elif L == K: # number fields can be equal but not identical return self.base_extend(K) # Construct an embedding f of K in L, and check that the # j-invariant is in the image, otherwise return an empty list: j = self.j_invariant() from sage.rings.all import QQ if K == QQ: try: jK = QQ(j) except (ValueError, TypeError): return [] elif f is None: embeddings = K.embeddings(L) if len(embeddings) == 0: raise TypeError("Input must be a subfield of the base field of the curve.") for g in embeddings: try: jK = g.preimage(j) f = g break except Exception: pass if f is None: return [] else: try: if f.domain() != K: raise ValueError("embedding has wrong domain") if f.codomain() != L: raise ValueError("embedding has wrong codomain") except AttributeError: raise ValueError("invalid embedding: %s" % s) try: jK = f.preimage(j) except Exception: return [] # Now we have the j-invariant in K and must find all twists # which work, separating the cases of j=0 and j=1728. if L.characteristic(): raise NotImplementedError("Not implemented in positive characteristic") if jK == 0: t = -54*self.c6() try: dlist = t.descend_mod_power(K,6) # list of d in K such that t/d is in L*^6 except AttributeError: raise NotImplementedError("Not implemented over %s" % L) Elist = [EllipticCurve([0,0,0,0,d]) for d in dlist] elif jK == 1728: t = -27*self.c4() try: dlist = t.descend_mod_power(K,4) # list of d in K such that t/d is in L*^4 except AttributeError: raise NotImplementedError("Not implemented over %s" % L) Elist = [EllipticCurve([0,0,0,d,0]) for d in dlist] else: c4, c6 = self.c_invariants() t = c6/c4 try: dlist = t.descend_mod_power(K,2) # list of d in K such that t/d is in L*^2 except AttributeError: raise NotImplementedError("Not implemented over %s" % L) c = -27*jK/(jK-1728) # =-27c4^3/c6^2 a4list = [c*d**2 for d in dlist] a6list = [2*a4*d for a4,d in zip(a4list,dlist)] Elist = [EllipticCurve([0,0,0,a4,a6]) for a4,a6 in zip(a4list,a6list)] if K is QQ: Elist = [E.minimal_model() for E in Elist] return Elist
def descend_to(self, K, f=None): r""" Given a subfield `K` and an elliptic curve self defined over a field `L`, this function determines whether there exists an elliptic curve over `K` which is isomorphic over `L` to self. If one exists, it finds it. INPUT: - `K` -- a subfield of the base field of self. - `f` -- an embedding of `K` into the base field of self. OUTPUT: Either an elliptic curve defined over `K` which is isomorphic to self or None if no such curve exists. .. NOTE:: This only works over number fields and QQ. EXAMPLES:: sage: E = EllipticCurve([1,2,3,4,5]) sage: E.descend_to(ZZ) Traceback (most recent call last): ... TypeError: Input must be a field. :: sage: F.<b> = QuadraticField(23) sage: G.<a> = F.extension(x^3+5) sage: E = EllipticCurve(j=1728*b).change_ring(G) sage: E.descend_to(F) Elliptic Curve defined by y^2 = x^3 + (8957952*b-206032896)*x + (-247669456896*b+474699792384) over Number Field in b with defining polynomial x^2 - 23 :: sage: L.<a> = NumberField(x^4 - 7) sage: K.<b> = NumberField(x^2 - 7) sage: E = EllipticCurve([a^6,0]) sage: E.descend_to(K) Elliptic Curve defined by y^2 = x^3 + 1296/49*b*x over Number Field in b with defining polynomial x^2 - 7 :: sage: K.<a> = QuadraticField(17) sage: E = EllipticCurve(j = 2*a) sage: print E.descend_to(QQ) None """ if not K.is_field(): raise TypeError, "Input must be a field." if self.base_field() == K: return self j = self.j_invariant() from sage.rings.all import QQ if K == QQ: f = QQ.embeddings(self.base_field())[0] if j in QQ: jbase = QQ(j) else: return None elif f == None: embeddings = K.embeddings(self.base_field()) if len(embeddings) == 0: raise TypeError, "Input must be a subfield of the base field of the curve." for g in embeddings: try: jbase = g.preimage(j) f = g break except StandardError: pass if f == None: return None else: try: jbase = f.preimage(j) except StandardError: return None E = EllipticCurve(j=jbase) E2 = EllipticCurve(self.base_field(), [f(a) for a in E.a_invariants()]) if jbase == 0: d = self.is_sextic_twist(E2) if d == 1: return E if d == 0: return None Etwist = E2.sextic_twist(d) elif jbase == 1728: d = self.is_quartic_twist(E2) if d == 1: return E if d == 0: return None Etwist = E2.quartic_twist(d) else: d = self.is_quadratic_twist(E2) if d == 1: return E if d == 0: return None Etwist = E2.quadratic_twist(d) if Etwist.is_isomorphic(self): try: Eout = EllipticCurve( K, [f.preimage(a) for a in Etwist.a_invariants()]) except StandardError: return None else: return Eout