def __init__(self, X, P, codomain=None, check=False): r""" Create the discrete probability space with probabilities on the space X given by the dictionary P with values in the field real_field. EXAMPLES:: sage: S = [ i for i in range(16) ] sage: P = {} sage: for i in range(15): P[i] = 2^(-i-1) sage: P[15] = 2^-16 sage: X = DiscreteProbabilitySpace(S,P) sage: X.domain() (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) sage: X.set() {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} sage: X.entropy().n() 1.99972534179688 A probability space can be defined on any list of elements:: sage: AZ = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' sage: S = [ AZ[i] for i in range(26) ] sage: P = { 'A':1/2, 'B':1/4, 'C':1/4 } sage: X = DiscreteProbabilitySpace(S,P) sage: X Discrete probability space defined by {'A': 1/2, 'C': 1/4, 'B': 1/4} sage: X.entropy().n() 1.50000000000000 """ if codomain is None: codomain = RealField() if not is_RealField(codomain) and not is_RationalField(codomain): raise TypeError( "Argument codomain (= %s) must be the reals or rationals" % codomain) if check: one = sum([P[x] for x in P.keys()]) if is_RationalField(codomain): if not one == 1: raise TypeError( "Argument P (= %s) does not define a probability function" ) else: if not Abs(one - 1) < 2 ^ (-codomain.precision() + 1): raise TypeError( "Argument P (= %s) does not define a probability function" ) ProbabilitySpace_generic.__init__(self, X, codomain) DiscreteRandomVariable.__init__(self, self, P, codomain, check)
def can_convert_to_singular(R): """ Returns True if this ring's base field or ring can be represented in Singular, and the polynomial ring has at least one generator. If this is True then this polynomial ring can be represented in Singular. The following base rings are supported: finite fields, rationals, number fields, and real and complex fields. EXAMPLES:: sage: from sage.rings.polynomial.polynomial_singular_interface import can_convert_to_singular sage: can_convert_to_singular(PolynomialRing(QQ, names=['x'])) True sage: can_convert_to_singular(PolynomialRing(QQ, names=[])) False """ if R.ngens() == 0: return False; base_ring = R.base_ring() return ( sage.rings.finite_rings.constructor.is_FiniteField(base_ring) or is_RationalField(base_ring) or (base_ring.is_prime_field() and base_ring.characteristic() <= 2147483647) or is_RealField(base_ring) or is_ComplexField(base_ring) or is_RealDoubleField(base_ring) or is_ComplexDoubleField(base_ring) or number_field.all.is_NumberField(base_ring) or ( sage.rings.fraction_field.is_FractionField(base_ring) and ( base_ring.base_ring().is_prime_field() or base_ring.base_ring() is ZZ ) ) or base_ring is ZZ or is_IntegerModRing(base_ring) )
def points(self, B=0): """ Return some or all rational points of a projective scheme. INPUT: - `B` -- integer (optional, default=0). The bound for the coordinates. OUTPUT: A list of points. Over a finite field, all points are returned. Over an infinite field, all points satisfying the bound are returned. EXAMPLES:: sage: P1 = ProjectiveSpace(GF(2),1) sage: F.<a> = GF(4,'a') sage: P1(F).points() [(0 : 1), (1 : 0), (1 : 1), (a : 1), (a + 1 : 1)] """ from sage.schemes.projective.projective_rational_point import enum_projective_rational_field from sage.schemes.projective.projective_rational_point import enum_projective_finite_field R = self.value_ring() if is_RationalField(R): if not B > 0: raise TypeError( "A positive bound B (= %s) must be specified." % B) return enum_projective_rational_field(self, B) elif is_FiniteField(R): return enum_projective_finite_field(self.extended_codomain()) else: raise TypeError("Unable to enumerate points over %s." % R)
def global_height(x, prec=53): r""" Return the (correct) global height of a homogeneous coordinate. EXAMPLES:: sage: from rational_points import global_height sage: global_height([1/1,2/3,5/8]) 24 sage: F.<u> = NumberField(x^3-5) sage: P = ProjectiveSpace(F, 2) sage: global_height(P(u,u^2/5,1)) 2.92401773821287 """ k = x[0].parent() # first get rid of the denominators denom = lcm([xi.denominator() for xi in x]) x = [xi * denom for xi in x] if is_RationalField(k): return max(abs(xi) for xi in x) / gcd(x) else: finite = 1 / sum(k.ideal(xi) for xi in x).norm() d = k.degree() infinite = product( max(abs(xi.complex_embedding(prec, i)) for xi in x) for i in range(d)) return (finite * infinite)**(RR(1) / d)
def points(self, B=0): """ Return some or all rational points of a projective scheme. INPUT: - `B` -- integer (optional, default=0). The bound for the coordinates. OUTPUT: A list of points. Over a finite field, all points are returned. Over an infinite field, all points satisfying the bound are returned. EXAMPLES:: sage: P1 = ProjectiveSpace(GF(2),1) sage: F.<a> = GF(4,'a') sage: P1(F).points() [(0 : 1), (1 : 0), (1 : 1), (a : 1), (a + 1 : 1)] """ from sage.schemes.projective.projective_rational_point import enum_projective_rational_field from sage.schemes.projective.projective_rational_point import enum_projective_finite_field R = self.value_ring() if is_RationalField(R): if not B > 0: raise TypeError("A positive bound B (= %s) must be specified."%B) return enum_projective_rational_field(self,B) elif is_FiniteField(R): return enum_projective_finite_field(self.extended_codomain()) else: raise TypeError("Unable to enumerate points over %s."%R)
def __init__(self, X, P, codomain = None, check = False): r""" Create the discrete probability space with probabilities on the space X given by the dictionary P with values in the field real_field. EXAMPLES:: sage: S = [ i for i in range(16) ] sage: P = {} sage: for i in range(15): P[i] = 2^(-i-1) sage: P[15] = 2^-16 sage: X = DiscreteProbabilitySpace(S,P) sage: X.domain() (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) sage: X.set() {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} sage: X.entropy() 1.9997253418 A probability space can be defined on any list of elements. EXAMPLES:: sage: AZ = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' sage: S = [ AZ[i] for i in range(26) ] sage: P = { 'A':1/2, 'B':1/4, 'C':1/4 } sage: X = DiscreteProbabilitySpace(S,P) sage: X Discrete probability space defined by {'A': 1/2, 'C': 1/4, 'B': 1/4} sage: X.entropy() 1.5 """ if codomain is None: codomain = RealField() if not is_RealField(codomain) and not is_RationalField(codomain): raise TypeError("Argument codomain (= %s) must be the reals or rationals" % codomain) if check: one = sum([ P[x] for x in P.keys() ]) if is_RationalField(codomain): if not one == 1: raise TypeError("Argument P (= %s) does not define a probability function") else: if not Abs(one-1) < 2^(-codomain.precision()+1): raise TypeError("Argument P (= %s) does not define a probability function") ProbabilitySpace_generic.__init__(self, X, codomain) DiscreteRandomVariable.__init__(self, self, P, codomain, check)
def relation_matrix_wtk_g0(syms, sign, field, sparse): r""" Compute the matrix of relations. Despite the name, this is used for all spaces (not just for Gamma0). For a description of the algorithm, see the docstring for ``compute_presentation``. INPUT: - ``syms`` -- :class:`ManinSymbolList` - ``sign``: integer (0, 1 or -1) - ``field``: the base field (non-field base rings not supported at present) - ``sparse``: (True or False) whether to use sparse arithmetic. Note that ManinSymbolList objects already have a specific weight, so there is no need for an extra ``weight`` parameter. OUTPUT: a pair (R, mod) where - R is a matrix as output by ``T_relation_matrix_wtk_g0`` - mod is a set of 2-term relations as output by ``sparse_2term_quotient`` EXAMPLES:: sage: L = sage.modular.modsym.manin_symbol_list.ManinSymbolList_gamma0(8,2) sage: A = sage.modular.modsym.relation_matrix.relation_matrix_wtk_g0(L, 0, GF(2), True); A ( [0 0 0 0 0 0 0 0 1 0 0 0] [0 0 0 0 0 0 0 0 1 1 1 0] [0 0 0 0 0 0 1 0 0 1 1 0] [0 0 0 0 0 0 1 0 0 0 0 0], [(1, 1), (1, 1), (8, 1), (10, 1), (6, 1), (11, 1), (6, 1), (9, 1), (8, 1), (9, 1), (10, 1), (11, 1)] ) sage: A[0].is_sparse() True """ rels = modS_relations(syms) if sign != 0: # Let rels = rels union I relations. rels.update(modI_relations(syms, sign)) rels = sorted(rels) # required for stability of doctests with python3 if syms._apply_S_only_0pm1() and is_RationalField(field): from . import relation_matrix_pyx mod = relation_matrix_pyx.sparse_2term_quotient_only_pm1( rels, len(syms)) else: mod = sparse_2term_quotient(rels, len(syms), field) R = T_relation_matrix_wtk_g0(syms, mod, field, sparse) return R, mod
def points(self, B=0): r""" Return some or all rational points of an affine scheme. INPUT: - ``B`` -- integer (optional, default: 0). The bound for the height of the coordinates. OUTPUT: - If the base ring is a finite field: all points of the scheme, given by coordinate tuples. - If the base ring is `\QQ` or `\ZZ`: the subset of points whose coordinates have height ``B`` or less. EXAMPLES: The bug reported at #11526 is fixed:: sage: A2 = AffineSpace(ZZ,2) sage: F = GF(3) sage: A2(F).points() [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)] sage: R = ZZ sage: A.<x,y> = R[] sage: I = A.ideal(x^2-y^2-1) sage: V = AffineSpace(R,2) sage: X = V.subscheme(I) sage: M = X(R) sage: M.points(1) [(-1, 0), (1, 0)] :: sage: u = QQ['u'].0 sage: K.<v> = NumberField(u^2 + 3) sage: A.<x,y> = AffineSpace(K,2) sage: len(A(K).points(9)) 361 """ R = self.value_ring() if is_RationalField(R) or R == ZZ: if not B > 0: raise TypeError("A positive bound B (= %s) must be specified."%B) from sage.schemes.affine.affine_rational_point import enum_affine_rational_field return enum_affine_rational_field(self,B) if R in NumberFields(): from sage.schemes.affine.affine_rational_point import enum_affine_number_field return enum_affine_number_field(self,B) elif is_FiniteField(R): from sage.schemes.affine.affine_rational_point import enum_affine_finite_field return enum_affine_finite_field(self) else: raise TypeError("Unable to enumerate points over %s."%R)
def points(self, B=0): """ Return some or all rational points of a projective scheme. INPUT: - `B` -- integer (optional, default=0). The bound for the coordinates. OUTPUT: A list of points. Over a finite field, all points are returned. Over an infinite field, all points satisfying the bound are returned. EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: P(QQ).points(4) [(-4 : 1), (-3 : 1), (-2 : 1), (-3/2 : 1), (-4/3 : 1), (-1 : 1), (-3/4 : 1), (-2/3 : 1), (-1/2 : 1), (-1/3 : 1), (-1/4 : 1), (0 : 1), (1/4 : 1), (1/3 : 1), (1/2 : 1), (2/3 : 1), (3/4 : 1), (1 : 0), (1 : 1), (4/3 : 1), (3/2 : 1), (2 : 1), (3 : 1), (4 : 1)] :: sage: u = QQ['u'].0 sage: K.<v> = NumberField(u^2 + 3) sage: P.<x,y,z> = ProjectiveSpace(K,2) sage: len(P(K).points(9)) 381 :: sage: P1 = ProjectiveSpace(GF(2),1) sage: F.<a> = GF(4,'a') sage: P1(F).points() [(0 : 1), (1 : 0), (1 : 1), (a : 1), (a + 1 : 1)] """ R = self.value_ring() if is_RationalField(R): if not B > 0: raise TypeError("A positive bound B (= %s) must be specified."%B) from sage.schemes.projective.projective_rational_point import enum_projective_rational_field return enum_projective_rational_field(self,B) elif R in NumberFields(): from sage.schemes.projective.projective_rational_point import enum_projective_number_field return enum_projective_number_field(self,B) elif is_FiniteField(R): from sage.schemes.projective.projective_rational_point import enum_projective_finite_field return enum_projective_finite_field(self.extended_codomain()) else: raise TypeError("Unable to enumerate points over %s."%R)
def can_convert_to_singular(R): """ Returns True if this ring's base field or ring can be represented in Singular, and the polynomial ring has at least one generator. If this is True then this polynomial ring can be represented in Singular. The following base rings are supported: finite fields, rationals, number fields, and real and complex fields. EXAMPLES:: sage: from sage.rings.polynomial.polynomial_singular_interface import can_convert_to_singular sage: can_convert_to_singular(PolynomialRing(QQ, names=['x'])) True sage: can_convert_to_singular(PolynomialRing(ZZ, names=['x'])) True sage: can_convert_to_singular(PolynomialRing(QQ, names=[])) False TESTS: Avoid non absolute number fields (see :trac:`23535`):: sage: K.<a,b> = NumberField([x^2-2,x^2-5]) sage: can_convert_to_singular(K['s,t']) False """ if R.ngens() == 0: return False base_ring = R.base_ring() if (base_ring is ZZ or sage.rings.finite_rings.finite_field_constructor.is_FiniteField( base_ring) or is_RationalField(base_ring) or isinstance( base_ring, (sage.rings.abc.IntegerModRing, sage.rings.abc.RealField, sage.rings.abc.ComplexField, sage.rings.abc.RealDoubleField, sage.rings.abc.ComplexDoubleField))): return True elif base_ring.is_prime_field(): return base_ring.characteristic() <= 2147483647 elif number_field.number_field_base.is_NumberField(base_ring): return base_ring.is_absolute() elif sage.rings.fraction_field.is_FractionField(base_ring): B = base_ring.base_ring() return B.is_prime_field() or B is ZZ or is_FiniteField(B) elif isinstance(base_ring, RationalFunctionField): return base_ring.constant_field().is_prime_field() else: return False
def relation_matrix_wtk_g0(syms, sign, field, sparse): r""" Compute the matrix of relations. Despite the name, this is used for all spaces (not just for Gamma0). For a description of the algorithm, see the docstring for ``compute_presentation``. INPUT: - ``syms`` -- :class:`ManinSymbolList` - ``sign``: integer (0, 1 or -1) - ``field``: the base field (non-field base rings not supported at present) - ``sparse``: (True or False) whether to use sparse arithmetic. Note that ManinSymbolList objects already have a specific weight, so there is no need for an extra ``weight`` parameter. OUTPUT: a pair (R, mod) where - R is a matrix as output by ``T_relation_matrix_wtk_g0`` - mod is a set of 2-term relations as output by ``sparse_2term_quotient`` EXAMPLE:: sage: L = sage.modular.modsym.manin_symbol_list.ManinSymbolList_gamma0(8,2) sage: A = sage.modular.modsym.relation_matrix.relation_matrix_wtk_g0(L, 0, GF(2), True); A ( [0 0 0 0 0 0 0 0 1 0 0 0] [0 0 0 0 0 0 0 0 1 1 1 0] [0 0 0 0 0 0 1 0 0 1 1 0] [0 0 0 0 0 0 1 0 0 0 0 0], [(1, 1), (1, 1), (8, 1), (10, 1), (6, 1), (11, 1), (6, 1), (9, 1), (8, 1), (9, 1), (10, 1), (11, 1)] ) sage: A[0].is_sparse() True """ rels = modS_relations(syms) if sign != 0: # Let rels = rels union I relations. rels.update(modI_relations(syms, sign)) if syms._apply_S_only_0pm1() and is_RationalField(field): import relation_matrix_pyx mod = relation_matrix_pyx.sparse_2term_quotient_only_pm1(rels, len(syms)) else: mod = sparse_2term_quotient(rels, len(syms), field) R = T_relation_matrix_wtk_g0(syms, mod, field, sparse) return R, mod
def can_convert_to_singular(R): """ Returns True if this ring's base field or ring can be represented in Singular, and the polynomial ring has at least one generator. If this is True then this polynomial ring can be represented in Singular. The following base rings are supported: finite fields, rationals, number fields, and real and complex fields. EXAMPLES:: sage: from sage.rings.polynomial.polynomial_singular_interface import can_convert_to_singular sage: can_convert_to_singular(PolynomialRing(QQ, names=['x'])) True sage: can_convert_to_singular(PolynomialRing(QQ, names=[])) False TESTS: Avoid non absolute number fields (see :trac:`23535`):: sage: K.<a,b> = NumberField([x^2-2,x^2-5]) sage: can_convert_to_singular(K['s,t']) False """ if R.ngens() == 0: return False; base_ring = R.base_ring() if (base_ring is ZZ or sage.rings.finite_rings.finite_field_constructor.is_FiniteField(base_ring) or is_RationalField(base_ring) or is_IntegerModRing(base_ring) or is_RealField(base_ring) or is_ComplexField(base_ring) or is_RealDoubleField(base_ring) or is_ComplexDoubleField(base_ring)): return True elif base_ring.is_prime_field(): return base_ring.characteristic() <= 2147483647 elif number_field.number_field_base.is_NumberField(base_ring): return base_ring.is_absolute() elif sage.rings.fraction_field.is_FractionField(base_ring): B = base_ring.base_ring() return B.is_prime_field() or B is ZZ or is_FiniteField(B) elif is_RationalFunctionField(base_ring): return base_ring.constant_field().is_prime_field() else: return False
def points(self, B=0): from sage.schemes.generic.rational_point import enum_projective_rational_field from sage.schemes.generic.rational_point import enum_projective_finite_field try: R = self.value_ring() except TypeError: raise TypeError, "Domain of argument must be of the form Spec(S)." if is_RationalField(R): if not B > 0: raise TypeError, "A positive bound B (= %s) must be specified." % B return enum_projective_rational_field(self, B) elif is_FiniteField(R): return enum_projective_finite_field(self) else: raise TypeError, "Unable to enumerate points over %s." % R
def points(self, B=0): r""" Return some or all rational points of an affine scheme. INPUT: - ``B`` -- integer (optional, default: 0). The bound for the height of the coordinates. OUTPUT: - If the base ring is a finite field: all points of the scheme, given by coordinate tuples. - If the base ring is `\QQ` or `\ZZ`: the subset of points whose coordinates have height ``B`` or less. EXAMPLES: The bug reported at #11526 is fixed:: sage: A2 = AffineSpace(ZZ,2) sage: F = GF(3) sage: A2(F).points() [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)] sage: R = ZZ sage: A.<x,y> = R[] sage: I = A.ideal(x^2-y^2-1) sage: V = AffineSpace(R,2) sage: X = V.subscheme(I) sage: M = X(R) sage: M.points(1) [(-1, 0), (1, 0)] """ R = self.value_ring() if is_RationalField(R) or R == ZZ: if not B > 0: raise TypeError( "A positive bound B (= %s) must be specified." % B) from sage.schemes.affine.affine_rational_point import enum_affine_rational_field return enum_affine_rational_field(self, B) elif is_FiniteField(R): from sage.schemes.affine.affine_rational_point import enum_affine_finite_field return enum_affine_finite_field(self) else: raise TypeError("Unable to enumerate points over %s." % R)
def points(self, B=0): r""" Return the set of points given by coordinate tuples with coordinates in the base ring. INPUT: - ``B`` -- an integer. OUTPUT: - If the base ring is a finite field: the set of points given by coordinate tuples. - If the base ring is `\QQ` or `\ZZ`: the subset of points whose coordinates have height ``B`` or less. EXAMPLES: The bug reported at #11526 is fixed:: sage: R = ZZ sage: A.<x,y> = R[] sage: I = A.ideal(x^2-y^2-1) sage: V = AffineSpace(R,2) sage: X = V.subscheme(I) sage: M = X(R) sage: M.points(1) [(-1, 0), (1, 0)] """ try: R = self.value_ring() except TypeError: raise TypeError, "Domain of argument must be of the form Spec(S)." if is_RationalField(R) or R == ZZ: if not B > 0: raise TypeError, "A positive bound B (= %s) must be specified." % B from sage.schemes.generic.rational_point import enum_affine_rational_field return enum_affine_rational_field(self, B) elif is_FiniteField(R): from sage.schemes.generic.rational_point import enum_affine_finite_field return enum_affine_finite_field(self) else: raise TypeError, "Unable to enumerate points over %s." % R
def Conic(base_field, F=None, names=None, unique=True): r""" Return the plane projective conic curve defined by ``F`` over ``base_field``. The input form ``Conic(F, names=None)`` is also accepted, in which case the fraction field of the base ring of ``F`` is used as base field. INPUT: - ``base_field`` -- The base field of the conic. - ``names`` -- a list, tuple, or comma separated string of three variable names specifying the names of the coordinate functions of the ambient space `\Bold{P}^3`. If not specified or read off from ``F``, then this defaults to ``'x,y,z'``. - ``F`` -- a polynomial, list, matrix, ternary quadratic form, or list or tuple of 5 points in the plane. If ``F`` is a polynomial or quadratic form, then the output is the curve in the projective plane defined by ``F = 0``. If ``F`` is a polynomial, then it must be a polynomial of degree at most 2 in 2 variables, or a homogeneous polynomial in of degree 2 in 3 variables. If ``F`` is a matrix, then the output is the zero locus of `(x,y,z) F (x,y,z)^t`. If ``F`` is a list of coefficients, then it has length 3 or 6 and gives the coefficients of the monomials `x^2, y^2, z^2` or all 6 monomials `x^2, xy, xz, y^2, yz, z^2` in lexicographic order. If ``F`` is a list of 5 points in the plane, then the output is a conic through those points. - ``unique`` -- Used only if ``F`` is a list of points in the plane. If the conic through the points is not unique, then raise ``ValueError`` if and only if ``unique`` is True OUTPUT: A plane projective conic curve defined by ``F`` over a field. EXAMPLES: Conic curves given by polynomials :: sage: X,Y,Z = QQ['X,Y,Z'].gens() sage: Conic(X^2 - X*Y + Y^2 - Z^2) Projective Conic Curve over Rational Field defined by X^2 - X*Y + Y^2 - Z^2 sage: x,y = GF(7)['x,y'].gens() sage: Conic(x^2 - x + 2*y^2 - 3, 'U,V,W') Projective Conic Curve over Finite Field of size 7 defined by U^2 + 2*V^2 - U*W - 3*W^2 Conic curves given by matrices :: sage: Conic(matrix(QQ, [[1, 2, 0], [4, 0, 0], [7, 0, 9]]), 'x,y,z') Projective Conic Curve over Rational Field defined by x^2 + 6*x*y + 7*x*z + 9*z^2 sage: x,y,z = GF(11)['x,y,z'].gens() sage: C = Conic(x^2+y^2-2*z^2); C Projective Conic Curve over Finite Field of size 11 defined by x^2 + y^2 - 2*z^2 sage: Conic(C.symmetric_matrix(), 'x,y,z') Projective Conic Curve over Finite Field of size 11 defined by x^2 + y^2 - 2*z^2 Conics given by coefficients :: sage: Conic(QQ, [1,2,3]) Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + 3*z^2 sage: Conic(GF(7), [1,2,3,4,5,6], 'X') Projective Conic Curve over Finite Field of size 7 defined by X0^2 + 2*X0*X1 - 3*X1^2 + 3*X0*X2 - 2*X1*X2 - X2^2 The conic through a set of points :: sage: C = Conic(QQ, [[10,2],[3,4],[-7,6],[7,8],[9,10]]); C Projective Conic Curve over Rational Field defined by x^2 + 13/4*x*y - 17/4*y^2 - 35/2*x*z + 91/4*y*z - 37/2*z^2 sage: C.rational_point() (10 : 2 : 1) sage: C.point([3,4]) (3 : 4 : 1) sage: a=AffineSpace(GF(13),2) sage: Conic([a([x,x^2]) for x in range(5)]) Projective Conic Curve over Finite Field of size 13 defined by x^2 - y*z """ if not (base_field is None or isinstance(base_field, IntegralDomain)): if names is None: names = F F = base_field base_field = None if isinstance(F, (list,tuple)): if len(F) == 1: return Conic(base_field, F[0], names) if names is None: names = 'x,y,z' if len(F) == 5: L=[] for f in F: if isinstance(f, SchemeMorphism_point_affine): C = Sequence(f, universe = base_field) if len(C) != 2: raise TypeError("points in F (=%s) must be planar"%F) C.append(1) elif isinstance(f, SchemeMorphism_point_projective_field): C = Sequence(f, universe = base_field) elif isinstance(f, (list, tuple)): C = Sequence(f, universe = base_field) if len(C) == 2: C.append(1) else: raise TypeError("F (=%s) must be a sequence of planar " \ "points" % F) if len(C) != 3: raise TypeError("points in F (=%s) must be planar" % F) P = C.universe() if not isinstance(P, IntegralDomain): raise TypeError("coordinates of points in F (=%s) must " \ "be in an integral domain" % F) L.append(Sequence([C[0]**2, C[0]*C[1], C[0]*C[2], C[1]**2, C[1]*C[2], C[2]**2], P.fraction_field())) M=Matrix(L) if unique and M.rank() != 5: raise ValueError("points in F (=%s) do not define a unique " \ "conic" % F) con = Conic(base_field, Sequence(M.right_kernel().gen()), names) con.point(F[0]) return con F = Sequence(F, universe = base_field) base_field = F.universe().fraction_field() temp_ring = PolynomialRing(base_field, 3, names) (x,y,z) = temp_ring.gens() if len(F) == 3: return Conic(F[0]*x**2 + F[1]*y**2 + F[2]*z**2) if len(F) == 6: return Conic(F[0]*x**2 + F[1]*x*y + F[2]*x*z + F[3]*y**2 + \ F[4]*y*z + F[5]*z**2) raise TypeError("F (=%s) must be a sequence of 3 or 6" \ "coefficients" % F) if is_QuadraticForm(F): F = F.matrix() if is_Matrix(F) and F.is_square() and F.ncols() == 3: if names is None: names = 'x,y,z' temp_ring = PolynomialRing(F.base_ring(), 3, names) F = vector(temp_ring.gens()) * F * vector(temp_ring.gens()) if not is_MPolynomial(F): raise TypeError("F (=%s) must be a three-variable polynomial or " \ "a sequence of points or coefficients" % F) if F.total_degree() != 2: raise TypeError("F (=%s) must have degree 2" % F) if base_field is None: base_field = F.base_ring() if not isinstance(base_field, IntegralDomain): raise ValueError("Base field (=%s) must be a field" % base_field) base_field = base_field.fraction_field() if names is None: names = F.parent().variable_names() pol_ring = PolynomialRing(base_field, 3, names) if F.parent().ngens() == 2: (x,y,z) = pol_ring.gens() F = pol_ring(F(x/z,y/z)*z**2) if F == 0: raise ValueError("F must be nonzero over base field %s" % base_field) if F.total_degree() != 2: raise TypeError("F (=%s) must have degree 2 over base field %s" % \ (F, base_field)) if F.parent().ngens() == 3: P2 = ProjectiveSpace(2, base_field, names) if is_PrimeFiniteField(base_field): return ProjectiveConic_prime_finite_field(P2, F) if is_FiniteField(base_field): return ProjectiveConic_finite_field(P2, F) if is_RationalField(base_field): return ProjectiveConic_rational_field(P2, F) if is_NumberField(base_field): return ProjectiveConic_number_field(P2, F) if is_FractionField(base_field) and (is_PolynomialRing(base_field.ring()) or is_MPolynomialRing(base_field.ring())): return ProjectiveConic_rational_function_field(P2, F) return ProjectiveConic_field(P2, F) raise TypeError("Number of variables of F (=%s) must be 2 or 3" % F)
def rational_points(X, F=None, split=False, bound=None, tolerance=0.01, prec=53): r""" Return an iterator of rational points on a scheme ``X`` INPUT: - ``X`` - a scheme, affine or projective OPTIONS: - ``F`` - coefficient field - ``split`` - whether to compute the splitting field when the scheme is 0-dimensional - ``bound`` - a bound for multiplicative height - ``tolerance`` - tolerance used for computing height - ``prec`` - precision used for computing height OUTPUT: - an iterator of rational points on ``X`` ALGORITHM: Use brute force plus some elimination. The complexity is approximately `O(n^d)`, where `n` is the size of the field (or the number of elements with bounded height when the field is infinite) and `d` is the dimension of the scheme ``X``. Significantly faster than the current available algorithms in Sage, especially for low dimension schemes in large ambient spaces. EXAMPLES:: sage: from rational_points import rational_points A curve of genus 9 over `\mathbf F_{97}` with many rational points (from the website `<https://manypoints.org>`_):: sage: A.<x,y,z> = AffineSpace(GF(97),3) sage: C = A.subscheme([x^4+y^4+1+3*x^2*y^2+13*y^2+68*x^2,z^2+84*y^2+x^2+67]) sage: len(list(rational_points(C))) 228 The following example is from the `documentation <http://magma.maths.usyd.edu.au/magma/handbook/text/1354>`_ of Magma: a space rational curve with one cusp. Unfeasible with the old methods:: sage: F = GF(7823) sage: P.<x,y,z,w> = ProjectiveSpace(F,3) sage: C = P.subscheme([4*x*z+2*x*w+y^2+4*y*w+7821*z^2+7820*w^2,\ ....: 4*x^2+4*x*y+7821*x*w+7822*y^2+7821*y*w+7821*z^2+7819*z*w+7820*w^2]) sage: len(list(rational_points(C))) # long time 7824 31 nodes on a `Togliatti surface <https://en.wikipedia.org/wiki/Togliatti_surface>`_: only 7 of them are defined over the field of definition. Use ``split=True`` to automatically find the splitting field:: sage: q = QQ['q'].0 sage: F.<q> = NumberField(q^4-10*q^2+20) sage: P.<x,y,z,w> = ProjectiveSpace(F,3) sage: f = 5*q*(2*z-q*w)*(4*(x^2+y^2-z^2)+(1+3*(5-q^2))*w^2)^2-64*(x-w)*\ ....: (x^4-4*x^3*w-10*x^2*y^2-4*x^2*w^2+16*x*w^3-20*x*y^2*w+5*y^4+16*w^4-20*y^2*w^2) sage: X = P.subscheme([f]+f.jacobian_ideal()) sage: len(list(rational_points(X))) 7 sage: len(list(rational_points(X,split=True))) 31 Enumeration of points on a projective plane over a number field:: sage: a = QQ['a'].0 sage: F.<a> = NumberField(a^3-5) sage: P.<x,y,z> = ProjectiveSpace(F, 2) sage: len(list(rational_points(P, bound=RR(5^(1/3))))) 49 """ def base_change(k, F): ch = F.characteristic() if ch == 0: return k.embeddings(F)[0] else: return F def enum_proj_points(I): R = I.ring() k = R.base() n = R.ngens() - 1 for i in range(n + 1): R_ = PolynomialRing(k, 'x', n - i) v = [k(0)] * i + [k(1)] pr = R.hom(v + list(R_.gens()), R_) for rest in enum_points(pr(I)): pt = v + rest if bound == None or global_height( pt, prec=prec) <= bound + tolerance: yield pt def enum_points(I): possibleValues = get_elements() R = I.ring() F = R.base() ch = F.characteristic() n = R.ngens() if n == 0: if I.is_zero(): yield [] return if I.is_one(): return if all(map(lambda _: _.degree() == 1, I.gens())) and (ch > 0 or I.dimension() == 0): # solve using linear algebra f = R.hom(n * [0], F) A = matrix([f(g.coefficient(xi)) for xi in R.gens()] for g in I.gens()) b = vector(-g.constant_coefficient() for g in I.gens()) v0 = A.solve_right(b) r = A.rank() if r == n: yield list(v0) else: K = A.right_kernel().matrix() for v in F**(n - r): yield list(v * K + v0) return if ch > 0 and I.is_homogeneous(): yield [F(0)] * n for pt in enum_proj_points(I): for sca in get_elements(): if sca != 0: yield [x * sca for x in pt] return elim = I.elimination_ideal(I.ring().gens()[1:]) g = elim.gens()[0] if g != 0: S = F['u'] pr1 = R.hom([S.gen()] + [0] * (n - 1), S) possibleValues = (v[0] for v in pr1(g).roots() if bound == None or global_height([v[0], F(1)]) <= bound + tolerance) if split: nonSplit = (f[0] for f in factor(pr1(g)) if f[0].degree() > 1) for f in nonSplit: if ch == 0: F_ = f.splitting_field('a') # `polredbest` from PARI/GP, improves performance significantly f = gen_to_sage( pari(F_.gen().minpoly('x')).polredbest(), {'x': S.gen()}) F_ = f.splitting_field('a') R_ = PolynomialRing(F_, 'x', n) I = R_.ideal( [f.change_ring(base_change(F, F_)) for f in I.gens()]) for pt in enum_points(I): yield pt return R_ = PolynomialRing(F, 'x', n - 1) if n == 1: for v in possibleValues: yield [v] else: for v in possibleValues: pr2 = R.hom([v] + list(R_.gens()), R_) for rest in enum_points(pr2(I)): yield [v] + rest ####################################################################### # begin of main function try: I = X.defining_ideal() R = I.ring() except: # when X has no defining ideal, i.e. when it's the whole space R = X.coordinate_ring() I = R.ideal([]) k = R.base() n = R.ngens() ambient = X.ambient_space() if F: # specified coefficient field R_ = PolynomialRing(F, 'x', n) I = R_.ideal([f.change_ring(base_change(k, F)) for f in I.gens()]) k = F ambient = ambient.change_ring(k) split = False ch = k.characteristic() if (X.is_projective() and I.dimension() == 1) or (not X.is_projective() and I.dimension() == 0): # 0-dimensional dimension # in 0-dim use elimination only bound = None get_elements = lambda: [] else: # positive dimension split = False # splitting field does not work in positive dimension if ch == 0: if bound == None: raise ValueError("need to specify a valid bound") if is_RationalField(k): get_elements = lambda: k.range_by_height(floor(bound) + 1) else: get_elements = lambda: k.elements_of_bounded_height( bound=bound**(k.degree()), tolerance=tolerance, precision=prec) else: # finite field bound = None get_elements = lambda: k if X.is_projective(): # projective case for pt in enum_proj_points(I): if split: # TODO construct homogeneous coordinates from a bunch of # elements from different fields yield pt else: yield ambient(pt) else: # affine case for pt in enum_points(I): if split: yield pt else: yield ambient(pt)
def points_of_bounded_height(self, bound): r""" Returns an iterator of the points in self of absolute height of at most the given bound. Bound check is strict for the rational field. Requires self to be affine space over a number field. Uses the Doyle-Krumm algorithm for computing algebraic numbers up to a given height [Doyle-Krumm]. INPUT: - ``bound`` - a real number OUTPUT: - an iterator of points in self EXAMPLES:: sage: A.<x,y> = AffineSpace(QQ,2) sage: list(A.points_of_bounded_height(3)) [(0, 0), (1, 0), (-1, 0), (1/2, 0), (-1/2, 0), (2, 0), (-2, 0), (0, 1), (1, 1), (-1, 1), (1/2, 1), (-1/2, 1), (2, 1), (-2, 1), (0, -1), (1, -1), (-1, -1), (1/2, -1), (-1/2, -1), (2, -1), (-2, -1), (0, 1/2), (1, 1/2), (-1, 1/2), (1/2, 1/2), (-1/2, 1/2), (2, 1/2), (-2, 1/2), (0, -1/2), (1, -1/2), (-1, -1/2), (1/2, -1/2), (-1/2, -1/2), (2, -1/2), (-2, -1/2), (0, 2), (1, 2), (-1, 2), (1/2, 2), (-1/2, 2), (2, 2), (-2, 2), (0, -2), (1, -2), (-1, -2), (1/2, -2), (-1/2, -2), (2, -2), (-2, -2)] :: sage: u = QQ['u'].0 sage: A.<x,y> = AffineSpace(NumberField(u^2 - 2,'v'), 2) sage: len(list(A.points_of_bounded_height(6))) 121 """ if (is_RationalField(self.base_ring())): ftype = False # stores whether field is a number field or the rational field elif ( self.base_ring() in NumberFields() ): # true for rational field as well, so check is_RationalField first ftype = True else: raise NotImplementedError( "self must be affine space over a number field.") bound = bound**(1 / self.base_ring().absolute_degree() ) # convert to relative height n = self.dimension_relative() R = self.base_ring() zero = R(0) P = [zero for _ in range(n)] yield self(P) if (ftype == False): iters = [R.range_by_height(bound) for _ in range(n)] else: iters = [R.elements_of_bounded_height(bound) for _ in range(n)] for x in iters: next(x) # put at zero i = 0 while i < n: try: P[i] = next(iters[i]) yield self(P) i = 0 except StopIteration: if (ftype == False): iters[i] = R.range_by_height(bound) # reset else: iters[i] = R.elements_of_bounded_height(bound) next(iters[i]) # put at zero P[i] = zero i += 1
def splitting_field(poly, name, map=False, degree_multiple=None, abort_degree=None, simplify=True, simplify_all=False): r""" Compute the splitting field of a given polynomial, defined over a number field. INPUT: - ``poly`` -- a monic polynomial over a number field - ``name`` -- a variable name for the number field - ``map`` -- (default: ``False``) also return an embedding of ``poly`` into the resulting field. Note that computing this embedding might be expensive. - ``degree_multiple`` -- a multiple of the absolute degree of the splitting field. If ``degree_multiple`` equals the actual degree, this can enormously speed up the computation. - ``abort_degree`` -- abort by raising a :class:`SplittingFieldAbort` if it can be determined that the absolute degree of the splitting field is strictly larger than ``abort_degree``. - ``simplify`` -- (default: ``True``) during the algorithm, try to find a simpler defining polynomial for the intermediate number fields using PARI's ``polred()``. This usually speeds up the computation but can also considerably slow it down. Try and see what works best in the given situation. - ``simplify_all`` -- (default: ``False``) If ``True``, simplify intermediate fields and also the resulting number field. OUTPUT: If ``map`` is ``False``, the splitting field as an absolute number field. If ``map`` is ``True``, a tuple ``(K, phi)`` where ``phi`` is an embedding of the base field in ``K``. EXAMPLES:: sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = (x^3 + 2).splitting_field(); K Number Field in a with defining polynomial x^6 + 3*x^5 + 6*x^4 + 11*x^3 + 12*x^2 - 3*x + 1 sage: K.<a> = (x^3 - 3*x + 1).splitting_field(); K Number Field in a with defining polynomial x^3 - 3*x + 1 The ``simplify`` and ``simplify_all`` flags usually yield fields defined by polynomials with smaller coefficients. By default, ``simplify`` is True and ``simplify_all`` is False. :: sage: (x^4 - x + 1).splitting_field('a', simplify=False) Number Field in a with defining polynomial x^24 - 2780*x^22 + 2*x^21 + 3527512*x^20 - 2876*x^19 - 2701391985*x^18 + 945948*x^17 + 1390511639677*x^16 + 736757420*x^15 - 506816498313560*x^14 - 822702898220*x^13 + 134120588299548463*x^12 + 362240696528256*x^11 - 25964582366880639486*x^10 - 91743672243419990*x^9 + 3649429473447308439427*x^8 + 14310332927134072336*x^7 - 363192569823568746892571*x^6 - 1353403793640477725898*x^5 + 24293393281774560140427565*x^4 + 70673814899934142357628*x^3 - 980621447508959243128437933*x^2 - 1539841440617805445432660*x + 18065914012013502602456565991 sage: (x^4 - x + 1).splitting_field('a', simplify=True) Number Field in a with defining polynomial x^24 + 8*x^23 - 32*x^22 - 310*x^21 + 540*x^20 + 4688*x^19 - 6813*x^18 - 32380*x^17 + 49525*x^16 + 102460*x^15 - 129944*x^14 - 287884*x^13 + 372727*x^12 + 150624*x^11 - 110530*x^10 - 566926*x^9 + 1062759*x^8 - 779940*x^7 + 863493*x^6 - 1623578*x^5 + 1759513*x^4 - 955624*x^3 + 459975*x^2 - 141948*x + 53919 sage: (x^4 - x + 1).splitting_field('a', simplify_all=True) Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1 Reducible polynomials also work:: sage: pol = (x^4 - 1)*(x^2 + 1/2)*(x^2 + 1/3) sage: pol.splitting_field('a', simplify_all=True) Number Field in a with defining polynomial x^8 - x^4 + 1 Relative situation:: sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = NumberField(x^3 + 2) sage: S.<t> = PolynomialRing(K) sage: L.<b> = (t^2 - a).splitting_field() sage: L Number Field in b with defining polynomial t^6 + 2 With ``map=True``, we also get the embedding of the base field into the splitting field:: sage: L.<b>, phi = (t^2 - a).splitting_field(map=True) sage: phi Ring morphism: From: Number Field in a with defining polynomial x^3 + 2 To: Number Field in b with defining polynomial t^6 + 2 Defn: a |--> b^2 sage: (x^4 - x + 1).splitting_field('a', simplify_all=True, map=True)[1] Ring morphism: From: Rational Field To: Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1 Defn: 1 |--> 1 We can enable verbose messages:: sage: from sage.misc.verbose import set_verbose sage: set_verbose(2) sage: K.<a> = (x^3 - x + 1).splitting_field() verbose 1 (...: splitting_field.py, splitting_field) Starting field: y verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: [(3, 0)] verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(2, 2), (3, 3)] verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6] verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^2 + 23 verbose 1 (...: splitting_field.py, splitting_field) New field before simplifying: x^2 + 23 (time = ...) verbose 1 (...: splitting_field.py, splitting_field) New field: y^2 - y + 6 (time = ...) verbose 2 (...: splitting_field.py, splitting_field) Converted polynomials to new field (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: [] verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(3, 3)] verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6] verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^3 - x + 1 verbose 1 (...: splitting_field.py, splitting_field) New field: y^6 + 3*y^5 + 19*y^4 + 35*y^3 + 127*y^2 + 73*y + 271 (time = ...) sage: set_verbose(0) Try all Galois groups in degree 4. We use a quadratic base field such that ``polgalois()`` cannot be used:: sage: R.<x> = PolynomialRing(QuadraticField(-11)) sage: C2C2pol = x^4 - 10*x^2 + 1 sage: C2C2pol.splitting_field('x') Number Field in x with defining polynomial x^8 + 24*x^6 + 608*x^4 + 9792*x^2 + 53824 sage: C4pol = x^4 + x^3 + x^2 + x + 1 sage: C4pol.splitting_field('x') Number Field in x with defining polynomial x^8 - x^7 - 2*x^6 + 5*x^5 + x^4 + 15*x^3 - 18*x^2 - 27*x + 81 sage: D8pol = x^4 - 2 sage: D8pol.splitting_field('x') Number Field in x with defining polynomial x^16 + 8*x^15 + 68*x^14 + 336*x^13 + 1514*x^12 + 5080*x^11 + 14912*x^10 + 35048*x^9 + 64959*x^8 + 93416*x^7 + 88216*x^6 + 41608*x^5 - 25586*x^4 - 60048*x^3 - 16628*x^2 + 12008*x + 34961 sage: A4pol = x^4 - 4*x^3 + 14*x^2 - 28*x + 21 sage: A4pol.splitting_field('x') Number Field in x with defining polynomial x^24 - 20*x^23 + 290*x^22 - 3048*x^21 + 26147*x^20 - 186132*x^19 + 1130626*x^18 - 5913784*x^17 + 26899345*x^16 - 106792132*x^15 + 371066538*x^14 - 1127792656*x^13 + 2991524876*x^12 - 6888328132*x^11 + 13655960064*x^10 - 23000783036*x^9 + 32244796382*x^8 - 36347834476*x^7 + 30850889884*x^6 - 16707053128*x^5 + 1896946429*x^4 + 4832907884*x^3 - 3038258802*x^2 - 200383596*x + 593179173 sage: S4pol = x^4 + x + 1 sage: S4pol.splitting_field('x') Number Field in x with defining polynomial x^48 ... Some bigger examples:: sage: R.<x> = PolynomialRing(QQ) sage: pol15 = chebyshev_T(31, x) - 1 # 2^30*(x-1)*minpoly(cos(2*pi/31))^2 sage: pol15.splitting_field('a') Number Field in a with defining polynomial x^15 - x^14 - 14*x^13 + 13*x^12 + 78*x^11 - 66*x^10 - 220*x^9 + 165*x^8 + 330*x^7 - 210*x^6 - 252*x^5 + 126*x^4 + 84*x^3 - 28*x^2 - 8*x + 1 sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12 sage: pol48.splitting_field('a') Number Field in a with defining polynomial x^48 ... If you somehow know the degree of the field in advance, you should add a ``degree_multiple`` argument. This can speed up the computation, in particular for polynomials of degree >= 12 or for relative extensions:: sage: pol15.splitting_field('a', degree_multiple=15) Number Field in a with defining polynomial x^15 + x^14 - 14*x^13 - 13*x^12 + 78*x^11 + 66*x^10 - 220*x^9 - 165*x^8 + 330*x^7 + 210*x^6 - 252*x^5 - 126*x^4 + 84*x^3 + 28*x^2 - 8*x - 1 A value for ``degree_multiple`` which isn't actually a multiple of the absolute degree of the splitting field can either result in a wrong answer or the following exception:: sage: pol48.splitting_field('a', degree_multiple=20) Traceback (most recent call last): ... ValueError: inconsistent degree_multiple in splitting_field() Compute the Galois closure as the splitting field of the defining polynomial:: sage: R.<x> = PolynomialRing(QQ) sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12 sage: K.<a> = NumberField(pol48) sage: L.<b> = pol48.change_ring(K).splitting_field() sage: L Number Field in b with defining polynomial x^48 ... Try all Galois groups over `\QQ` in degree 5 except for `S_5` (the latter is infeasible with the current implementation):: sage: C5pol = x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1 sage: C5pol.splitting_field('x') Number Field in x with defining polynomial x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1 sage: D10pol = x^5 - x^4 - 5*x^3 + 4*x^2 + 3*x - 1 sage: D10pol.splitting_field('x') Number Field in x with defining polynomial x^10 - 28*x^8 + 216*x^6 - 681*x^4 + 902*x^2 - 401 sage: AGL_1_5pol = x^5 - 2 sage: AGL_1_5pol.splitting_field('x') Number Field in x with defining polynomial x^20 + 10*x^19 + 55*x^18 + 210*x^17 + 595*x^16 + 1300*x^15 + 2250*x^14 + 3130*x^13 + 3585*x^12 + 3500*x^11 + 2965*x^10 + 2250*x^9 + 1625*x^8 + 1150*x^7 + 750*x^6 + 400*x^5 + 275*x^4 + 100*x^3 + 75*x^2 + 25 sage: A5pol = x^5 - x^4 + 2*x^2 - 2*x + 2 sage: A5pol.splitting_field('x') Number Field in x with defining polynomial x^60 ... We can use the ``abort_degree`` option if we don't want to compute fields of too large degree (this can be used to check whether the splitting field has small degree):: sage: (x^5+x+3).splitting_field('b', abort_degree=119) Traceback (most recent call last): ... SplittingFieldAbort: degree of splitting field equals 120 sage: (x^10+x+3).splitting_field('b', abort_degree=60) # long time (10s on sage.math, 2014) Traceback (most recent call last): ... SplittingFieldAbort: degree of splitting field is a multiple of 180 Use the ``degree_divisor`` attribute to recover the divisor of the degree of the splitting field or ``degree_multiple`` to recover a multiple:: sage: from sage.rings.number_field.splitting_field import SplittingFieldAbort sage: try: # long time (4s on sage.math, 2014) ....: (x^8+x+1).splitting_field('b', abort_degree=60, simplify=False) ....: except SplittingFieldAbort as e: ....: print(e.degree_divisor) ....: print(e.degree_multiple) 120 1440 TESTS:: sage: from sage.rings.number_field.splitting_field import splitting_field sage: splitting_field(polygen(QQ), name='x', map=True, simplify_all=True) (Number Field in x with defining polynomial x, Ring morphism: From: Rational Field To: Number Field in x with defining polynomial x Defn: 1 |--> 1) """ from sage.misc.all import cputime from sage.misc.verbose import verbose degree_multiple = Integer(degree_multiple or 0) abort_degree = Integer(abort_degree or 0) # Kpol = PARI polynomial in y defining the extension found so far F = poly.base_ring() if is_RationalField(F): Kpol = pari("'y") else: Kpol = F.pari_polynomial("y") # Fgen = the generator of F as element of Q[y]/Kpol # (only needed if map=True) if map: Fgen = F.gen().__pari__() verbose("Starting field: %s" % Kpol) # L and Lred are lists of SplittingData. # L contains polynomials which are irreducible over K, # Lred contains polynomials which need to be factored. L = [] Lred = [SplittingData(poly._pari_with_name(), degree_multiple)] # Main loop, handle polynomials one by one while True: # Absolute degree of current field K absolute_degree = Integer(Kpol.poldegree()) # Compute minimum relative degree of splitting field rel_degree_divisor = Integer(1) for splitting in L: rel_degree_divisor = rel_degree_divisor.lcm(splitting.poldegree()) # Check for early aborts abort_rel_degree = abort_degree // absolute_degree if abort_rel_degree and rel_degree_divisor > abort_rel_degree: raise SplittingFieldAbort(absolute_degree * rel_degree_divisor, degree_multiple) # First, factor polynomials in Lred and store the result in L verbose("SplittingData to factor: %s" % [s._repr_tuple() for s in Lred]) t = cputime() for splitting in Lred: m = splitting.dm.gcd(degree_multiple).gcd( factorial(splitting.poldegree())) if m == 1: continue factors = Kpol.nffactor(splitting.pol)[0] for q in factors: d = q.poldegree() fac = factorial(d) # Multiple of the degree of the splitting field of q, # note that the degree equals fac iff the Galois group is S_n. mq = m.gcd(fac) if mq == 1: continue # Multiple of the degree of the splitting field of q # over the field defined by adding square root of the # discriminant. # If the Galois group is contained in A_n, then mq_alt is # also the degree multiple over the current field K. # Here, we have equality if the Galois group is A_n. mq_alt = mq.gcd(fac // 2) # If we are over Q, then use PARI's polgalois() to compute # these degrees exactly. if absolute_degree == 1: try: G = q.polgalois() except PariError: pass else: mq = Integer(G[0]) mq_alt = mq // 2 if (G[1] == -1) else mq # In degree 4, use the cubic resolvent to refine the # degree bounds. if d == 4 and mq >= 12: # mq equals 12 or 24 # Compute cubic resolvent a0, a1, a2, a3, a4 = (q / q.pollead()).Vecrev() assert a4 == 1 cubicpol = pari([ 4 * a0 * a2 - a1 * a1 - a0 * a3 * a3, a1 * a3 - 4 * a0, -a2, 1 ]).Polrev() cubicfactors = Kpol.nffactor(cubicpol)[0] if len(cubicfactors) == 1: # A4 or S4 # After adding a root of the cubic resolvent, # the degree of the extension defined by q # is a factor 3 smaller. L.append(SplittingData(cubicpol, 3)) rel_degree_divisor = rel_degree_divisor.lcm(3) mq = mq // 3 # 4 or 8 mq_alt = 4 elif len(cubicfactors) == 2: # C4 or D8 # The irreducible degree 2 factor is # equivalent to x^2 - q.poldisc(). discpol = cubicfactors[1] L.append(SplittingData(discpol, 2)) mq = mq_alt = 4 else: # C2 x C2 mq = mq_alt = 4 if mq > mq_alt >= 3: # Add quadratic resolvent x^2 - D to decrease # the degree multiple by a factor 2. discpol = pari([-q.poldisc(), 0, 1]).Polrev() discfactors = Kpol.nffactor(discpol)[0] if len(discfactors) == 1: # Discriminant is not a square L.append(SplittingData(discpol, 2)) rel_degree_divisor = rel_degree_divisor.lcm(2) mq = mq_alt L.append(SplittingData(q, mq)) rel_degree_divisor = rel_degree_divisor.lcm(q.poldegree()) if abort_rel_degree and rel_degree_divisor > abort_rel_degree: raise SplittingFieldAbort( absolute_degree * rel_degree_divisor, degree_multiple) verbose("Done factoring", t, level=2) if len(L) == 0: # Nothing left to do break # Recompute absolute degree multiple new_degree_multiple = absolute_degree for splitting in L: new_degree_multiple *= splitting.dm degree_multiple = new_degree_multiple.gcd(degree_multiple) # Absolute degree divisor degree_divisor = rel_degree_divisor * absolute_degree # Sort according to degree to handle low degrees first L.sort(key=lambda x: x.key()) verbose("SplittingData to handle: %s" % [s._repr_tuple() for s in L]) verbose("Bounds for absolute degree: [%s, %s]" % (degree_divisor, degree_multiple)) # Check consistency if degree_multiple % degree_divisor != 0: raise ValueError( "inconsistent degree_multiple in splitting_field()") for splitting in L: # The degree of the splitting field must be a multiple of # the degree of the polynomial. Only do this check for # SplittingData with minimal dm, because the higher dm are # defined as relative degree over the splitting field of # the polynomials with lesser dm. if splitting.dm > L[0].dm: break if splitting.dm % splitting.poldegree() != 0: raise ValueError( "inconsistent degree_multiple in splitting_field()") # Add a root of f = L[0] to construct the field N = K[x]/f(x) splitting = L[0] f = splitting.pol verbose("Handling polynomial %s" % (f.lift()), level=2) t = cputime() Npol, KtoN, k = Kpol.rnfequation(f, flag=1) # Make Npol monic integral primitive, store in Mpol # (after this, we don't need Npol anymore, only Mpol) Mdiv = pari(1) Mpol = Npol while True: denom = Integer(Mpol.pollead()) if denom == 1: break denom = pari(denom.factor().radical_value()) Mpol = (Mpol * (denom**Mpol.poldegree())).subst( "x", pari([0, 1 / denom]).Polrev("x")) Mpol /= Mpol.content() Mdiv *= denom # We are finished for sure if we hit the degree bound finished = (Mpol.poldegree() >= degree_multiple) if simplify_all or (simplify and not finished): # Find a simpler defining polynomial Lpol for Mpol verbose("New field before simplifying: %s" % Mpol, t) t = cputime() M = Mpol.polred(flag=3) n = len(M[0]) - 1 Lpol = M[1][n].change_variable_name("y") LtoM = M[0][n].change_variable_name("y").Mod( Mpol.change_variable_name("y")) MtoL = LtoM.modreverse() else: # Lpol = Mpol Lpol = Mpol.change_variable_name("y") MtoL = pari("'y") NtoL = MtoL / Mdiv KtoL = KtoN.lift().subst("x", NtoL).Mod(Lpol) Kpol = Lpol # New Kpol (for next iteration) verbose("New field: %s" % Kpol, t) if map: t = cputime() Fgen = Fgen.lift().subst("y", KtoL) verbose("Computed generator of F in K", t, level=2) if finished: break t = cputime() # Convert f and elements of L from K to L and store in L # (if the polynomial is certain to remain irreducible) or Lred. Lold = L[1:] L = [] Lred = [] # First add f divided by the linear factor we obtained, # mg is the new degree multiple. mg = splitting.dm // f.poldegree() if mg > 1: g = [c.subst("y", KtoL).Mod(Lpol) for c in f.Vecrev().lift()] g = pari(g).Polrev() g /= pari([k * KtoL - NtoL, 1]).Polrev() # divide linear factor Lred.append(SplittingData(g, mg)) for splitting in Lold: g = [c.subst("y", KtoL) for c in splitting.pol.Vecrev().lift()] g = pari(g).Polrev() mg = splitting.dm if Integer(g.poldegree()).gcd( f.poldegree()) == 1: # linearly disjoint fields L.append(SplittingData(g, mg)) else: Lred.append(SplittingData(g, mg)) verbose("Converted polynomials to new field", t, level=2) # Convert Kpol to Sage and construct the absolute number field Kpol = PolynomialRing(RationalField(), name=poly.variable_name())(Kpol / Kpol.pollead()) K = NumberField(Kpol, name) if map: return K, F.hom(Fgen, K) else: return K
def points(self, **kwds): """ Return some or all rational points of a projective scheme. Over a finite field, all points are returned. Over an infinite field, all points satisfying the bound are returned. For a zero-dimensional subscheme, all points are returned regardless of whether the base ring is a field or not. For number fields, this uses the Doyle-Krumm algorithm 4 (algorithm 5 for imaginary quadratic) for computing algebraic numbers up to a given height [Doyle-Krumm]_. The algorithm requires floating point arithmetic, so the user is allowed to specify the precision for such calculations. Additionally, due to floating point issues, points slightly larger than the bound may be returned. This can be controlled by lowering the tolerance. INPUT: - ``bound`` - a real number - ``tolerance`` - a rational number in (0,1] used in doyle-krumm algorithm-4 - ``precision`` - the precision to use for computing the elements of bounded height of number fields. OUTPUT: - a list of rational points of a projective scheme EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: P(QQ).points(bound=4) [(-4 : 1), (-3 : 1), (-2 : 1), (-3/2 : 1), (-4/3 : 1), (-1 : 1), (-3/4 : 1), (-2/3 : 1), (-1/2 : 1), (-1/3 : 1), (-1/4 : 1), (0 : 1), (1/4 : 1), (1/3 : 1), (1/2 : 1), (2/3 : 1), (3/4 : 1), (1 : 0), (1 : 1), (4/3 : 1), (3/2 : 1), (2 : 1), (3 : 1), (4 : 1)] :: sage: u = QQ['u'].0 sage: K.<v> = NumberField(u^2 + 3) sage: P.<x,y,z> = ProjectiveSpace(K,2) sage: len(P(K).points(bound=1.8)) 381 :: sage: P1 = ProjectiveSpace(GF(2),1) sage: F.<a> = GF(4,'a') sage: P1(F).points() [(0 : 1), (1 : 0), (1 : 1), (a : 1), (a + 1 : 1)] :: sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: E = P.subscheme([(y^3-y*z^2) - (x^3-x*z^2),(y^3-y*z^2) + (x^3-x*z^2)]) sage: E(P.base_ring()).points() [(-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 : 1)] """ B = kwds.pop('bound', 0) tol = kwds.pop('tolerance', 1e-2) prec = kwds.pop('precision', 53) X = self.codomain() from sage.schemes.projective.projective_space import is_ProjectiveSpace if not is_ProjectiveSpace(X) and X.base_ring() in Fields(): #Then it must be a subscheme dim_ideal = X.defining_ideal().dimension() if dim_ideal < 1: # no points return [] if dim_ideal == 1: # if X zero-dimensional rat_points = set() PS = X.ambient_space() N = PS.dimension_relative() BR = X.base_ring() #need a lexicographic ordering for elimination R = PolynomialRing(BR, N + 1, PS.variable_names(), order='lex') I = R.ideal(X.defining_polynomials()) I0 = R.ideal(0) #Determine the points through elimination #This is much faster than using the I.variety() function on each affine chart. for k in range(N + 1): #create the elimination ideal for the kth affine patch G = I.substitute({R.gen(k): 1}).groebner_basis() if G != [1]: P = {} #keep track that we know the kth coordinate is 1 P.update({R.gen(k): 1}) points = [P] #work backwards from solving each equation for the possible #values of the next coordinate for i in range(len(G) - 1, -1, -1): new_points = [] good = 0 for P in points: #substitute in our dictionary entry that has the values #of coordinates known so far. This results in a single #variable polynomial (by elimination) L = G[i].substitute(P) if L != 0: L = L.factor() #the linear factors give the possible rational values of #this coordinate for pol, pow in L: if pol.degree() == 1 and len( pol.variables()) == 1: good = 1 r = pol.variables()[0] varindex = R.gens().index(r) #add this coordinates information to #each dictionary entry P.update({ R.gen(varindex): -pol.constant_coefficient() / pol.monomial_coefficient(r) }) new_points.append(copy(P)) else: new_points.append(P) good = 1 if good: points = new_points #the dictionary entries now have values for all coordinates #they are the rational solutions to the equations #make them into projective points for i in range(len(points)): if len(points[i]) == N + 1 and I.subs( points[i]) == I0: S = X([ points[i][R.gen(j)] for j in range(N + 1) ]) S.normalize_coordinates() rat_points.add(S) rat_points = sorted(rat_points) return rat_points R = self.value_ring() if is_RationalField(R): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified" % B) if isinstance(X, AlgebraicScheme_subscheme ): # sieve should only be called for subschemes from sage.schemes.projective.projective_rational_point import sieve return sieve(X, B) else: from sage.schemes.projective.projective_rational_point import enum_projective_rational_field return enum_projective_rational_field(self, B) elif R in NumberFields(): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified" % B) from sage.schemes.projective.projective_rational_point import enum_projective_number_field return enum_projective_number_field(self, bound=B, tolerance=tol, precision=prec) elif is_FiniteField(R): from sage.schemes.projective.projective_rational_point import enum_projective_finite_field return enum_projective_finite_field(self.extended_codomain()) else: raise TypeError("unable to enumerate points over %s" % R)
def splitting_field(poly, name, map=False, degree_multiple=None, abort_degree=None, simplify=True, simplify_all=False): """ Compute the splitting field of a given polynomial, defined over a number field. INPUT: - ``poly`` -- a monic polynomial over a number field - ``name`` -- a variable name for the number field - ``map`` -- (default: ``False``) also return an embedding of ``poly`` into the resulting field. Note that computing this embedding might be expensive. - ``degree_multiple`` -- a multiple of the absolute degree of the splitting field. If ``degree_multiple`` equals the actual degree, this can enormously speed up the computation. - ``abort_degree`` -- abort by raising a :class:`SplittingFieldAbort` if it can be determined that the absolute degree of the splitting field is strictly larger than ``abort_degree``. - ``simplify`` -- (default: ``True``) during the algorithm, try to find a simpler defining polynomial for the intermediate number fields using PARI's ``polred()``. This usually speeds up the computation but can also considerably slow it down. Try and see what works best in the given situation. - ``simplify_all`` -- (default: ``False``) If ``True``, simplify intermediate fields and also the resulting number field. OUTPUT: If ``map`` is ``False``, the splitting field as an absolute number field. If ``map`` is ``True``, a tuple ``(K, phi)`` where ``phi`` is an embedding of the base field in ``K``. EXAMPLES:: sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = (x^3 + 2).splitting_field(); K Number Field in a with defining polynomial x^6 + 3*x^5 + 6*x^4 + 11*x^3 + 12*x^2 - 3*x + 1 sage: K.<a> = (x^3 - 3*x + 1).splitting_field(); K Number Field in a with defining polynomial x^3 - 3*x + 1 The ``simplify`` and ``simplify_all`` flags usually yield fields defined by polynomials with smaller coefficients. By default, ``simplify`` is True and ``simplify_all`` is False. :: sage: (x^4 - x + 1).splitting_field('a', simplify=False) Number Field in a with defining polynomial x^24 - 2780*x^22 + 2*x^21 + 3527512*x^20 - 2876*x^19 - 2701391985*x^18 + 945948*x^17 + 1390511639677*x^16 + 736757420*x^15 - 506816498313560*x^14 - 822702898220*x^13 + 134120588299548463*x^12 + 362240696528256*x^11 - 25964582366880639486*x^10 - 91743672243419990*x^9 + 3649429473447308439427*x^8 + 14310332927134072336*x^7 - 363192569823568746892571*x^6 - 1353403793640477725898*x^5 + 24293393281774560140427565*x^4 + 70673814899934142357628*x^3 - 980621447508959243128437933*x^2 - 1539841440617805445432660*x + 18065914012013502602456565991 sage: (x^4 - x + 1).splitting_field('a', simplify=True) Number Field in a with defining polynomial x^24 + 8*x^23 - 32*x^22 - 310*x^21 + 540*x^20 + 4688*x^19 - 6813*x^18 - 32380*x^17 + 49525*x^16 + 102460*x^15 - 129944*x^14 - 287884*x^13 + 372727*x^12 + 150624*x^11 - 110530*x^10 - 566926*x^9 + 1062759*x^8 - 779940*x^7 + 863493*x^6 - 1623578*x^5 + 1759513*x^4 - 955624*x^3 + 459975*x^2 - 141948*x + 53919 sage: (x^4 - x + 1).splitting_field('a', simplify_all=True) Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1 Reducible polynomials also work:: sage: pol = (x^4 - 1)*(x^2 + 1/2)*(x^2 + 1/3) sage: pol.splitting_field('a', simplify_all=True) Number Field in a with defining polynomial x^8 - x^4 + 1 Relative situation:: sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = NumberField(x^3 + 2) sage: S.<t> = PolynomialRing(K) sage: L.<b> = (t^2 - a).splitting_field() sage: L Number Field in b with defining polynomial t^6 + 2 With ``map=True``, we also get the embedding of the base field into the splitting field:: sage: L.<b>, phi = (t^2 - a).splitting_field(map=True) sage: phi Ring morphism: From: Number Field in a with defining polynomial x^3 + 2 To: Number Field in b with defining polynomial t^6 + 2 Defn: a |--> b^2 sage: (x^4 - x + 1).splitting_field('a', simplify_all=True, map=True)[1] Ring morphism: From: Rational Field To: Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1 Defn: 1 |--> 1 We can enable verbose messages:: sage: set_verbose(2) sage: K.<a> = (x^3 - x + 1).splitting_field() verbose 1 (...: splitting_field.py, splitting_field) Starting field: y verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: [(3, 0)] verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(2, 2), (3, 3)] verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6] verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^2 + 23 verbose 1 (...: splitting_field.py, splitting_field) New field before simplifying: x^2 + 23 (time = ...) verbose 1 (...: splitting_field.py, splitting_field) New field: y^2 - y + 6 (time = ...) verbose 2 (...: splitting_field.py, splitting_field) Converted polynomials to new field (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: [] verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(3, 3)] verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6] verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^3 - x + 1 verbose 1 (...: splitting_field.py, splitting_field) New field: y^6 + 3*y^5 + 19*y^4 + 35*y^3 + 127*y^2 + 73*y + 271 (time = ...) sage: set_verbose(0) Try all Galois groups in degree 4. We use a quadratic base field such that ``polgalois()`` cannot be used:: sage: R.<x> = PolynomialRing(QuadraticField(-11)) sage: C2C2pol = x^4 - 10*x^2 + 1 sage: C2C2pol.splitting_field('x') Number Field in x with defining polynomial x^8 + 24*x^6 + 608*x^4 + 9792*x^2 + 53824 sage: C4pol = x^4 + x^3 + x^2 + x + 1 sage: C4pol.splitting_field('x') Number Field in x with defining polynomial x^8 - x^7 - 2*x^6 + 5*x^5 + x^4 + 15*x^3 - 18*x^2 - 27*x + 81 sage: D8pol = x^4 - 2 sage: D8pol.splitting_field('x') Number Field in x with defining polynomial x^16 + 8*x^15 + 68*x^14 + 336*x^13 + 1514*x^12 + 5080*x^11 + 14912*x^10 + 35048*x^9 + 64959*x^8 + 93416*x^7 + 88216*x^6 + 41608*x^5 - 25586*x^4 - 60048*x^3 - 16628*x^2 + 12008*x + 34961 sage: A4pol = x^4 - 4*x^3 + 14*x^2 - 28*x + 21 sage: A4pol.splitting_field('x') Number Field in x with defining polynomial x^24 - 20*x^23 + 290*x^22 - 3048*x^21 + 26147*x^20 - 186132*x^19 + 1130626*x^18 - 5913784*x^17 + 26899345*x^16 - 106792132*x^15 + 371066538*x^14 - 1127792656*x^13 + 2991524876*x^12 - 6888328132*x^11 + 13655960064*x^10 - 23000783036*x^9 + 32244796382*x^8 - 36347834476*x^7 + 30850889884*x^6 - 16707053128*x^5 + 1896946429*x^4 + 4832907884*x^3 - 3038258802*x^2 - 200383596*x + 593179173 sage: S4pol = x^4 + x + 1 sage: S4pol.splitting_field('x') Number Field in x with defining polynomial x^48 ... Some bigger examples:: sage: R.<x> = PolynomialRing(QQ) sage: pol15 = chebyshev_T(31, x) - 1 # 2^30*(x-1)*minpoly(cos(2*pi/31))^2 sage: pol15.splitting_field('a') Number Field in a with defining polynomial x^15 - x^14 - 14*x^13 + 13*x^12 + 78*x^11 - 66*x^10 - 220*x^9 + 165*x^8 + 330*x^7 - 210*x^6 - 252*x^5 + 126*x^4 + 84*x^3 - 28*x^2 - 8*x + 1 sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12 sage: pol48.splitting_field('a') Number Field in a with defining polynomial x^48 ... If you somehow know the degree of the field in advance, you should add a ``degree_multiple`` argument. This can speed up the computation, in particular for polynomials of degree >= 12 or for relative extensions:: sage: pol15.splitting_field('a', degree_multiple=15) Number Field in a with defining polynomial x^15 + x^14 - 14*x^13 - 13*x^12 + 78*x^11 + 66*x^10 - 220*x^9 - 165*x^8 + 330*x^7 + 210*x^6 - 252*x^5 - 126*x^4 + 84*x^3 + 28*x^2 - 8*x - 1 A value for ``degree_multiple`` which isn't actually a multiple of the absolute degree of the splitting field can either result in a wrong answer or the following exception:: sage: pol48.splitting_field('a', degree_multiple=20) Traceback (most recent call last): ... ValueError: inconsistent degree_multiple in splitting_field() Compute the Galois closure as the splitting field of the defining polynomial:: sage: R.<x> = PolynomialRing(QQ) sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12 sage: K.<a> = NumberField(pol48) sage: L.<b> = pol48.change_ring(K).splitting_field() sage: L Number Field in b with defining polynomial x^48 ... Try all Galois groups over `\QQ` in degree 5 except for `S_5` (the latter is infeasible with the current implementation):: sage: C5pol = x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1 sage: C5pol.splitting_field('x') Number Field in x with defining polynomial x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1 sage: D10pol = x^5 - x^4 - 5*x^3 + 4*x^2 + 3*x - 1 sage: D10pol.splitting_field('x') Number Field in x with defining polynomial x^10 - 28*x^8 + 216*x^6 - 681*x^4 + 902*x^2 - 401 sage: AGL_1_5pol = x^5 - 2 sage: AGL_1_5pol.splitting_field('x') Number Field in x with defining polynomial x^20 + 10*x^19 + 55*x^18 + 210*x^17 + 595*x^16 + 1300*x^15 + 2250*x^14 + 3130*x^13 + 3585*x^12 + 3500*x^11 + 2965*x^10 + 2250*x^9 + 1625*x^8 + 1150*x^7 + 750*x^6 + 400*x^5 + 275*x^4 + 100*x^3 + 75*x^2 + 25 sage: A5pol = x^5 - x^4 + 2*x^2 - 2*x + 2 sage: A5pol.splitting_field('x') Number Field in x with defining polynomial x^60 ... We can use the ``abort_degree`` option if we don't want to compute fields of too large degree (this can be used to check whether the splitting field has small degree):: sage: (x^5+x+3).splitting_field('b', abort_degree=119) Traceback (most recent call last): ... SplittingFieldAbort: degree of splitting field equals 120 sage: (x^10+x+3).splitting_field('b', abort_degree=60) # long time (10s on sage.math, 2014) Traceback (most recent call last): ... SplittingFieldAbort: degree of splitting field is a multiple of 180 Use the ``degree_divisor`` attribute to recover the divisor of the degree of the splitting field or ``degree_multiple`` to recover a multiple:: sage: from sage.rings.number_field.splitting_field import SplittingFieldAbort sage: try: # long time (4s on sage.math, 2014) ....: (x^8+x+1).splitting_field('b', abort_degree=60, simplify=False) ....: except SplittingFieldAbort as e: ....: print(e.degree_divisor) ....: print(e.degree_multiple) 120 1440 TESTS:: sage: from sage.rings.number_field.splitting_field import splitting_field sage: splitting_field(polygen(QQ), name='x', map=True, simplify_all=True) (Number Field in x with defining polynomial x, Ring morphism: From: Rational Field To: Number Field in x with defining polynomial x Defn: 1 |--> 1) """ from sage.misc.all import verbose, cputime degree_multiple = Integer(degree_multiple or 0) abort_degree = Integer(abort_degree or 0) # Kpol = PARI polynomial in y defining the extension found so far F = poly.base_ring() if is_RationalField(F): Kpol = pari("'y") else: Kpol = F.pari_polynomial("y") # Fgen = the generator of F as element of Q[y]/Kpol # (only needed if map=True) if map: Fgen = F.gen().__pari__() verbose("Starting field: %s"%Kpol) # L and Lred are lists of SplittingData. # L contains polynomials which are irreducible over K, # Lred contains polynomials which need to be factored. L = [] Lred = [SplittingData(poly._pari_with_name(), degree_multiple)] # Main loop, handle polynomials one by one while True: # Absolute degree of current field K absolute_degree = Integer(Kpol.poldegree()) # Compute minimum relative degree of splitting field rel_degree_divisor = Integer(1) for splitting in L: rel_degree_divisor = rel_degree_divisor.lcm(splitting.poldegree()) # Check for early aborts abort_rel_degree = abort_degree//absolute_degree if abort_rel_degree and rel_degree_divisor > abort_rel_degree: raise SplittingFieldAbort(absolute_degree * rel_degree_divisor, degree_multiple) # First, factor polynomials in Lred and store the result in L verbose("SplittingData to factor: %s"%[s._repr_tuple() for s in Lred]) t = cputime() for splitting in Lred: m = splitting.dm.gcd(degree_multiple).gcd(factorial(splitting.poldegree())) if m == 1: continue factors = Kpol.nffactor(splitting.pol)[0] for q in factors: d = q.poldegree() fac = factorial(d) # Multiple of the degree of the splitting field of q, # note that the degree equals fac iff the Galois group is S_n. mq = m.gcd(fac) if mq == 1: continue # Multiple of the degree of the splitting field of q # over the field defined by adding square root of the # discriminant. # If the Galois group is contained in A_n, then mq_alt is # also the degree multiple over the current field K. # Here, we have equality if the Galois group is A_n. mq_alt = mq.gcd(fac//2) # If we are over Q, then use PARI's polgalois() to compute # these degrees exactly. if absolute_degree == 1: try: G = q.polgalois() except PariError: pass else: mq = Integer(G[0]) mq_alt = mq//2 if (G[1] == -1) else mq # In degree 4, use the cubic resolvent to refine the # degree bounds. if d == 4 and mq >= 12: # mq equals 12 or 24 # Compute cubic resolvent a0, a1, a2, a3, a4 = (q/q.pollead()).Vecrev() assert a4 == 1 cubicpol = pari([4*a0*a2 - a1*a1 -a0*a3*a3, a1*a3 - 4*a0, -a2, 1]).Polrev() cubicfactors = Kpol.nffactor(cubicpol)[0] if len(cubicfactors) == 1: # A4 or S4 # After adding a root of the cubic resolvent, # the degree of the extension defined by q # is a factor 3 smaller. L.append(SplittingData(cubicpol, 3)) rel_degree_divisor = rel_degree_divisor.lcm(3) mq = mq//3 # 4 or 8 mq_alt = 4 elif len(cubicfactors) == 2: # C4 or D8 # The irreducible degree 2 factor is # equivalent to x^2 - q.poldisc(). discpol = cubicfactors[1] L.append(SplittingData(discpol, 2)) mq = mq_alt = 4 else: # C2 x C2 mq = mq_alt = 4 if mq > mq_alt >= 3: # Add quadratic resolvent x^2 - D to decrease # the degree multiple by a factor 2. discpol = pari([-q.poldisc(), 0, 1]).Polrev() discfactors = Kpol.nffactor(discpol)[0] if len(discfactors) == 1: # Discriminant is not a square L.append(SplittingData(discpol, 2)) rel_degree_divisor = rel_degree_divisor.lcm(2) mq = mq_alt L.append(SplittingData(q, mq)) rel_degree_divisor = rel_degree_divisor.lcm(q.poldegree()) if abort_rel_degree and rel_degree_divisor > abort_rel_degree: raise SplittingFieldAbort(absolute_degree * rel_degree_divisor, degree_multiple) verbose("Done factoring", t, level=2) if len(L) == 0: # Nothing left to do break # Recompute absolute degree multiple new_degree_multiple = absolute_degree for splitting in L: new_degree_multiple *= splitting.dm degree_multiple = new_degree_multiple.gcd(degree_multiple) # Absolute degree divisor degree_divisor = rel_degree_divisor * absolute_degree # Sort according to degree to handle low degrees first L.sort(key=lambda x: x.key()) verbose("SplittingData to handle: %s"%[s._repr_tuple() for s in L]) verbose("Bounds for absolute degree: [%s, %s]"%(degree_divisor,degree_multiple)) # Check consistency if degree_multiple % degree_divisor != 0: raise ValueError("inconsistent degree_multiple in splitting_field()") for splitting in L: # The degree of the splitting field must be a multiple of # the degree of the polynomial. Only do this check for # SplittingData with minimal dm, because the higher dm are # defined as relative degree over the splitting field of # the polynomials with lesser dm. if splitting.dm > L[0].dm: break if splitting.dm % splitting.poldegree() != 0: raise ValueError("inconsistent degree_multiple in splitting_field()") # Add a root of f = L[0] to construct the field N = K[x]/f(x) splitting = L[0] f = splitting.pol verbose("Handling polynomial %s"%(f.lift()), level=2) t = cputime() Npol, KtoN, k = Kpol.rnfequation(f, flag=1) # Make Npol monic integral primitive, store in Mpol # (after this, we don't need Npol anymore, only Mpol) Mdiv = pari(1) Mpol = Npol while True: denom = Integer(Mpol.pollead()) if denom == 1: break denom = pari(denom.factor().radical_value()) Mpol = (Mpol*(denom**Mpol.poldegree())).subst("x", pari([0,1/denom]).Polrev("x")) Mpol /= Mpol.content() Mdiv *= denom # We are finished for sure if we hit the degree bound finished = (Mpol.poldegree() >= degree_multiple) if simplify_all or (simplify and not finished): # Find a simpler defining polynomial Lpol for Mpol verbose("New field before simplifying: %s"%Mpol, t) t = cputime() M = Mpol.polred(flag=3) n = len(M[0])-1 Lpol = M[1][n].change_variable_name("y") LtoM = M[0][n].change_variable_name("y").Mod(Mpol.change_variable_name("y")) MtoL = LtoM.modreverse() else: # Lpol = Mpol Lpol = Mpol.change_variable_name("y") MtoL = pari("'y") NtoL = MtoL/Mdiv KtoL = KtoN.lift().subst("x", NtoL).Mod(Lpol) Kpol = Lpol # New Kpol (for next iteration) verbose("New field: %s"%Kpol, t) if map: t = cputime() Fgen = Fgen.lift().subst("y", KtoL) verbose("Computed generator of F in K", t, level=2) if finished: break t = cputime() # Convert f and elements of L from K to L and store in L # (if the polynomial is certain to remain irreducible) or Lred. Lold = L[1:] L = [] Lred = [] # First add f divided by the linear factor we obtained, # mg is the new degree multiple. mg = splitting.dm//f.poldegree() if mg > 1: g = [c.subst("y", KtoL).Mod(Lpol) for c in f.Vecrev().lift()] g = pari(g).Polrev() g /= pari([k*KtoL - NtoL, 1]).Polrev() # divide linear factor Lred.append(SplittingData(g, mg)) for splitting in Lold: g = [c.subst("y", KtoL) for c in splitting.pol.Vecrev().lift()] g = pari(g).Polrev() mg = splitting.dm if Integer(g.poldegree()).gcd(f.poldegree()) == 1: # linearly disjoint fields L.append(SplittingData(g, mg)) else: Lred.append(SplittingData(g, mg)) verbose("Converted polynomials to new field", t, level=2) # Convert Kpol to Sage and construct the absolute number field Kpol = PolynomialRing(RationalField(), name=poly.variable_name())(Kpol/Kpol.pollead()) K = NumberField(Kpol, name) if map: return K, F.hom(Fgen, K) else: return K
def EllipticCurve(x=None, y=None, j=None, minimal_twist=True): r""" Construct an elliptic curve. In Sage, an elliptic curve is always specified by its a-invariants .. math:: y^2 + a_1 xy + a_3 y = x^3 + a_2 x^2 + a_4 x + a_6. INPUT: There are several ways to construct an elliptic curve: - ``EllipticCurve([a1,a2,a3,a4,a6])``: Elliptic curve with given a-invariants. The invariants are coerced into the parent of the first element. If all are integers, they are coerced into the rational numbers. - ``EllipticCurve([a4,a6])``: Same as above, but `a_1=a_2=a_3=0`. - ``EllipticCurve(label)``: Returns the elliptic curve over Q from the Cremona database with the given label. The label is a string, such as ``"11a"`` or ``"37b2"``. The letters in the label *must* be lower case (Cremona's new labeling). - ``EllipticCurve(R, [a1,a2,a3,a4,a6])``: Create the elliptic curve over ``R`` with given a-invariants. Here ``R`` can be an arbitrary ring. Note that addition need not be defined. - ``EllipticCurve(j=j0)`` or ``EllipticCurve_from_j(j0)``: Return an elliptic curve with j-invariant ``j0``. - ``EllipticCurve(polynomial)``: Read off the a-invariants from the polynomial coefficients, see :func:`EllipticCurve_from_Weierstrass_polynomial`. In each case above where the input is a list of length 2 or 5, one can instead give a 2 or 5-tuple instead. EXAMPLES: We illustrate creating elliptic curves:: sage: EllipticCurve([0,0,1,-1,0]) Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field We create a curve from a Cremona label:: sage: EllipticCurve('37b2') Elliptic Curve defined by y^2 + y = x^3 + x^2 - 1873*x - 31833 over Rational Field sage: EllipticCurve('5077a') Elliptic Curve defined by y^2 + y = x^3 - 7*x + 6 over Rational Field sage: EllipticCurve('389a') Elliptic Curve defined by y^2 + y = x^3 + x^2 - 2*x over Rational Field Old Cremona labels are allowed:: sage: EllipticCurve('2400FF') Elliptic Curve defined by y^2 = x^3 + x^2 + 2*x + 8 over Rational Field Unicode labels are allowed:: sage: EllipticCurve(u'389a') Elliptic Curve defined by y^2 + y = x^3 + x^2 - 2*x over Rational Field We create curves over a finite field as follows:: sage: EllipticCurve([GF(5)(0),0,1,-1,0]) Elliptic Curve defined by y^2 + y = x^3 + 4*x over Finite Field of size 5 sage: EllipticCurve(GF(5), [0, 0,1,-1,0]) Elliptic Curve defined by y^2 + y = x^3 + 4*x over Finite Field of size 5 Elliptic curves over `\ZZ/N\ZZ` with `N` prime are of type "elliptic curve over a finite field":: sage: F = Zmod(101) sage: EllipticCurve(F, [2, 3]) Elliptic Curve defined by y^2 = x^3 + 2*x + 3 over Ring of integers modulo 101 sage: E = EllipticCurve([F(2), F(3)]) sage: type(E) <class 'sage.schemes.elliptic_curves.ell_finite_field.EllipticCurve_finite_field_with_category'> sage: E.category() Category of schemes over Ring of integers modulo 101 In contrast, elliptic curves over `\ZZ/N\ZZ` with `N` composite are of type "generic elliptic curve":: sage: F = Zmod(95) sage: EllipticCurve(F, [2, 3]) Elliptic Curve defined by y^2 = x^3 + 2*x + 3 over Ring of integers modulo 95 sage: E = EllipticCurve([F(2), F(3)]) sage: type(E) <class 'sage.schemes.elliptic_curves.ell_generic.EllipticCurve_generic_with_category'> sage: E.category() Category of schemes over Ring of integers modulo 95 The following is a curve over the complex numbers:: sage: E = EllipticCurve(CC, [0,0,1,-1,0]) sage: E Elliptic Curve defined by y^2 + 1.00000000000000*y = x^3 + (-1.00000000000000)*x over Complex Field with 53 bits of precision sage: E.j_invariant() 2988.97297297297 We can also create elliptic curves by giving the Weierstrass equation:: sage: x, y = var('x,y') sage: EllipticCurve(y^2 + y == x^3 + x - 9) Elliptic Curve defined by y^2 + y = x^3 + x - 9 over Rational Field sage: R.<x,y> = GF(5)[] sage: EllipticCurve(x^3 + x^2 + 2 - y^2 - y*x) Elliptic Curve defined by y^2 + x*y = x^3 + x^2 + 2 over Finite Field of size 5 We can explicitly specify the `j`-invariant:: sage: E = EllipticCurve(j=1728); E; E.j_invariant(); E.label() Elliptic Curve defined by y^2 = x^3 - x over Rational Field 1728 '32a2' sage: E = EllipticCurve(j=GF(5)(2)); E; E.j_invariant() Elliptic Curve defined by y^2 = x^3 + x + 1 over Finite Field of size 5 2 See :trac:`6657` :: sage: EllipticCurve(GF(144169),j=1728) Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 144169 By default, when a rational value of `j` is given, the constructed curve is a minimal twist (minimal conductor for curves with that `j`-invariant). This can be changed by setting the optional parameter ``minimal_twist``, which is True by default, to False:: sage: EllipticCurve(j=100) Elliptic Curve defined by y^2 = x^3 + x^2 + 3392*x + 307888 over Rational Field sage: E =EllipticCurve(j=100); E Elliptic Curve defined by y^2 = x^3 + x^2 + 3392*x + 307888 over Rational Field sage: E.conductor() 33129800 sage: E.j_invariant() 100 sage: E =EllipticCurve(j=100, minimal_twist=False); E Elliptic Curve defined by y^2 = x^3 + 488400*x - 530076800 over Rational Field sage: E.conductor() 298168200 sage: E.j_invariant() 100 Without this option, constructing the curve could take a long time since both `j` and `j-1728` have to be factored to compute the minimal twist (see :trac:`13100`):: sage: E = EllipticCurve_from_j(2^256+1,minimal_twist=False) sage: E.j_invariant() == 2^256+1 True TESTS:: sage: R = ZZ['u', 'v'] sage: EllipticCurve(R, [1,1]) Elliptic Curve defined by y^2 = x^3 + x + 1 over Multivariate Polynomial Ring in u, v over Integer Ring We create a curve and a point over QQbar (see #6879):: sage: E = EllipticCurve(QQbar,[0,1]) sage: E(0) (0 : 1 : 0) sage: E.base_field() Algebraic Field sage: E = EllipticCurve(RR,[1,2]); E; E.base_field() Elliptic Curve defined by y^2 = x^3 + 1.00000000000000*x + 2.00000000000000 over Real Field with 53 bits of precision Real Field with 53 bits of precision sage: EllipticCurve(CC,[3,4]); E; E.base_field() Elliptic Curve defined by y^2 = x^3 + 3.00000000000000*x + 4.00000000000000 over Complex Field with 53 bits of precision Elliptic Curve defined by y^2 = x^3 + 1.00000000000000*x + 2.00000000000000 over Real Field with 53 bits of precision Real Field with 53 bits of precision sage: E = EllipticCurve(QQbar,[5,6]); E; E.base_field() Elliptic Curve defined by y^2 = x^3 + 5*x + 6 over Algebraic Field Algebraic Field See :trac:`6657` :: sage: EllipticCurve(3,j=1728) Traceback (most recent call last): ... ValueError: First parameter (if present) must be a ring when j is specified sage: EllipticCurve(GF(5),j=3/5) Traceback (most recent call last): ... ValueError: First parameter must be a ring containing 3/5 If the universe of the coefficients is a general field, the object constructed has type EllipticCurve_field. Otherwise it is EllipticCurve_generic. See :trac:`9816` :: sage: E = EllipticCurve([QQbar(1),3]); E Elliptic Curve defined by y^2 = x^3 + x + 3 over Algebraic Field sage: type(E) <class 'sage.schemes.elliptic_curves.ell_field.EllipticCurve_field_with_category'> sage: E = EllipticCurve([RR(1),3]); E Elliptic Curve defined by y^2 = x^3 + 1.00000000000000*x + 3.00000000000000 over Real Field with 53 bits of precision sage: type(E) <class 'sage.schemes.elliptic_curves.ell_field.EllipticCurve_field_with_category'> sage: E = EllipticCurve([i,i]); E Elliptic Curve defined by y^2 = x^3 + I*x + I over Symbolic Ring sage: type(E) <class 'sage.schemes.elliptic_curves.ell_field.EllipticCurve_field_with_category'> sage: E.category() Category of schemes over Symbolic Ring sage: SR in Fields() True sage: F = FractionField(PolynomialRing(QQ,'t')) sage: t = F.gen() sage: E = EllipticCurve([t,0]); E Elliptic Curve defined by y^2 = x^3 + t*x over Fraction Field of Univariate Polynomial Ring in t over Rational Field sage: type(E) <class 'sage.schemes.elliptic_curves.ell_field.EllipticCurve_field_with_category'> sage: E.category() Category of schemes over Fraction Field of Univariate Polynomial Ring in t over Rational Field See :trac:`12517`:: sage: E = EllipticCurve([1..5]) sage: EllipticCurve(E.a_invariants()) Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Rational Field See :trac:`11773`:: sage: E = EllipticCurve() Traceback (most recent call last): ... TypeError: invalid input to EllipticCurve constructor """ import ell_generic, ell_field, ell_finite_field, ell_number_field, ell_rational_field, ell_padic_field # here to avoid circular includes if j is not None: if not x is None: if is_Ring(x): try: j = x(j) except (ZeroDivisionError, ValueError, TypeError): raise ValueError, "First parameter must be a ring containing %s"%j else: raise ValueError, "First parameter (if present) must be a ring when j is specified" return EllipticCurve_from_j(j, minimal_twist) if x is None: raise TypeError, "invalid input to EllipticCurve constructor" if is_SymbolicEquation(x): x = x.lhs() - x.rhs() if parent(x) is SR: x = x._polynomial_(rings.QQ['x', 'y']) if is_MPolynomial(x): if y is None: return EllipticCurve_from_Weierstrass_polynomial(x) else: return EllipticCurve_from_cubic(x, y, morphism=False) if is_Ring(x): if is_RationalField(x): return ell_rational_field.EllipticCurve_rational_field(x, y) elif is_FiniteField(x) or (is_IntegerModRing(x) and x.characteristic().is_prime()): return ell_finite_field.EllipticCurve_finite_field(x, y) elif rings.is_pAdicField(x): return ell_padic_field.EllipticCurve_padic_field(x, y) elif is_NumberField(x): return ell_number_field.EllipticCurve_number_field(x, y) elif x in _Fields: return ell_field.EllipticCurve_field(x, y) return ell_generic.EllipticCurve_generic(x, y) if isinstance(x, unicode): x = str(x) if isinstance(x, basestring): return ell_rational_field.EllipticCurve_rational_field(x) if is_RingElement(x) and y is None: raise TypeError, "invalid input to EllipticCurve constructor" if not isinstance(x, (list, tuple)): raise TypeError, "invalid input to EllipticCurve constructor" x = Sequence(x) if not (len(x) in [2,5]): raise ValueError, "sequence of coefficients must have length 2 or 5" R = x.universe() if isinstance(x[0], (rings.Rational, rings.Integer, int, long)): return ell_rational_field.EllipticCurve_rational_field(x, y) elif is_NumberField(R): return ell_number_field.EllipticCurve_number_field(x, y) elif rings.is_pAdicField(R): return ell_padic_field.EllipticCurve_padic_field(x, y) elif is_FiniteField(R) or (is_IntegerModRing(R) and R.characteristic().is_prime()): return ell_finite_field.EllipticCurve_finite_field(x, y) elif R in _Fields: return ell_field.EllipticCurve_field(x, y) return ell_generic.EllipticCurve_generic(x, y)
def points(self, B=0, prec=53): """ Return some or all rational points of a projective scheme. INPUT: - ``B`` - integer (optional, default=0). The bound for the coordinates. - ``prec`` - he precision to use to compute the elements of bounded height for number fields. OUTPUT: A list of points. Over a finite field, all points are returned. Over an infinite field, all points satisfying the bound are returned. .. WARNING:: In the current implementation, the output of the [Doyle-Krumm] algorithm cannot be guaranteed to be correct due to the necessity of floating point computations. In some cases, the default 53-bit precision is considerably lower than would be required for the algorithm to generate correct output. EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: P(QQ).points(4) [(-4 : 1), (-3 : 1), (-2 : 1), (-3/2 : 1), (-4/3 : 1), (-1 : 1), (-3/4 : 1), (-2/3 : 1), (-1/2 : 1), (-1/3 : 1), (-1/4 : 1), (0 : 1), (1/4 : 1), (1/3 : 1), (1/2 : 1), (2/3 : 1), (3/4 : 1), (1 : 0), (1 : 1), (4/3 : 1), (3/2 : 1), (2 : 1), (3 : 1), (4 : 1)] :: sage: u = QQ['u'].0 sage: K.<v> = NumberField(u^2 + 3) sage: P.<x,y,z> = ProjectiveSpace(K,2) sage: len(P(K).points(1.8)) 381 :: sage: P1 = ProjectiveSpace(GF(2),1) sage: F.<a> = GF(4,'a') sage: P1(F).points() [(0 : 1), (1 : 0), (1 : 1), (a : 1), (a + 1 : 1)] :: sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: E = P.subscheme([(y^3-y*z^2) - (x^3-x*z^2),(y^3-y*z^2) + (x^3-x*z^2)]) sage: E(P.base_ring()).points() [(-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 : 1)] """ X = self.codomain() from sage.schemes.projective.projective_space import is_ProjectiveSpace if not is_ProjectiveSpace(X) and X.base_ring() in Fields(): #Then it must be a subscheme dim_ideal = X.defining_ideal().dimension() if dim_ideal < 1: # no points return [] if dim_ideal == 1: # if X zero-dimensional rat_points = set() PS = X.ambient_space() N = PS.dimension_relative() BR = X.base_ring() #need a lexicographic ordering for elimination R = PolynomialRing(BR, N + 1, PS.gens(), order='lex') I = R.ideal(X.defining_polynomials()) I0 = R.ideal(0) #Determine the points through elimination #This is much faster than using the I.variety() function on each affine chart. for k in range(N + 1): #create the elimination ideal for the kth affine patch G = I.substitute({R.gen(k):1}).groebner_basis() if G != [1]: P = {} #keep track that we know the kth coordinate is 1 P.update({R.gen(k):1}) points = [P] #work backwards from solving each equation for the possible #values of the next coordinate for i in range(len(G) - 1, -1, -1): new_points = [] good = 0 for P in points: #substitute in our dictionary entry that has the values #of coordinates known so far. This results in a single #variable polynomial (by elimination) L = G[i].substitute(P) if L != 0: L = L.factor() #the linear factors give the possible rational values of #this coordinate for pol, pow in L: if pol.degree() == 1 and len(pol.variables()) == 1: good = 1 r = pol.variables()[0] varindex = R.gens().index(r) #add this coordinates information to #each dictionary entry P.update({R.gen(varindex):-pol.constant_coefficient() / pol.monomial_coefficient(r)}) new_points.append(copy(P)) else: new_points.append(P) good = 1 if good: points = new_points #the dictionary entries now have values for all coordinates #they are the rational solutions to the equations #make them into projective points for i in range(len(points)): if len(points[i]) == N + 1 and I.subs(points[i]) == I0: S = X([points[i][R.gen(j)] for j in range(N + 1)]) S.normalize_coordinates() rat_points.add(S) rat_points = sorted(rat_points) return rat_points R = self.value_ring() if is_RationalField(R): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified"%B) from sage.schemes.projective.projective_rational_point import enum_projective_rational_field return enum_projective_rational_field(self,B) elif R in NumberFields(): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified"%B) from sage.schemes.projective.projective_rational_point import enum_projective_number_field return enum_projective_number_field(self,B, prec=prec) elif is_FiniteField(R): from sage.schemes.projective.projective_rational_point import enum_projective_finite_field return enum_projective_finite_field(self.extended_codomain()) else: raise TypeError("unable to enumerate points over %s"%R)
def points(self, **kwds): r""" Return some or all rational points of an affine scheme. Over a finite field, all points are returned. Over an infinite field, all points satisfying the bound are returned. For a zero-dimensional subscheme, all points are returned regardless of whether the field is infinite or not. For number fields, this uses the Doyle-Krumm algorithm 4 (algorithm 5 for imaginary quadratic) for computing algebraic numbers up to a given height [Doyle-Krumm]_. The algorithm requires floating point arithmetic, so the user is allowed to specify the precision for such calculations. Additionally, due to floating point issues, points slightly larger than the bound may be returned. This can be controlled by lowering the tolerance. INPUT: - ``bound`` - a real number - ``tolerance`` - a rational number in (0,1] used in doyle-krumm algorithm-4 - ``precision`` - the precision to use for computing the elements of bounded height of number fields. OUTPUT: - a list of rational points of a affine scheme EXAMPLES: The bug reported at #11526 is fixed:: sage: A2 = AffineSpace(ZZ, 2) sage: F = GF(3) sage: A2(F).points() [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)] sage: R = ZZ sage: A.<x,y> = R[] sage: I = A.ideal(x^2-y^2-1) sage: V = AffineSpace(R, 2) sage: X = V.subscheme(I) sage: M = X(R) sage: M.points(bound=1) [(-1, 0), (1, 0)] :: sage: u = QQ['u'].0 sage: K.<v> = NumberField(u^2 + 3) sage: A.<x,y> = AffineSpace(K, 2) sage: len(A(K).points(bound=2)) 1849 :: sage: A.<x,y> = AffineSpace(QQ, 2) sage: E = A.subscheme([x^2 + y^2 - 1, y^2 - x^3 + x^2 + x - 1]) sage: E(A.base_ring()).points() [(-1, 0), (0, -1), (0, 1), (1, 0)] """ B = kwds.pop('bound', 0) tol = kwds.pop('tolerance', 1e-2) prec = kwds.pop('precision', 53) X = self.codomain() from sage.schemes.affine.affine_space import is_AffineSpace if not is_AffineSpace(X) and X.base_ring() in Fields(): # Then X must be a subscheme dim_ideal = X.defining_ideal().dimension() if dim_ideal < 0: # no points return [] if dim_ideal == 0: # if X zero-dimensional N = len(X.ambient_space().gens()) S = X.defining_polynomials()[0].parent() R = PolynomialRing(S.base_ring(), 's', N, order='lex') phi = S.hom(R.gens(), R) J = R.ideal([phi(t) for t in X.defining_polynomials()]) D = J.variety() points = [] for d in D: P = [d[t] for t in R.gens()] points.append(X(P)) points.sort() return points R = self.value_ring() if is_RationalField(R) or R == ZZ: if not B > 0: raise TypeError("a positive bound B (= %s) must be specified" % B) from sage.schemes.affine.affine_rational_point import enum_affine_rational_field return enum_affine_rational_field(self, B) if R in NumberFields(): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified" % B) from sage.schemes.affine.affine_rational_point import enum_affine_number_field return enum_affine_number_field(self, bound=B, tolerance=tol, precision=prec) elif is_FiniteField(R): from sage.schemes.affine.affine_rational_point import enum_affine_finite_field return enum_affine_finite_field(self) else: raise TypeError("unable to enumerate points over %s" % R)
def points(self, B=0, prec=53): """ Return some or all rational points of a projective scheme. INPUT: - ``B`` - integer (optional, default=0). The bound for the coordinates. - ``prec`` - he precision to use to compute the elements of bounded height for number fields. OUTPUT: A list of points. Over a finite field, all points are returned. Over an infinite field, all points satisfying the bound are returned. .. WARNING:: In the current implementation, the output of the [Doyle-Krumm] algorithm cannot be guaranteed to be correct due to the necessity of floating point computations. In some cases, the default 53-bit precision is considerably lower than would be required for the algorithm to generate correct output. EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: P(QQ).points(4) [(-4 : 1), (-3 : 1), (-2 : 1), (-3/2 : 1), (-4/3 : 1), (-1 : 1), (-3/4 : 1), (-2/3 : 1), (-1/2 : 1), (-1/3 : 1), (-1/4 : 1), (0 : 1), (1/4 : 1), (1/3 : 1), (1/2 : 1), (2/3 : 1), (3/4 : 1), (1 : 0), (1 : 1), (4/3 : 1), (3/2 : 1), (2 : 1), (3 : 1), (4 : 1)] :: sage: u = QQ['u'].0 sage: K.<v> = NumberField(u^2 + 3) sage: P.<x,y,z> = ProjectiveSpace(K,2) sage: len(P(K).points(1.8)) 381 :: sage: P1 = ProjectiveSpace(GF(2),1) sage: F.<a> = GF(4,'a') sage: P1(F).points() [(0 : 1), (1 : 0), (1 : 1), (a : 1), (a + 1 : 1)] :: sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: E = P.subscheme([(y^3-y*z^2) - (x^3-x*z^2),(y^3-y*z^2) + (x^3-x*z^2)]) sage: E(P.base_ring()).points() [(-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 : 1)] """ X = self.codomain() from sage.schemes.projective.projective_space import is_ProjectiveSpace if not is_ProjectiveSpace(X) and X.base_ring() in Fields(): #Then it must be a subscheme dim_ideal = X.defining_ideal().dimension() if dim_ideal < 1: # no points return [] if dim_ideal == 1: # if X zero-dimensional rat_points = set() PS = X.ambient_space() N = PS.dimension_relative() BR = X.base_ring() #need a lexicographic ordering for elimination R = PolynomialRing(BR, N + 1, PS.variable_names(), order='lex') I = R.ideal(X.defining_polynomials()) I0 = R.ideal(0) #Determine the points through elimination #This is much faster than using the I.variety() function on each affine chart. for k in range(N + 1): #create the elimination ideal for the kth affine patch G = I.substitute({R.gen(k): 1}).groebner_basis() if G != [1]: P = {} #keep track that we know the kth coordinate is 1 P.update({R.gen(k): 1}) points = [P] #work backwards from solving each equation for the possible #values of the next coordinate for i in range(len(G) - 1, -1, -1): new_points = [] good = 0 for P in points: #substitute in our dictionary entry that has the values #of coordinates known so far. This results in a single #variable polynomial (by elimination) L = G[i].substitute(P) if L != 0: L = L.factor() #the linear factors give the possible rational values of #this coordinate for pol, pow in L: if pol.degree() == 1 and len( pol.variables()) == 1: good = 1 r = pol.variables()[0] varindex = R.gens().index(r) #add this coordinates information to #each dictionary entry P.update({ R.gen(varindex): -pol.constant_coefficient() / pol.monomial_coefficient(r) }) new_points.append(copy(P)) else: new_points.append(P) good = 1 if good: points = new_points #the dictionary entries now have values for all coordinates #they are the rational solutions to the equations #make them into projective points for i in range(len(points)): if len(points[i]) == N + 1 and I.subs( points[i]) == I0: S = X([ points[i][R.gen(j)] for j in range(N + 1) ]) S.normalize_coordinates() rat_points.add(S) rat_points = sorted(rat_points) return rat_points R = self.value_ring() if is_RationalField(R): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified" % B) from sage.schemes.projective.projective_rational_point import enum_projective_rational_field return enum_projective_rational_field(self, B) elif R in NumberFields(): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified" % B) from sage.schemes.projective.projective_rational_point import enum_projective_number_field return enum_projective_number_field(self, B, prec=prec) elif is_FiniteField(R): from sage.schemes.projective.projective_rational_point import enum_projective_finite_field return enum_projective_finite_field(self.extended_codomain()) else: raise TypeError("unable to enumerate points over %s" % R)
def points(self, **kwds): r""" Return some or all rational points of a projective scheme. Over a finite field, all points are returned. Over an infinite field, all points satisfying the bound are returned. For a zero-dimensional subscheme, all points are returned regardless of whether the base ring is a field or not. For number fields, this uses the Doyle-Krumm algorithm 4 (algorithm 5 for imaginary quadratic) for computing algebraic numbers up to a given height [DK2013]_. The algorithm requires floating point arithmetic, so the user is allowed to specify the precision for such calculations. Additionally, due to floating point issues, points slightly larger than the bound may be returned. This can be controlled by lowering the tolerance. INPUT: - ``bound`` - a real number - ``tolerance`` - a rational number in (0,1] used in doyle-krumm algorithm-4 - ``precision`` - the precision to use for computing the elements of bounded height of number fields. OUTPUT: - a list of rational points of a projective scheme EXAMPLES:: sage: P.<x,y,z,w> = ProductProjectiveSpaces([1, 1], QQ) sage: X = P.subscheme([x - y, z^2 - 2*w^2]) sage: X(P.base_ring()).points() [] :: sage: u = QQ['u'].0 sage: P.<x,y,z,w> = ProductProjectiveSpaces([1,1], NumberField(u^2 - 2, 'v')) sage: X = P.subscheme([x^2 - y^2, z^2 - 2*w^2]) sage: X(P.base_ring()).points() [(-1 : 1 , -v : 1), (1 : 1 , v : 1), (1 : 1 , -v : 1), (-1 : 1 , v : 1)] :: sage: u = QQ['u'].0 sage: K = NumberField(u^2 + 1, 'v') sage: P.<x,y,z,w> = ProductProjectiveSpaces([1, 1], K) sage: P(K).points(bound=1) [(-1 : 1 , -1 : 1), (-1 : 1 , -v : 1), (-1 : 1 , 0 : 1), (-1 : 1 , v : 1), (-1 : 1 , 1 : 0), (-1 : 1 , 1 : 1), (-v : 1 , -1 : 1), (-v : 1 , -v : 1), (-v : 1 , 0 : 1), (-v : 1 , v : 1), (-v : 1 , 1 : 0), (-v : 1 , 1 : 1), (0 : 1 , -1 : 1), (0 : 1 , -v : 1), (0 : 1 , 0 : 1), (0 : 1 , v : 1), (0 : 1 , 1 : 0), (0 : 1 , 1 : 1), (v : 1 , -1 : 1), (v : 1 , -v : 1), (v : 1 , 0 : 1), (v : 1 , v : 1), (v : 1 , 1 : 0), (v : 1 , 1 : 1), (1 : 0 , -1 : 1), (1 : 0 , -v : 1), (1 : 0 , 0 : 1), (1 : 0 , v : 1), (1 : 0 , 1 : 0), (1 : 0 , 1 : 1), (1 : 1 , -1 : 1), (1 : 1 , -v : 1), (1 : 1 , 0 : 1), (1 : 1 , v : 1), (1 : 1 , 1 : 0), (1 : 1 , 1 : 1)] :: sage: P.<x,y,z,u,v> = ProductProjectiveSpaces([2, 1], GF(3)) sage: P(P.base_ring()).points() [(0 : 0 : 1 , 0 : 1), (0 : 0 : 1 , 1 : 0), (0 : 0 : 1 , 1 : 1), (0 : 0 : 1 , 2 : 1), (0 : 1 : 0 , 0 : 1), (0 : 1 : 0 , 1 : 0), (0 : 1 : 0 , 1 : 1), (0 : 1 : 0 , 2 : 1), (0 : 1 : 1 , 0 : 1), (0 : 1 : 1 , 1 : 0), (0 : 1 : 1 , 1 : 1), (0 : 1 : 1 , 2 : 1), (0 : 2 : 1 , 0 : 1), (0 : 2 : 1 , 1 : 0), (0 : 2 : 1 , 1 : 1), (0 : 2 : 1 , 2 : 1), (1 : 0 : 0 , 0 : 1), (1 : 0 : 0 , 1 : 0), (1 : 0 : 0 , 1 : 1), (1 : 0 : 0 , 2 : 1), (1 : 0 : 1 , 0 : 1), (1 : 0 : 1 , 1 : 0), (1 : 0 : 1 , 1 : 1), (1 : 0 : 1 , 2 : 1), (1 : 1 : 0 , 0 : 1), (1 : 1 : 0 , 1 : 0), (1 : 1 : 0 , 1 : 1), (1 : 1 : 0 , 2 : 1), (1 : 1 : 1 , 0 : 1), (1 : 1 : 1 , 1 : 0), (1 : 1 : 1 , 1 : 1), (1 : 1 : 1 , 2 : 1), (1 : 2 : 1 , 0 : 1), (1 : 2 : 1 , 1 : 0), (1 : 2 : 1 , 1 : 1), (1 : 2 : 1 , 2 : 1), (2 : 0 : 1 , 0 : 1), (2 : 0 : 1 , 1 : 0), (2 : 0 : 1 , 1 : 1), (2 : 0 : 1 , 2 : 1), (2 : 1 : 0 , 0 : 1), (2 : 1 : 0 , 1 : 0), (2 : 1 : 0 , 1 : 1), (2 : 1 : 0 , 2 : 1), (2 : 1 : 1 , 0 : 1), (2 : 1 : 1 , 1 : 0), (2 : 1 : 1 , 1 : 1), (2 : 1 : 1 , 2 : 1), (2 : 2 : 1 , 0 : 1), (2 : 2 : 1 , 1 : 0), (2 : 2 : 1 , 1 : 1), (2 : 2 : 1 , 2 : 1)] """ B = kwds.pop('bound', 0) X = self.codomain() from sage.schemes.product_projective.space import is_ProductProjectiveSpaces if not is_ProductProjectiveSpaces(X) and X.base_ring() in Fields(): # no points if X.dimension() == -1: return [] # if X is zero-dimensional if X.dimension() == 0: points = set() # find points from all possible affine patches for I in xmrange([ n + 1 for n in X.ambient_space().dimension_relative_components() ]): [Y, phi] = X.affine_patch(I, True) aff_points = Y.rational_points() for PP in aff_points: points.add(phi(PP)) return list(points) R = self.value_ring() points = [] if is_RationalField(R): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified" % B) from sage.schemes.product_projective.rational_point import enum_product_projective_rational_field return enum_product_projective_rational_field(self, B) elif R in NumberFields(): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified" % B) from sage.schemes.product_projective.rational_point import enum_product_projective_number_field return enum_product_projective_number_field(self, bound=B) elif is_FiniteField(R): from sage.schemes.product_projective.rational_point import enum_product_projective_finite_field return enum_product_projective_finite_field(self) else: raise TypeError("unable to enumerate points over %s" % R)
def points_of_bounded_height(self, bound, prec=53): r""" Returns an iterator of the points in self of absolute height of at most the given bound. Bound check is strict for the rational field. Requires self to be projective space over a number field. Uses the Doyle-Krumm algorithm for computing algebraic numbers up to a given height [Doyle-Krumm]. INPUT: - ``bound`` - a real number - ``prec`` - the precision to use to compute the elements of bounded height for number fields OUTPUT: - an iterator of points in self .. WARNING:: In the current implementation, the output of the [Doyle-Krumm] algorithm cannot be guaranteed to be correct due to the necessity of floating point computations. In some cases, the default 53-bit precision is considerably lower than would be required for the algorithm to generate correct output. EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: list(P.points_of_bounded_height(5)) [(0 : 1), (1 : 1), (-1 : 1), (1/2 : 1), (-1/2 : 1), (2 : 1), (-2 : 1), (1/3 : 1), (-1/3 : 1), (3 : 1), (-3 : 1), (2/3 : 1), (-2/3 : 1), (3/2 : 1), (-3/2 : 1), (1/4 : 1), (-1/4 : 1), (4 : 1), (-4 : 1), (3/4 : 1), (-3/4 : 1), (4/3 : 1), (-4/3 : 1), (1 : 0)] :: sage: u = QQ['u'].0 sage: P.<x,y,z> = ProjectiveSpace(NumberField(u^2 - 2,'v'), 2) sage: len(list(P.points_of_bounded_height(1.5))) 57 """ if (is_RationalField(self.base_ring())): ftype = False # stores whether the field is a number field or the rational field elif ( self.base_ring() in NumberFields() ): # true for rational field as well, so check is_RationalField first ftype = True else: raise NotImplementedError( "self must be projective space over a number field.") bound = bound**(self.base_ring().absolute_degree() ) # convert to relative height n = self.dimension_relative() R = self.base_ring() zero = R(0) i = n while not i < 0: P = [zero for _ in range(i)] + [R(1)] + [zero for _ in range(n - i)] yield self(P) if (ftype == False): # if rational field iters = [R.range_by_height(bound) for _ in range(i)] else: # if number field iters = [ R.elements_of_bounded_height(bound, precision=prec) for _ in range(i) ] for x in iters: x.next() # put at zero j = 0 while j < i: try: P[j] = iters[j].next() yield self(P) j = 0 except StopIteration: if (ftype == False): # if rational field iters[j] = R.range_by_height(bound) # reset else: # if number field iters[j] = R.elements_of_bounded_height( bound, precision=prec) # reset iters[j].next() # put at zero P[j] = zero j += 1 i -= 1
def points(self, B=0, prec=53): """ Return some or all rational points of a projective scheme. INPUT: - `B` - integer (optional, default=0). The bound for the coordinates. - ``prec`` - he precision to use to compute the elements of bounded height for number fields OUTPUT: A list of points. Over a finite field, all points are returned. Over an infinite field, all points satisfying the bound are returned. .. WARNING:: In the current implementation, the output of the [Doyle-Krumm] algorithm cannot be guaranteed to be correct due to the necessity of floating point computations. In some cases, the default 53-bit precision is considerably lower than would be required for the algorithm to generate correct output. EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: P(QQ).points(4) [(-4 : 1), (-3 : 1), (-2 : 1), (-3/2 : 1), (-4/3 : 1), (-1 : 1), (-3/4 : 1), (-2/3 : 1), (-1/2 : 1), (-1/3 : 1), (-1/4 : 1), (0 : 1), (1/4 : 1), (1/3 : 1), (1/2 : 1), (2/3 : 1), (3/4 : 1), (1 : 0), (1 : 1), (4/3 : 1), (3/2 : 1), (2 : 1), (3 : 1), (4 : 1)] :: sage: u = QQ['u'].0 sage: K.<v> = NumberField(u^2 + 3) sage: P.<x,y,z> = ProjectiveSpace(K,2) sage: len(P(K).points(1.8)) 381 :: sage: P1 = ProjectiveSpace(GF(2),1) sage: F.<a> = GF(4,'a') sage: P1(F).points() [(0 : 1), (1 : 0), (1 : 1), (a : 1), (a + 1 : 1)] :: sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: E = P.subscheme([(y^3-y*z^2) - (x^3-x*z^2),(y^3-y*z^2) + (x^3-x*z^2)]) sage: E(P.base_ring()).points() [(-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 : 1)] """ X = self.codomain() from sage.schemes.projective.projective_space import is_ProjectiveSpace if not is_ProjectiveSpace(X) and X.base_ring() in Fields(): #Then it must be a subscheme dim_ideal = X.defining_ideal().dimension() if dim_ideal < 1: # no points return [] if dim_ideal == 1: # if X zero-dimensional points = set() for i in range(X.ambient_space().dimension_relative() + 1): Y = X.affine_patch(i) phi = Y.projective_embedding() aff_points = Y.rational_points() for PP in aff_points: points.add(X.ambient_space()(list(phi(PP)))) points = sorted(points) return points R = self.value_ring() if is_RationalField(R): if not B > 0: raise TypeError("A positive bound B (= %s) must be specified."%B) from sage.schemes.projective.projective_rational_point import enum_projective_rational_field return enum_projective_rational_field(self,B) elif R in NumberFields(): if not B > 0: raise TypeError("A positive bound B (= %s) must be specified."%B) from sage.schemes.projective.projective_rational_point import enum_projective_number_field return enum_projective_number_field(self,B, prec=prec) elif is_FiniteField(R): from sage.schemes.projective.projective_rational_point import enum_projective_finite_field return enum_projective_finite_field(self.extended_codomain()) else: raise TypeError("Unable to enumerate points over %s."%R)
def points(self, B=0, prec=53): """ Return some or all rational points of a projective scheme. INPUT: - ``B`` - integer (optional, default=0). The bound for the coordinates. - ``prec`` - he precision to use to compute the elements of bounded height for number fields. OUTPUT: A list of points. Over a finite field, all points are returned. Over an infinite field, all points satisfying the bound are returned. .. WARNING:: In the current implementation, the output of the [Doyle-Krumm] algorithm cannot be guaranteed to be correct due to the necessity of floating point computations. In some cases, the default 53-bit precision is considerably lower than would be required for the algorithm to generate correct output. EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: P(QQ).points(4) [(-4 : 1), (-3 : 1), (-2 : 1), (-3/2 : 1), (-4/3 : 1), (-1 : 1), (-3/4 : 1), (-2/3 : 1), (-1/2 : 1), (-1/3 : 1), (-1/4 : 1), (0 : 1), (1/4 : 1), (1/3 : 1), (1/2 : 1), (2/3 : 1), (3/4 : 1), (1 : 0), (1 : 1), (4/3 : 1), (3/2 : 1), (2 : 1), (3 : 1), (4 : 1)] :: sage: u = QQ['u'].0 sage: K.<v> = NumberField(u^2 + 3) sage: P.<x,y,z> = ProjectiveSpace(K,2) sage: len(P(K).points(1.8)) 381 :: sage: P1 = ProjectiveSpace(GF(2),1) sage: F.<a> = GF(4,'a') sage: P1(F).points() [(0 : 1), (1 : 0), (1 : 1), (a : 1), (a + 1 : 1)] :: sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: E = P.subscheme([(y^3-y*z^2) - (x^3-x*z^2),(y^3-y*z^2) + (x^3-x*z^2)]) sage: E(P.base_ring()).points() [(-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 : 1)] """ X = self.codomain() from sage.schemes.projective.projective_space import is_ProjectiveSpace if not is_ProjectiveSpace(X) and X.base_ring() in Fields(): #Then it must be a subscheme dim_ideal = X.defining_ideal().dimension() if dim_ideal < 1: # no points return [] if dim_ideal == 1: # if X zero-dimensional points = set() for i in range(X.ambient_space().dimension_relative() + 1): Y = X.affine_patch(i) phi = Y.projective_embedding() aff_points = Y.rational_points() for PP in aff_points: points.add(X.ambient_space()(list(phi(PP)))) points = sorted(points) return points R = self.value_ring() if is_RationalField(R): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified"%B) from sage.schemes.projective.projective_rational_point import enum_projective_rational_field return enum_projective_rational_field(self,B) elif R in NumberFields(): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified"%B) from sage.schemes.projective.projective_rational_point import enum_projective_number_field return enum_projective_number_field(self,B, prec=prec) elif is_FiniteField(R): from sage.schemes.projective.projective_rational_point import enum_projective_finite_field return enum_projective_finite_field(self.extended_codomain()) else: raise TypeError("unable to enumerate points over %s"%R)
def EllipticCurve(x=None, y=None, j=None, minimal_twist=True): r""" Construct an elliptic curve. In Sage, an elliptic curve is always specified by its a-invariants .. math:: y^2 + a_1 xy + a_3 y = x^3 + a_2 x^2 + a_4 x + a_6. INPUT: There are several ways to construct an elliptic curve: - ``EllipticCurve([a1,a2,a3,a4,a6])``: Elliptic curve with given a-invariants. The invariants are coerced into the parent of the first element. If all are integers, they are coerced into the rational numbers. - ``EllipticCurve([a4,a6])``: Same as above, but `a_1=a_2=a_3=0`. - ``EllipticCurve(label)``: Returns the elliptic curve over Q from the Cremona database with the given label. The label is a string, such as ``"11a"`` or ``"37b2"``. The letters in the label *must* be lower case (Cremona's new labeling). - ``EllipticCurve(R, [a1,a2,a3,a4,a6])``: Create the elliptic curve over ``R`` with given a-invariants. Here ``R`` can be an arbitrary ring. Note that addition need not be defined. - ``EllipticCurve(j=j0)`` or ``EllipticCurve_from_j(j0)``: Return an elliptic curve with j-invariant ``j0``. - ``EllipticCurve(polynomial)``: Read off the a-invariants from the polynomial coefficients, see :func:`EllipticCurve_from_Weierstrass_polynomial`. In each case above where the input is a list of length 2 or 5, one can instead give a 2 or 5-tuple instead. EXAMPLES: We illustrate creating elliptic curves:: sage: EllipticCurve([0,0,1,-1,0]) Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field We create a curve from a Cremona label:: sage: EllipticCurve('37b2') Elliptic Curve defined by y^2 + y = x^3 + x^2 - 1873*x - 31833 over Rational Field sage: EllipticCurve('5077a') Elliptic Curve defined by y^2 + y = x^3 - 7*x + 6 over Rational Field sage: EllipticCurve('389a') Elliptic Curve defined by y^2 + y = x^3 + x^2 - 2*x over Rational Field Old Cremona labels are allowed:: sage: EllipticCurve('2400FF') Elliptic Curve defined by y^2 = x^3 + x^2 + 2*x + 8 over Rational Field Unicode labels are allowed:: sage: EllipticCurve(u'389a') Elliptic Curve defined by y^2 + y = x^3 + x^2 - 2*x over Rational Field We create curves over a finite field as follows:: sage: EllipticCurve([GF(5)(0),0,1,-1,0]) Elliptic Curve defined by y^2 + y = x^3 + 4*x over Finite Field of size 5 sage: EllipticCurve(GF(5), [0, 0,1,-1,0]) Elliptic Curve defined by y^2 + y = x^3 + 4*x over Finite Field of size 5 Elliptic curves over `\ZZ/N\ZZ` with `N` prime are of type "elliptic curve over a finite field":: sage: F = Zmod(101) sage: EllipticCurve(F, [2, 3]) Elliptic Curve defined by y^2 = x^3 + 2*x + 3 over Ring of integers modulo 101 sage: E = EllipticCurve([F(2), F(3)]) sage: type(E) <class 'sage.schemes.elliptic_curves.ell_finite_field.EllipticCurve_finite_field_with_category'> sage: E.category() Category of schemes over Ring of integers modulo 101 In contrast, elliptic curves over `\ZZ/N\ZZ` with `N` composite are of type "generic elliptic curve":: sage: F = Zmod(95) sage: EllipticCurve(F, [2, 3]) Elliptic Curve defined by y^2 = x^3 + 2*x + 3 over Ring of integers modulo 95 sage: E = EllipticCurve([F(2), F(3)]) sage: type(E) <class 'sage.schemes.elliptic_curves.ell_generic.EllipticCurve_generic_with_category'> sage: E.category() Category of schemes over Ring of integers modulo 95 The following is a curve over the complex numbers:: sage: E = EllipticCurve(CC, [0,0,1,-1,0]) sage: E Elliptic Curve defined by y^2 + 1.00000000000000*y = x^3 + (-1.00000000000000)*x over Complex Field with 53 bits of precision sage: E.j_invariant() 2988.97297297297 We can also create elliptic curves by giving the Weierstrass equation:: sage: x, y = var('x,y') sage: EllipticCurve(y^2 + y == x^3 + x - 9) Elliptic Curve defined by y^2 + y = x^3 + x - 9 over Rational Field sage: R.<x,y> = GF(5)[] sage: EllipticCurve(x^3 + x^2 + 2 - y^2 - y*x) Elliptic Curve defined by y^2 + x*y = x^3 + x^2 + 2 over Finite Field of size 5 We can explicitly specify the `j`-invariant:: sage: E = EllipticCurve(j=1728); E; E.j_invariant(); E.label() Elliptic Curve defined by y^2 = x^3 - x over Rational Field 1728 '32a2' sage: E = EllipticCurve(j=GF(5)(2)); E; E.j_invariant() Elliptic Curve defined by y^2 = x^3 + x + 1 over Finite Field of size 5 2 See :trac:`6657` :: sage: EllipticCurve(GF(144169),j=1728) Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 144169 By default, when a rational value of `j` is given, the constructed curve is a minimal twist (minimal conductor for curves with that `j`-invariant). This can be changed by setting the optional parameter ``minimal_twist``, which is True by default, to False:: sage: EllipticCurve(j=100) Elliptic Curve defined by y^2 = x^3 + x^2 + 3392*x + 307888 over Rational Field sage: E =EllipticCurve(j=100); E Elliptic Curve defined by y^2 = x^3 + x^2 + 3392*x + 307888 over Rational Field sage: E.conductor() 33129800 sage: E.j_invariant() 100 sage: E =EllipticCurve(j=100, minimal_twist=False); E Elliptic Curve defined by y^2 = x^3 + 488400*x - 530076800 over Rational Field sage: E.conductor() 298168200 sage: E.j_invariant() 100 Without this option, constructing the curve could take a long time since both `j` and `j-1728` have to be factored to compute the minimal twist (see :trac:`13100`):: sage: E = EllipticCurve_from_j(2^256+1,minimal_twist=False) sage: E.j_invariant() == 2^256+1 True TESTS:: sage: R = ZZ['u', 'v'] sage: EllipticCurve(R, [1,1]) Elliptic Curve defined by y^2 = x^3 + x + 1 over Multivariate Polynomial Ring in u, v over Integer Ring We create a curve and a point over QQbar (see #6879):: sage: E = EllipticCurve(QQbar,[0,1]) sage: E(0) (0 : 1 : 0) sage: E.base_field() Algebraic Field sage: E = EllipticCurve(RR,[1,2]); E; E.base_field() Elliptic Curve defined by y^2 = x^3 + 1.00000000000000*x + 2.00000000000000 over Real Field with 53 bits of precision Real Field with 53 bits of precision sage: EllipticCurve(CC,[3,4]); E; E.base_field() Elliptic Curve defined by y^2 = x^3 + 3.00000000000000*x + 4.00000000000000 over Complex Field with 53 bits of precision Elliptic Curve defined by y^2 = x^3 + 1.00000000000000*x + 2.00000000000000 over Real Field with 53 bits of precision Real Field with 53 bits of precision sage: E = EllipticCurve(QQbar,[5,6]); E; E.base_field() Elliptic Curve defined by y^2 = x^3 + 5*x + 6 over Algebraic Field Algebraic Field See :trac:`6657` :: sage: EllipticCurve(3,j=1728) Traceback (most recent call last): ... ValueError: First parameter (if present) must be a ring when j is specified sage: EllipticCurve(GF(5),j=3/5) Traceback (most recent call last): ... ValueError: First parameter must be a ring containing 3/5 If the universe of the coefficients is a general field, the object constructed has type EllipticCurve_field. Otherwise it is EllipticCurve_generic. See :trac:`9816` :: sage: E = EllipticCurve([QQbar(1),3]); E Elliptic Curve defined by y^2 = x^3 + x + 3 over Algebraic Field sage: type(E) <class 'sage.schemes.elliptic_curves.ell_field.EllipticCurve_field_with_category'> sage: E = EllipticCurve([RR(1),3]); E Elliptic Curve defined by y^2 = x^3 + 1.00000000000000*x + 3.00000000000000 over Real Field with 53 bits of precision sage: type(E) <class 'sage.schemes.elliptic_curves.ell_field.EllipticCurve_field_with_category'> sage: E = EllipticCurve([i,i]); E Elliptic Curve defined by y^2 = x^3 + I*x + I over Symbolic Ring sage: type(E) <class 'sage.schemes.elliptic_curves.ell_field.EllipticCurve_field_with_category'> sage: E.category() Category of schemes over Symbolic Ring sage: SR in Fields() True sage: F = FractionField(PolynomialRing(QQ,'t')) sage: t = F.gen() sage: E = EllipticCurve([t,0]); E Elliptic Curve defined by y^2 = x^3 + t*x over Fraction Field of Univariate Polynomial Ring in t over Rational Field sage: type(E) <class 'sage.schemes.elliptic_curves.ell_field.EllipticCurve_field_with_category'> sage: E.category() Category of schemes over Fraction Field of Univariate Polynomial Ring in t over Rational Field See :trac:`12517`:: sage: E = EllipticCurve([1..5]) sage: EllipticCurve(E.a_invariants()) Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Rational Field See :trac:`11773`:: sage: E = EllipticCurve() Traceback (most recent call last): ... TypeError: invalid input to EllipticCurve constructor """ import ell_generic, ell_field, ell_finite_field, ell_number_field, ell_rational_field, ell_padic_field # here to avoid circular includes if j is not None: if not x is None: if is_Ring(x): try: j = x(j) except (ZeroDivisionError, ValueError, TypeError): raise ValueError, "First parameter must be a ring containing %s" % j else: raise ValueError, "First parameter (if present) must be a ring when j is specified" return EllipticCurve_from_j(j, minimal_twist) if x is None: raise TypeError, "invalid input to EllipticCurve constructor" if is_SymbolicEquation(x): x = x.lhs() - x.rhs() if parent(x) is SR: x = x._polynomial_(rings.QQ['x', 'y']) if is_MPolynomial(x): if y is None: return EllipticCurve_from_Weierstrass_polynomial(x) else: return EllipticCurve_from_cubic(x, y, morphism=False) if is_Ring(x): if is_RationalField(x): return ell_rational_field.EllipticCurve_rational_field(x, y) elif is_FiniteField(x) or (is_IntegerModRing(x) and x.characteristic().is_prime()): return ell_finite_field.EllipticCurve_finite_field(x, y) elif rings.is_pAdicField(x): return ell_padic_field.EllipticCurve_padic_field(x, y) elif is_NumberField(x): return ell_number_field.EllipticCurve_number_field(x, y) elif x in _Fields: return ell_field.EllipticCurve_field(x, y) return ell_generic.EllipticCurve_generic(x, y) if isinstance(x, unicode): x = str(x) if isinstance(x, basestring): return ell_rational_field.EllipticCurve_rational_field(x) if is_RingElement(x) and y is None: raise TypeError, "invalid input to EllipticCurve constructor" if not isinstance(x, (list, tuple)): raise TypeError, "invalid input to EllipticCurve constructor" x = Sequence(x) if not (len(x) in [2, 5]): raise ValueError, "sequence of coefficients must have length 2 or 5" R = x.universe() if isinstance(x[0], (rings.Rational, rings.Integer, int, long)): return ell_rational_field.EllipticCurve_rational_field(x, y) elif is_NumberField(R): return ell_number_field.EllipticCurve_number_field(x, y) elif rings.is_pAdicField(R): return ell_padic_field.EllipticCurve_padic_field(x, y) elif is_FiniteField(R) or (is_IntegerModRing(R) and R.characteristic().is_prime()): return ell_finite_field.EllipticCurve_finite_field(x, y) elif R in _Fields: return ell_field.EllipticCurve_field(x, y) return ell_generic.EllipticCurve_generic(x, y)
def points(self, **kwds): r""" Return some or all rational points of a projective scheme. Over a finite field, all points are returned. Over an infinite field, all points satisfying the bound are returned. For a zero-dimensional subscheme, all points are returned regardless of whether the base ring is a field or not. For number fields, this uses the Doyle-Krumm algorithm 4 (algorithm 5 for imaginary quadratic) for computing algebraic numbers up to a given height [DK2013]_ or uses the chinese remainder theorem and points modulo primes for larger bounds. The algorithm requires floating point arithmetic, so the user is allowed to specify the precision for such calculations. Additionally, due to floating point issues, points slightly larger than the bound may be returned. This can be controlled by lowering the tolerance. INPUT: - ``bound`` - a real number - ``tolerance`` - a rational number in (0,1] used in doyle-krumm algorithm-4 - ``precision`` - the precision to use for computing the elements of bounded height of number fields. - ``algorithm`` - either 'sieve' or 'enumerate' algorithms can be used over `QQ`. If not specified, enumerate is used only for small height bounds. OUTPUT: - a list of rational points of a projective scheme EXAMPLES:: sage: P.<x,y,z,w> = ProductProjectiveSpaces([1, 1], QQ) sage: X = P.subscheme([x - y, z^2 - 2*w^2]) sage: X(P.base_ring()).points() [] :: sage: u = QQ['u'].0 sage: P.<x,y,z,w> = ProductProjectiveSpaces([1,1], NumberField(u^2 - 2, 'v')) sage: X = P.subscheme([x^2 - y^2, z^2 - 2*w^2]) sage: sorted(X(P.base_ring()).points()) [(-1 : 1 , -v : 1), (-1 : 1 , v : 1), (1 : 1 , -v : 1), (1 : 1 , v : 1)] :: sage: u = QQ['u'].0 sage: K = NumberField(u^2 + 1, 'v') sage: P.<x,y,z,w> = ProductProjectiveSpaces([1, 1], K) sage: P(K).points(bound=1) [(-1 : 1 , -1 : 1), (-1 : 1 , -v : 1), (-1 : 1 , 0 : 1), (-1 : 1 , v : 1), (-1 : 1 , 1 : 0), (-1 : 1 , 1 : 1), (-v : 1 , -1 : 1), (-v : 1 , -v : 1), (-v : 1 , 0 : 1), (-v : 1 , v : 1), (-v : 1 , 1 : 0), (-v : 1 , 1 : 1), (0 : 1 , -1 : 1), (0 : 1 , -v : 1), (0 : 1 , 0 : 1), (0 : 1 , v : 1), (0 : 1 , 1 : 0), (0 : 1 , 1 : 1), (v : 1 , -1 : 1), (v : 1 , -v : 1), (v : 1 , 0 : 1), (v : 1 , v : 1), (v : 1 , 1 : 0), (v : 1 , 1 : 1), (1 : 0 , -1 : 1), (1 : 0 , -v : 1), (1 : 0 , 0 : 1), (1 : 0 , v : 1), (1 : 0 , 1 : 0), (1 : 0 , 1 : 1), (1 : 1 , -1 : 1), (1 : 1 , -v : 1), (1 : 1 , 0 : 1), (1 : 1 , v : 1), (1 : 1 , 1 : 0), (1 : 1 , 1 : 1)] :: sage: P.<x,y,z,u,v> = ProductProjectiveSpaces([2, 1], GF(3)) sage: P(P.base_ring()).points() [(0 : 0 : 1 , 0 : 1), (0 : 0 : 1 , 1 : 0), (0 : 0 : 1 , 1 : 1), (0 : 0 : 1 , 2 : 1), (0 : 1 : 0 , 0 : 1), (0 : 1 : 0 , 1 : 0), (0 : 1 : 0 , 1 : 1), (0 : 1 : 0 , 2 : 1), (0 : 1 : 1 , 0 : 1), (0 : 1 : 1 , 1 : 0), (0 : 1 : 1 , 1 : 1), (0 : 1 : 1 , 2 : 1), (0 : 2 : 1 , 0 : 1), (0 : 2 : 1 , 1 : 0), (0 : 2 : 1 , 1 : 1), (0 : 2 : 1 , 2 : 1), (1 : 0 : 0 , 0 : 1), (1 : 0 : 0 , 1 : 0), (1 : 0 : 0 , 1 : 1), (1 : 0 : 0 , 2 : 1), (1 : 0 : 1 , 0 : 1), (1 : 0 : 1 , 1 : 0), (1 : 0 : 1 , 1 : 1), (1 : 0 : 1 , 2 : 1), (1 : 1 : 0 , 0 : 1), (1 : 1 : 0 , 1 : 0), (1 : 1 : 0 , 1 : 1), (1 : 1 : 0 , 2 : 1), (1 : 1 : 1 , 0 : 1), (1 : 1 : 1 , 1 : 0), (1 : 1 : 1 , 1 : 1), (1 : 1 : 1 , 2 : 1), (1 : 2 : 1 , 0 : 1), (1 : 2 : 1 , 1 : 0), (1 : 2 : 1 , 1 : 1), (1 : 2 : 1 , 2 : 1), (2 : 0 : 1 , 0 : 1), (2 : 0 : 1 , 1 : 0), (2 : 0 : 1 , 1 : 1), (2 : 0 : 1 , 2 : 1), (2 : 1 : 0 , 0 : 1), (2 : 1 : 0 , 1 : 0), (2 : 1 : 0 , 1 : 1), (2 : 1 : 0 , 2 : 1), (2 : 1 : 1 , 0 : 1), (2 : 1 : 1 , 1 : 0), (2 : 1 : 1 , 1 : 1), (2 : 1 : 1 , 2 : 1), (2 : 2 : 1 , 0 : 1), (2 : 2 : 1 , 1 : 0), (2 : 2 : 1 , 1 : 1), (2 : 2 : 1 , 2 : 1)] :: sage: PP.<x,y,z,u,v> = ProductProjectiveSpaces([2,1], QQ) sage: X = PP.subscheme([x + y, u*u-v*u]) sage: X.rational_points(bound=2) [(-2 : 2 : 1 , 0 : 1), (-2 : 2 : 1 , 1 : 1), (-1 : 1 : 0 , 0 : 1), (-1 : 1 : 0 , 1 : 1), (-1 : 1 : 1 , 0 : 1), (-1 : 1 : 1 , 1 : 1), (-1/2 : 1/2 : 1 , 0 : 1), (-1/2 : 1/2 : 1 , 1 : 1), (0 : 0 : 1 , 0 : 1), (0 : 0 : 1 , 1 : 1), (1/2 : -1/2 : 1 , 0 : 1), (1/2 : -1/2 : 1 , 1 : 1), (1 : -1 : 1 , 0 : 1), (1 : -1 : 1 , 1 : 1), (2 : -2 : 1 , 0 : 1), (2 : -2 : 1 , 1 : 1)] better to enumerate with low codimension:: sage: PP.<x,y,z,u,v,a,b,c> = ProductProjectiveSpaces([2,1,2], QQ) sage: X = PP.subscheme([x*u^2*a, b*z*u*v,z*v^2*c ]) sage: len(X.rational_points(bound=1, algorithm='enumerate')) 232 """ B = kwds.pop('bound', 0) X = self.codomain() from sage.schemes.product_projective.space import is_ProductProjectiveSpaces if not is_ProductProjectiveSpaces(X) and X.base_ring() in Fields(): # no points if X.dimension() == -1: return [] # if X is zero-dimensional if X.dimension() == 0: points = set() # find points from all possible affine patches for I in xmrange([n + 1 for n in X.ambient_space().dimension_relative_components()]): [Y,phi] = X.affine_patch(I, True) aff_points = Y.rational_points() for PP in aff_points: points.add(phi(PP)) return list(points) R = self.value_ring() points = [] if is_RationalField(R): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified"%B) alg = kwds.pop('algorithm', None) if alg is None: # sieve should only be called for subschemes and if the bound is not very small N = prod([k+1 for k in X.ambient_space().dimension_relative_components()]) if isinstance(X, AlgebraicScheme_subscheme) and B**N > 5000: from sage.schemes.product_projective.rational_point import sieve return sieve(X, B) else: from sage.schemes.product_projective.rational_point import enum_product_projective_rational_field return enum_product_projective_rational_field(self, B) elif alg == 'sieve': from sage.schemes.product_projective.rational_point import sieve return sieve(X, B) elif alg == 'enumerate': from sage.schemes.product_projective.rational_point import enum_product_projective_rational_field return enum_product_projective_rational_field(self, B) else: raise ValueError("algorithm must be 'sieve' or 'enumerate'") elif R in NumberFields(): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified"%B) from sage.schemes.product_projective.rational_point import enum_product_projective_number_field return enum_product_projective_number_field(self, bound=B) elif is_FiniteField(R): from sage.schemes.product_projective.rational_point import enum_product_projective_finite_field return enum_product_projective_finite_field(self) else: raise TypeError("unable to enumerate points over %s" % R)
def dual(self): r""" Return the projective dual of the given subscheme of projective space. INPUT: - ``X`` -- A subscheme of projective space. At present, ``X`` is required to be an irreducible and reduced hypersurface defined over `\QQ` or a finite field. OUTPUT: - The dual of ``X`` as a subscheme of the dual projective space. EXAMPLES: The dual of a smooth conic in the plane is also a smooth conic:: sage: R.<x, y, z> = QQ[] sage: P.<x, y, z> = ProjectiveSpace(2, QQ) sage: I = R.ideal(x^2 + y^2 + z^2) sage: X = P.subscheme(I) sage: X.dual() Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: y0^2 + y1^2 + y2^2 The dual of the twisted cubic curve in projective 3-space is a singular quartic surface. In the following example, we compute the dual of this surface, which by double duality is equal to the twisted cubic itself. The output is the twisted cubic as an intersection of three quadrics:: sage: R.<x, y, z, w> = QQ[] sage: P.<x, y, z, w> = ProjectiveSpace(3, QQ) sage: I = R.ideal(y^2*z^2 - 4*x*z^3 - 4*y^3*w + 18*x*y*z*w - 27*x^2*w^2) sage: X = P.subscheme(I) sage: X.dual() Closed subscheme of Projective Space of dimension 3 over Rational Field defined by: y2^2 - y1*y3, y1*y2 - y0*y3, y1^2 - y0*y2 The singular locus of the quartic surface in the last example is itself supported on a twisted cubic:: sage: X.Jacobian().radical() Ideal (z^2 - 3*y*w, y*z - 9*x*w, y^2 - 3*x*z) of Multivariate Polynomial Ring in x, y, z, w over Rational Field An example over a finite field:: sage: R = PolynomialRing(GF(61), 'a,b,c') sage: P.<a, b, c> = ProjectiveSpace(2, R.base_ring()) sage: X = P.subscheme(R.ideal(a*a+2*b*b+3*c*c)) sage: X.dual() Closed subscheme of Projective Space of dimension 2 over Finite Field of size 61 defined by: y0^2 - 30*y1^2 - 20*y2^2 TESTS:: sage: R = PolynomialRing(Qp(3), 'a,b,c') sage: P.<a, b, c> = ProjectiveSpace(2, R.base_ring()) sage: X = P.subscheme(R.ideal(a*a+2*b*b+3*c*c)) sage: X.dual() Traceback (most recent call last): ... NotImplementedError: base ring must be QQ or a finite field """ from sage.libs.singular.function_factory import ff K = self.base_ring() if not(is_RationalField(K) or is_FiniteField(K)): raise NotImplementedError("base ring must be QQ or a finite field") I = self.defining_ideal() m = I.ngens() n = I.ring().ngens() - 1 if (m != 1 or (n < 1) or I.is_zero() or I.is_trivial() or not I.is_prime()): raise NotImplementedError("At the present, the method is only" " implemented for irreducible and" " reduced hypersurfaces and the given" " list of generators for the ideal must" " have exactly one element.") R = PolynomialRing(K, 'x', n + 1) from sage.schemes.projective.projective_space import ProjectiveSpace Pd = ProjectiveSpace(n, K, 'y') Rd = Pd.coordinate_ring() x = R.variable_names() y = Rd.variable_names() S = PolynomialRing(K, x + y + ('t',)) if S.has_coerce_map_from(I.ring()): T = PolynomialRing(K, 'w', n + 1) I_S = (I.change_ring(T)).change_ring(S) else: I_S = I.change_ring(S) f_S = I_S.gens()[0] z = S.gens() J = I_S for i in range(n + 1): J = J + S.ideal(z[-1] * f_S.derivative(z[i]) - z[i + n + 1]) sat = ff.elim__lib.sat max_ideal = S.ideal(z[n + 1: 2 * n + 2]) J_sat_gens = sat(J, max_ideal)[0] J_sat = S.ideal(J_sat_gens) L = J_sat.elimination_ideal(z[0: n + 1] + (z[-1],)) return Pd.subscheme(L.change_ring(Rd))
def has_rational_point(self, point = False, obstruction = False, algorithm = 'default', read_cache = True): r""" Returns ``True`` if and only if ``self`` has a point defined over its base field `B`. If ``point`` and ``obstruction`` are both False (default), then the output is a boolean ``out`` saying whether ``self`` has a rational point. If ``point`` or ``obstruction`` is True, then the output is a pair ``(out, S)``, where ``out`` is as above and: - if ``point`` is True and ``self`` has a rational point, then ``S`` is a rational point, - if ``obstruction`` is True, ``self`` has no rational point, then ``S`` is a prime or infinite place of `B` such that no rational point exists over the completion at ``S``. Points and obstructions are cached whenever they are found. Cached information is used for the output if available, but only if ``read_cache`` is True. ALGORITHM: The parameter ``algorithm`` specifies the algorithm to be used: - ``'rnfisnorm'`` -- Use PARI's rnfisnorm (cannot be combined with ``obstruction = True``) - ``'local'`` -- Check if a local solution exists for all primes and infinite places of `B` and apply the Hasse principle. (Cannot be combined with ``point = True``.) - ``'default'`` -- Use algorithm ``'rnfisnorm'`` first. Then, if no point exists and obstructions are requested, use algorithm ``'local'`` to find an obstruction. - ``'magma'`` (requires Magma to be installed) -- delegates the task to the Magma computer algebra system. EXAMPLES: An example over `\QQ` :: sage: C = Conic(QQ, [1, 113922743, -310146482690273725409]) sage: C.has_rational_point(point = True) (True, (-76842858034579/5424 : -5316144401/5424 : 1)) sage: C.has_rational_point(algorithm = 'local', read_cache = False) True Examples over number fields:: sage: K.<i> = QuadraticField(-1) sage: C = Conic(K, [1, 3, -5]) sage: C.has_rational_point(point = True, obstruction = True) (False, Fractional ideal (-i - 2)) sage: C.has_rational_point(algorithm = "rnfisnorm") False sage: C.has_rational_point(algorithm = "rnfisnorm", obstruction = True, read_cache=False) Traceback (most recent call last): ... ValueError: Algorithm rnfisnorm cannot be combined with obstruction = True in has_rational_point sage: P.<x> = QQ[] sage: L.<b> = NumberField(x^3-5) sage: C = Conic(L, [1, 2, -3]) sage: C.has_rational_point(point = True, algorithm = 'rnfisnorm') (True, (5/3 : -1/3 : 1)) sage: K.<a> = NumberField(x^4+2) sage: Conic(QQ, [4,5,6]).has_rational_point() False sage: Conic(K, [4,5,6]).has_rational_point() True sage: Conic(K, [4,5,6]).has_rational_point(algorithm='magma', read_cache=False) # optional - magma True TESTS: Create a bunch of conics over number fields and check whether ``has_rational_point`` runs without errors for algorithms ``'rnfisnorm'`` and ``'local'``. Check if all points returned are valid. If Magma is available, then also check if the output agrees with Magma. :: sage: P.<X> = QQ[] sage: Q = P.fraction_field() sage: c = [1, X/2, 1/X] sage: l = Sequence(cartesian_product_iterator([c for i in range(3)])) sage: l = l + [[X, 1, 1, 1, 1, 1]] + [[X, 1/5, 1, 1, 2, 1]] sage: K.<a> = QuadraticField(-23) sage: L.<b> = QuadraticField(19) sage: M.<c> = NumberField(X^3+3*X+1) sage: m = [[Q(b)(F.gen()) for b in a] for a in l for F in [K, L, M]] sage: d = [] sage: c = [] sage: c = [Conic(a) for a in m if a != [0,0,0]] sage: d = [C.has_rational_point(algorithm = 'rnfisnorm', point = True) for C in c] # long time: 3.3 seconds sage: all([c[k].defining_polynomial()(Sequence(d[k][1])) == 0 for k in range(len(d)) if d[k][0]]) True sage: [C.has_rational_point(algorithm='local', read_cache=False) for C in c] == [o[0] for o in d] # long time: 5 seconds True sage: [C.has_rational_point(algorithm = 'magma', read_cache=False) for C in c] == [o[0] for o in d] # long time: 3 seconds, optional - magma True Create a bunch of conics that are known to have rational points already over `\QQ` and check if points are found by ``has_rational_point``. :: sage: l = Sequence(cartesian_product_iterator([[-1, 0, 1] for i in range(3)])) sage: K.<a> = QuadraticField(-23) sage: L.<b> = QuadraticField(19) sage: M.<c> = NumberField(x^5+3*x+1) sage: m = [[F(b) for b in a] for a in l for F in [K, L, M]] sage: c = [Conic(a) for a in m if a != [0,0,0] and a != [1,1,1] and a != [-1,-1,-1]] sage: assert all([C.has_rational_point(algorithm = 'rnfisnorm') for C in c]) sage: assert all([C.defining_polynomial()(Sequence(C.has_rational_point(point = True)[1])) == 0 for C in c]) sage: assert all([C.has_rational_point(algorithm='local', read_cache=False) for C in c]) # long time: 1 second """ if read_cache: if self._rational_point is not None: if point or obstruction: return True, self._rational_point else: return True if self._local_obstruction is not None: if point or obstruction: return False, self._local_obstruction else: return False if (not point) and self._finite_obstructions == [] and \ self._infinite_obstructions == []: if obstruction: return True, None return True if self.has_singular_point(): if point: return self.has_singular_point(point = True) if obstruction: return True, None return True B = self.base_ring() if algorithm == 'default': ret = self.has_rational_point(point=True, obstruction=False, algorithm='rnfisnorm', read_cache=False) if ret[0]: if point or obstruction: return ret return True if obstruction: ret = self.has_rational_point(point=False, obstruction=True, algorithm='local', read_cache=False) if ret[0]: raise RuntimeError("Outputs of algorithms in " \ "has_rational_point disagree " \ "for conic %s" % self) return ret if point: return False, None return False if algorithm == 'local': if point: raise ValueError("Algorithm 'local' cannot be combined " \ "with point = True in has_rational_point") obs = self.local_obstructions(infinite = True, finite = False, read_cache = read_cache) if obs != []: if obstruction: return False, obs[0] return False obs = self.local_obstructions(read_cache = read_cache) if obs == []: if obstruction: return True, None return True if obstruction: return False, obs[0] return False if algorithm == 'rnfisnorm': from sage.modules.free_module_element import vector if obstruction: raise ValueError("Algorithm rnfisnorm cannot be combined " \ "with obstruction = True in " \ "has_rational_point") D, T = self.diagonal_matrix() abc = [D[0,0], D[1,1], D[2,2]] for j in range(3): if abc[j] == 0: pt = self.point(T*vector({2:0,j:1})) if point or obstruction: return True, pt return True if (-abc[1]/abc[0]).is_square(): pt = self.point(T*vector([(-abc[1]/abc[0]).sqrt(), 1, 0])) if point or obstruction: return True, pt return True if (-abc[2]/abc[0]).is_square(): pt = self.point(T*vector([(-abc[2]/abc[0]).sqrt(), 0, 1])) if point or obstruction: return True, pt return True if is_RationalField(B): K = B [KtoB, BtoK] = [K.hom(K) for i in range(2)] else: K = B.absolute_field('Y') [KtoB, BtoK] = K.structure() X = PolynomialRing(K, 'X').gen() d = BtoK(-abc[1]/abc[0]) den = d.denominator() L = K.extension(X**2 - d*den**2, names='y') isnorm = BtoK(-abc[2]/abc[0]).is_norm(L, element=True) if isnorm[0]: pt = self.point(T*vector([KtoB(isnorm[1][0]), KtoB(isnorm[1][1]*den), 1])) if point: return True, pt return True if point: return False, None return False if algorithm == 'qfsolve': raise TypeError("Algorithm qfsolve in has_rational_point only " \ "for conics over QQ, not over %s" % B) if obstruction: raise ValueError("Invalid combination: obstruction=True and " \ "algorithm=%s" % algorithm) return ProjectiveConic_field.has_rational_point(self, point = point, algorithm = algorithm, read_cache = False)
def points_of_bounded_height(self,bound): r""" Returns an iterator of the points in self of absolute height of at most the given bound. Bound check is strict for the rational field. Requires self to be projective space over a number field. Uses the Doyle-Krumm algorithm for computing algebraic numbers up to a given height [Doyle-Krumm]. INPUT: - ``bound`` - a real number OUTPUT: - an iterator of points in self EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: list(P.points_of_bounded_height(5)) [(0 : 1), (1 : 1), (-1 : 1), (1/2 : 1), (-1/2 : 1), (2 : 1), (-2 : 1), (1/3 : 1), (-1/3 : 1), (3 : 1), (-3 : 1), (2/3 : 1), (-2/3 : 1), (3/2 : 1), (-3/2 : 1), (1/4 : 1), (-1/4 : 1), (4 : 1), (-4 : 1), (3/4 : 1), (-3/4 : 1), (4/3 : 1), (-4/3 : 1), (1 : 0)] :: sage: u = QQ['u'].0 sage: P.<x,y,z> = ProjectiveSpace(NumberField(u^2 - 2,'v'), 2) sage: len(list(P.points_of_bounded_height(6))) 133 """ if (is_RationalField(self.base_ring())): ftype = False # stores whether the field is a number field or the rational field elif (self.base_ring() in NumberFields()): # true for rational field as well, so check is_RationalField first ftype = True else: raise NotImplementedError("self must be projective space over a number field.") bound = bound**(1/self.base_ring().absolute_degree()) # convert to relative height n = self.dimension_relative() R = self.base_ring() zero = R(0) i = n while not i < 0: P = [ zero for _ in range(i) ] + [ R(1) ] + [ zero for _ in range(n-i) ] yield self(P) if (ftype == False): # if rational field iters = [ R.range_by_height(bound) for _ in range(i) ] else: # if number field iters = [ R.elements_of_bounded_height(bound) for _ in range(i) ] for x in iters: x.next() # put at zero j = 0 while j < i: try: P[j] = iters[j].next() yield self(P) j = 0 except StopIteration: if (ftype == False): # if rational field iters[j] = R.range_by_height(bound) # reset else: # if number field iters[j] = R.elements_of_bounded_height(bound) # reset iters[j].next() # put at zero P[j] = zero j += 1 i -= 1
def points(self, **kwds): """ Return some or all rational points of a projective scheme. For dimension 0 subschemes points are determined through a groebner basis calculation. For schemes or subschemes with dimension greater than 1 points are determined through enumeration up to the specified bound. INPUT: kwds: - ``bound`` - real number (optional, default=0). The bound for the coordinates for subschemes with dimension at least 1. - ``precision`` - integer (optional, default=53). The precision to use to compute the elements of bounded height for number fields. - ``point_tolerance`` - positive real number (optional, default=10^(-10)). For numerically inexact fields, two points are considered the same if their coordinates are within tolerance. - ``zero_tolerance`` - positive real number (optional, default=10^(-10)). For numerically inexact fields, points are on the subscheme if they satisfy the equations to within tolerance. - ``tolerance`` - a rational number in (0,1] used in doyle-krumm algorithm-4 for enumeration over number fields. OUTPUT: - a list of rational points of a projective scheme .. WARNING:: For numerically inexact fields such as ComplexField or RealField the list of points returned is very likely to be incomplete. It may also contain repeated points due to tolerances. EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: P(QQ).points(bound=4) [(-4 : 1), (-3 : 1), (-2 : 1), (-3/2 : 1), (-4/3 : 1), (-1 : 1), (-3/4 : 1), (-2/3 : 1), (-1/2 : 1), (-1/3 : 1), (-1/4 : 1), (0 : 1), (1/4 : 1), (1/3 : 1), (1/2 : 1), (2/3 : 1), (3/4 : 1), (1 : 0), (1 : 1), (4/3 : 1), (3/2 : 1), (2 : 1), (3 : 1), (4 : 1)] :: sage: u = QQ['u'].0 sage: K.<v> = NumberField(u^2 + 3) sage: P.<x,y,z> = ProjectiveSpace(K,2) sage: len(P(K).points(bound=1.8)) 381 :: sage: P1 = ProjectiveSpace(GF(2),1) sage: F.<a> = GF(4,'a') sage: P1(F).points() [(0 : 1), (1 : 0), (1 : 1), (a : 1), (a + 1 : 1)] :: sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: E = P.subscheme([(y^3-y*z^2) - (x^3-x*z^2),(y^3-y*z^2) + (x^3-x*z^2)]) sage: E(P.base_ring()).points() [(-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 : 1)] :: sage: P.<x,y,z> = ProjectiveSpace(CC, 2) sage: E = P.subscheme([y^3 - x^3 - x*z^2, x*y*z]) sage: L=E(P.base_ring()).points(); sorted(L, key=str) verbose 0 (71: projective_homset.py, points) Warning: computations in the numerical fields are inexact;points may be computed partially or incorrectly. [(-0.500000000000000 + 0.866025403784439*I : 1.00000000000000 : 0.000000000000000), (-0.500000000000000 - 0.866025403784439*I : 1.00000000000000 : 0.000000000000000), (-1.00000000000000*I : 0.000000000000000 : 1.00000000000000), (0.000000000000000 : 0.000000000000000 : 1.00000000000000), (1.00000000000000 : 1.00000000000000 : 0.000000000000000), (1.00000000000000*I : 0.000000000000000 : 1.00000000000000)] sage: L[0].codomain() Projective Space of dimension 2 over Complex Field with 53 bits of precision :: sage: P.<x,y,z> = ProjectiveSpace(CDF, 2) sage: E = P.subscheme([y^2 + x^2 + z^2, x*y*z]) sage: len(E(P.base_ring()).points()) verbose 0 (71: projective_homset.py, points) Warning: computations in the numerical fields are inexact;points may be computed partially or incorrectly. 6 """ from sage.schemes.projective.projective_space import is_ProjectiveSpace X = self.codomain() if not is_ProjectiveSpace(X) and X.base_ring() in Fields(): if hasattr(X.base_ring(), 'precision'): numerical = True verbose( "Warning: computations in the numerical fields are inexact;points may be computed partially or incorrectly.", level=0) pt_tol = RR(kwds.pop('point_tolerance', 10**(-10))) zero_tol = RR(kwds.pop('zero_tolerance', 10**(-10))) if pt_tol <= 0 or zero_tol <= 0: raise ValueError("tolerance must be positive") else: numerical = False #Then it must be a subscheme dim_ideal = X.defining_ideal().dimension() if dim_ideal < 1: # no points return [] if dim_ideal == 1: # if X zero-dimensional rat_points = set() PS = X.ambient_space() N = PS.dimension_relative() BR = X.base_ring() #need a lexicographic ordering for elimination R = PolynomialRing(BR, N + 1, PS.variable_names(), order='lex') I = R.ideal(X.defining_polynomials()) I0 = R.ideal(0) #Determine the points through elimination #This is much faster than using the I.variety() function on each affine chart. for k in range(N + 1): #create the elimination ideal for the kth affine patch G = I.substitute({R.gen(k): 1}).groebner_basis() if G != [1]: P = {} #keep track that we know the kth coordinate is 1 P.update({R.gen(k): 1}) points = [P] #work backwards from solving each equation for the possible #values of the next coordinate for i in range(len(G) - 1, -1, -1): new_points = [] good = 0 for P in points: #substitute in our dictionary entry that has the values #of coordinates known so far. This results in a single #variable polynomial (by elimination) L = G[i].substitute(P) if R(L).degree() > 0: if numerical: for pol in L.univariate_polynomial( ).roots(multiplicities=False): good = 1 r = L.variables()[0] varindex = R.gens().index(r) P.update({R.gen(varindex): pol}) new_points.append(copy(P)) else: L = L.factor() #the linear factors give the possible rational values of #this coordinate for pol, pow in L: if pol.degree() == 1 and len( pol.variables()) == 1: good = 1 r = pol.variables()[0] varindex = R.gens().index(r) #add this coordinates information to #each dictionary entry P.update({ R.gen(varindex): -pol.constant_coefficient( ) / pol.monomial_coefficient(r) }) new_points.append(copy(P)) else: new_points.append(P) good = 1 if good: points = new_points #the dictionary entries now have values for all coordinates #they are the rational solutions to the equations #make them into projective points for i in range(len(points)): if numerical: if len(points[i]) == N + 1: S = PS([ points[i][R.gen(j)] for j in range(N + 1) ]) S.normalize_coordinates() if all( g(list(S)) < zero_tol for g in X.defining_polynomials()): rat_points.add(S) else: if len(points[i]) == N + 1 and I.subs( points[i]) == I0: S = X([ points[i][R.gen(j)] for j in range(N + 1) ]) S.normalize_coordinates() rat_points.add(S) # remove duplicate element using tolerance if numerical: dupl_points = list(rat_points) for i in range(len(dupl_points)): u = dupl_points[i] for j in range(i + 1, len(dupl_points)): v = dupl_points[j] if all((u[k] - v[k]).abs() < pt_tol for k in range(len(u))): rat_points.remove(u) break rat_points = sorted(rat_points) return rat_points R = self.value_ring() B = kwds.pop('bound', 0) tol = kwds.pop('tolerance', 1e-2) prec = kwds.pop('precision', 53) if is_RationalField(R): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified" % B) if isinstance(X, AlgebraicScheme_subscheme ): # sieve should only be called for subschemes from sage.schemes.projective.projective_rational_point import sieve return sieve(X, B) else: from sage.schemes.projective.projective_rational_point import enum_projective_rational_field return enum_projective_rational_field(self, B) elif R in NumberFields(): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified" % B) from sage.schemes.projective.projective_rational_point import enum_projective_number_field return enum_projective_number_field(self, bound=B, tolerance=tol, precision=prec) elif is_FiniteField(R): from sage.schemes.projective.projective_rational_point import enum_projective_finite_field return enum_projective_finite_field(self.extended_codomain()) else: raise TypeError("unable to enumerate points over %s" % R)
def points(self, **kwds): r""" Return some or all rational points of an affine scheme. For dimension 0 subschemes points are determined through a groebner basis calculation. For schemes or subschemes with dimension greater than 1 points are determined through enumeration up to the specified bound. Over a finite field, all points are returned. Over an infinite field, all points satisfying the bound are returned. For a zero-dimensional subscheme, all points are returned regardless of whether the field is infinite or not. For number fields, this uses the Doyle-Krumm algorithm 4 (algorithm 5 for imaginary quadratic) for computing algebraic numbers up to a given height [Doyle-Krumm]_. The algorithm requires floating point arithmetic, so the user is allowed to specify the precision for such calculations. Additionally, due to floating point issues, points slightly larger than the bound may be returned. This can be controlled by lowering the tolerance. INPUT: kwds: - ``bound`` - real number (optional, default: 0). The bound for the height of the coordinates. Only used for subschemes with dimension at least 1. - ``zero_tolerance`` - positive real number (optional, default=10^(-10)). For numerically inexact fields, points are on the subscheme if they satisfy the equations to within tolerance. - ``tolerance`` - a rational number in (0,1] used in doyle-krumm algorithm-4 for enumeration over number fields. - ``precision`` - the precision to use for computing the elements of bounded height of number fields. OUTPUT: - a list of rational points of a affine scheme .. WARNING:: For numerically inexact fields such as ComplexField or RealField the list of points returned is very likely to be incomplete. It may also contain repeated points due to tolerance. EXAMPLES: The bug reported at #11526 is fixed:: sage: A2 = AffineSpace(ZZ, 2) sage: F = GF(3) sage: A2(F).points() [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)] :: sage: A.<x,y> = ZZ[] sage: I = A.ideal(x^2-y^2-1) sage: V = AffineSpace(ZZ, 2) sage: X = V.subscheme(I) sage: M = X(ZZ) sage: M.points(bound=1) [(-1, 0), (1, 0)] :: sage: u = QQ['u'].0 sage: K.<v> = NumberField(u^2 + 3) sage: A.<x,y> = AffineSpace(K, 2) sage: len(A(K).points(bound=2)) 1849 :: sage: A.<x,y> = AffineSpace(QQ, 2) sage: E = A.subscheme([x^2 + y^2 - 1, y^2 - x^3 + x^2 + x - 1]) sage: E(A.base_ring()).points() [(-1, 0), (0, -1), (0, 1), (1, 0)] :: sage: A.<x,y> = AffineSpace(CC, 2) sage: E = A.subscheme([y^3 - x^3 - x^2, x*y]) sage: E(A.base_ring()).points() verbose 0 (124: affine_homset.py, points) Warning: computations in the numerical fields are inexact;points may be computed partially or incorrectly. [(-1.00000000000000, 0.000000000000000), (0.000000000000000, 0.000000000000000)] :: sage: A.<x1,x2> = AffineSpace(CDF, 2) sage: E = A.subscheme([x1^2 + x2^2 + x1*x2, x1 + x2]) sage: E(A.base_ring()).points() verbose 0 (124: affine_homset.py, points) Warning: computations in the numerical fields are inexact;points may be computed partially or incorrectly. [(0.0, 0.0)] """ from sage.schemes.affine.affine_space import is_AffineSpace X = self.codomain() if not is_AffineSpace(X) and X.base_ring() in Fields(): if hasattr(X.base_ring(), 'precision'): numerical = True verbose("Warning: computations in the numerical fields are inexact;points may be computed partially or incorrectly.", level=0) zero_tol = RR(kwds.pop('zero_tolerance', 10**(-10))) if zero_tol <= 0: raise ValueError("tolerance must be positive") else: numerical = False # Then X must be a subscheme dim_ideal = X.defining_ideal().dimension() if dim_ideal < 0: # no points return [] if dim_ideal == 0: # if X zero-dimensional rat_points = [] AS = X.ambient_space() N = AS.dimension_relative() BR = X.base_ring() #need a lexicographic ordering for elimination R = PolynomialRing(BR, N, AS.gens(), order='lex') I = R.ideal(X.defining_polynomials()) I0 = R.ideal(0) #Determine the points through elimination #This is much faster than using the I.variety() function on each affine chart. G = I.groebner_basis() if G != [1]: P = {} points = [P] #work backwards from solving each equation for the possible #values of the next coordinate for i in range(len(G) - 1, -1, -1): new_points = [] good = 0 for P in points: #substitute in our dictionary entry that has the values #of coordinates known so far. This results in a single #variable polynomial (by elimination) L = G[i].substitute(P) if R(L).degree() > 0: if numerical: for pol in L.univariate_polynomial().roots(multiplicities=False): r = L.variables()[0] varindex = R.gens().index(r) P.update({R.gen(varindex):pol}) new_points.append(copy(P)) good = 1 else: L = L.factor() #the linear factors give the possible rational values of #this coordinate for pol, pow in L: if pol.degree() == 1 and len(pol.variables()) == 1: good = 1 r = pol.variables()[0] varindex = R.gens().index(r) #add this coordinates information to #each dictionary entry P.update({R.gen(varindex):-pol.constant_coefficient() / pol.monomial_coefficient(r)}) new_points.append(copy(P)) else: new_points.append(P) good = 1 if good: points = new_points #the dictionary entries now have values for all coordinates #they are the rational solutions to the equations #make them into affine points for i in range(len(points)): if numerical: if len(points[i]) == N: S = AS([points[i][R.gen(j)] for j in range(N)]) if all([g(list(S)) < zero_tol for g in X.defining_polynomials()]): rat_points.append(S) else: if len(points[i]) == N and I.subs(points[i]) == I0: S = X([points[i][R.gen(j)] for j in range(N)]) rat_points.append(S) rat_points = sorted(rat_points) return rat_points R = self.value_ring() B = kwds.pop('bound', 0) tol = kwds.pop('tolerance', 1e-2) prec = kwds.pop('precision', 53) if is_RationalField(R) or R == ZZ: if not B > 0: raise TypeError("a positive bound B (= %s) must be specified"%B) from sage.schemes.affine.affine_rational_point import enum_affine_rational_field return enum_affine_rational_field(self,B) if R in NumberFields(): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified"%B) from sage.schemes.affine.affine_rational_point import enum_affine_number_field return enum_affine_number_field(self, bound=B, tolerance=tol, precision=prec) elif is_FiniteField(R): from sage.schemes.affine.affine_rational_point import enum_affine_finite_field return enum_affine_finite_field(self) else: raise TypeError("unable to enumerate points over %s"%R)
def Conic(base_field, F=None, names=None, unique=True): r""" Return the plane projective conic curve defined by ``F`` over ``base_field``. The input form ``Conic(F, names=None)`` is also accepted, in which case the fraction field of the base ring of ``F`` is used as base field. INPUT: - ``base_field`` -- The base field of the conic. - ``names`` -- a list, tuple, or comma separated string of three variable names specifying the names of the coordinate functions of the ambient space `\Bold{P}^3`. If not specified or read off from ``F``, then this defaults to ``'x,y,z'``. - ``F`` -- a polynomial, list, matrix, ternary quadratic form, or list or tuple of 5 points in the plane. If ``F`` is a polynomial or quadratic form, then the output is the curve in the projective plane defined by ``F = 0``. If ``F`` is a polynomial, then it must be a polynomial of degree at most 2 in 2 variables, or a homogeneous polynomial in of degree 2 in 3 variables. If ``F`` is a matrix, then the output is the zero locus of `(x,y,z) F (x,y,z)^t`. If ``F`` is a list of coefficients, then it has length 3 or 6 and gives the coefficients of the monomials `x^2, y^2, z^2` or all 6 monomials `x^2, xy, xz, y^2, yz, z^2` in lexicographic order. If ``F`` is a list of 5 points in the plane, then the output is a conic through those points. - ``unique`` -- Used only if ``F`` is a list of points in the plane. If the conic through the points is not unique, then raise ``ValueError`` if and only if ``unique`` is True OUTPUT: A plane projective conic curve defined by ``F`` over a field. EXAMPLES: Conic curves given by polynomials :: sage: X,Y,Z = QQ['X,Y,Z'].gens() sage: Conic(X^2 - X*Y + Y^2 - Z^2) Projective Conic Curve over Rational Field defined by X^2 - X*Y + Y^2 - Z^2 sage: x,y = GF(7)['x,y'].gens() sage: Conic(x^2 - x + 2*y^2 - 3, 'U,V,W') Projective Conic Curve over Finite Field of size 7 defined by U^2 + 2*V^2 - U*W - 3*W^2 Conic curves given by matrices :: sage: Conic(matrix(QQ, [[1, 2, 0], [4, 0, 0], [7, 0, 9]]), 'x,y,z') Projective Conic Curve over Rational Field defined by x^2 + 6*x*y + 7*x*z + 9*z^2 sage: x,y,z = GF(11)['x,y,z'].gens() sage: C = Conic(x^2+y^2-2*z^2); C Projective Conic Curve over Finite Field of size 11 defined by x^2 + y^2 - 2*z^2 sage: Conic(C.symmetric_matrix(), 'x,y,z') Projective Conic Curve over Finite Field of size 11 defined by x^2 + y^2 - 2*z^2 Conics given by coefficients :: sage: Conic(QQ, [1,2,3]) Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + 3*z^2 sage: Conic(GF(7), [1,2,3,4,5,6], 'X') Projective Conic Curve over Finite Field of size 7 defined by X0^2 + 2*X0*X1 - 3*X1^2 + 3*X0*X2 - 2*X1*X2 - X2^2 The conic through a set of points :: sage: C = Conic(QQ, [[10,2],[3,4],[-7,6],[7,8],[9,10]]); C Projective Conic Curve over Rational Field defined by x^2 + 13/4*x*y - 17/4*y^2 - 35/2*x*z + 91/4*y*z - 37/2*z^2 sage: C.rational_point() (10 : 2 : 1) sage: C.point([3,4]) (3 : 4 : 1) sage: a=AffineSpace(GF(13),2) sage: Conic([a([x,x^2]) for x in range(5)]) Projective Conic Curve over Finite Field of size 13 defined by x^2 - y*z """ if not (base_field is None or isinstance(base_field, IntegralDomain)): if names is None: names = F F = base_field base_field = None if isinstance(F, (list, tuple)): if len(F) == 1: return Conic(base_field, F[0], names) if names is None: names = 'x,y,z' if len(F) == 5: L = [] for f in F: if isinstance(f, SchemeMorphism_point_affine): C = Sequence(f, universe=base_field) if len(C) != 2: raise TypeError("points in F (=%s) must be planar" % F) C.append(1) elif isinstance(f, SchemeMorphism_point_projective_field): C = Sequence(f, universe=base_field) elif isinstance(f, (list, tuple)): C = Sequence(f, universe=base_field) if len(C) == 2: C.append(1) else: raise TypeError("F (=%s) must be a sequence of planar " \ "points" % F) if len(C) != 3: raise TypeError("points in F (=%s) must be planar" % F) P = C.universe() if not isinstance(P, IntegralDomain): raise TypeError("coordinates of points in F (=%s) must " \ "be in an integral domain" % F) L.append( Sequence([ C[0]**2, C[0] * C[1], C[0] * C[2], C[1]**2, C[1] * C[2], C[2]**2 ], P.fraction_field())) M = Matrix(L) if unique and M.rank() != 5: raise ValueError("points in F (=%s) do not define a unique " \ "conic" % F) con = Conic(base_field, Sequence(M.right_kernel().gen()), names) con.point(F[0]) return con F = Sequence(F, universe=base_field) base_field = F.universe().fraction_field() temp_ring = PolynomialRing(base_field, 3, names) (x, y, z) = temp_ring.gens() if len(F) == 3: return Conic(F[0] * x**2 + F[1] * y**2 + F[2] * z**2) if len(F) == 6: return Conic(F[0]*x**2 + F[1]*x*y + F[2]*x*z + F[3]*y**2 + \ F[4]*y*z + F[5]*z**2) raise TypeError("F (=%s) must be a sequence of 3 or 6" \ "coefficients" % F) if is_QuadraticForm(F): F = F.matrix() if is_Matrix(F) and F.is_square() and F.ncols() == 3: if names is None: names = 'x,y,z' temp_ring = PolynomialRing(F.base_ring(), 3, names) F = vector(temp_ring.gens()) * F * vector(temp_ring.gens()) if not is_MPolynomial(F): raise TypeError("F (=%s) must be a three-variable polynomial or " \ "a sequence of points or coefficients" % F) if F.total_degree() != 2: raise TypeError("F (=%s) must have degree 2" % F) if base_field is None: base_field = F.base_ring() if not isinstance(base_field, IntegralDomain): raise ValueError("Base field (=%s) must be a field" % base_field) base_field = base_field.fraction_field() if names is None: names = F.parent().variable_names() pol_ring = PolynomialRing(base_field, 3, names) if F.parent().ngens() == 2: (x, y, z) = pol_ring.gens() F = pol_ring(F(x / z, y / z) * z**2) if F == 0: raise ValueError("F must be nonzero over base field %s" % base_field) if F.total_degree() != 2: raise TypeError("F (=%s) must have degree 2 over base field %s" % \ (F, base_field)) if F.parent().ngens() == 3: P2 = ProjectiveSpace(2, base_field, names) if is_PrimeFiniteField(base_field): return ProjectiveConic_prime_finite_field(P2, F) if is_FiniteField(base_field): return ProjectiveConic_finite_field(P2, F) if is_RationalField(base_field): return ProjectiveConic_rational_field(P2, F) if is_NumberField(base_field): return ProjectiveConic_number_field(P2, F) if is_FractionField(base_field) and (is_PolynomialRing( base_field.ring()) or is_MPolynomialRing(base_field.ring())): return ProjectiveConic_rational_function_field(P2, F) return ProjectiveConic_field(P2, F) raise TypeError("Number of variables of F (=%s) must be 2 or 3" % F)
def points(self, B=0): r""" Return some or all rational points of an affine scheme. INPUT: - ``B`` -- integer (optional, default: 0). The bound for the height of the coordinates. OUTPUT: - If the base ring is a finite field: all points of the scheme, given by coordinate tuples. - If the base ring is `\QQ` or `\ZZ`: the subset of points whose coordinates have height ``B`` or less. EXAMPLES: The bug reported at #11526 is fixed:: sage: A2 = AffineSpace(ZZ, 2) sage: F = GF(3) sage: A2(F).points() [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)] sage: R = ZZ sage: A.<x,y> = R[] sage: I = A.ideal(x^2-y^2-1) sage: V = AffineSpace(R, 2) sage: X = V.subscheme(I) sage: M = X(R) sage: M.points(1) [(-1, 0), (1, 0)] :: sage: u = QQ['u'].0 sage: K.<v> = NumberField(u^2 + 3) sage: A.<x,y> = AffineSpace(K, 2) sage: len(A(K).points(9)) 361 :: sage: A.<x,y> = AffineSpace(QQ, 2) sage: E = A.subscheme([x^2 + y^2 - 1, y^2 - x^3 + x^2 + x - 1]) sage: E(A.base_ring()).points() [(-1, 0), (0, -1), (0, 1), (1, 0)] """ X = self.codomain() from sage.schemes.affine.affine_space import is_AffineSpace if not is_AffineSpace(X) and X.base_ring() in Fields(): # Then X must be a subscheme dim_ideal = X.defining_ideal().dimension() if dim_ideal < 0: # no points return [] if dim_ideal == 0: # if X zero-dimensional N = len(X.ambient_space().gens()) S = X.defining_polynomials()[0].parent() R = PolynomialRing(S.base_ring(), 's', N, order='lex') phi = S.hom(R.gens(),R) J = R.ideal([phi(t) for t in X.defining_polynomials()]) D = J.variety() points = [] for d in D: P = [d[t] for t in R.gens()] points.append(X(P)) points.sort() return points R = self.value_ring() if is_RationalField(R) or R == ZZ: if not B > 0: raise TypeError("a positive bound B (= %s) must be specified"%B) from sage.schemes.affine.affine_rational_point import enum_affine_rational_field return enum_affine_rational_field(self,B) if R in NumberFields(): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified"%B) from sage.schemes.affine.affine_rational_point import enum_affine_number_field return enum_affine_number_field(self,B) elif is_FiniteField(R): from sage.schemes.affine.affine_rational_point import enum_affine_finite_field return enum_affine_finite_field(self) else: raise TypeError("unable to enumerate points over %s"%R)
def ProjectiveSpace(n, R=None, names='x'): r""" Return projective space of dimension `n` over the ring `R`. EXAMPLES: The dimension and ring can be given in either order. :: sage: ProjectiveSpace(3, QQ) Projective Space of dimension 3 over Rational Field sage: ProjectiveSpace(5, QQ) Projective Space of dimension 5 over Rational Field sage: P = ProjectiveSpace(2, QQ, names='XYZ'); P Projective Space of dimension 2 over Rational Field sage: P.coordinate_ring() Multivariate Polynomial Ring in X, Y, Z over Rational Field The divide operator does base extension. :: sage: ProjectiveSpace(5)/GF(17) Projective Space of dimension 5 over Finite Field of size 17 The default base ring is `\ZZ`. :: sage: ProjectiveSpace(5) Projective Space of dimension 5 over Integer Ring There is also an projective space associated each polynomial ring. :: sage: R = GF(7)['x,y,z'] sage: P = ProjectiveSpace(R); P Projective Space of dimension 2 over Finite Field of size 7 sage: P.coordinate_ring() Multivariate Polynomial Ring in x, y, z over Finite Field of size 7 sage: P.coordinate_ring() is R True :: sage: ProjectiveSpace(3, Zp(5), 'y') Projective Space of dimension 3 over 5-adic Ring with capped relative precision 20 :: sage: ProjectiveSpace(2,QQ,'x,y,z') Projective Space of dimension 2 over Rational Field :: sage: PS.<x,y>=ProjectiveSpace(1,CC) sage: PS Projective Space of dimension 1 over Complex Field with 53 bits of precision Projective spaces are not cached, i.e., there can be several with the same base ring and dimension (to facilitate gluing constructions). """ if is_MPolynomialRing(n) and R is None: A = ProjectiveSpace(n.ngens() - 1, n.base_ring()) A._coordinate_ring = n return A if isinstance(R, (int, long, Integer)): n, R = R, n if R is None: R = ZZ # default is the integers if R in _Fields: if is_FiniteField(R): return ProjectiveSpace_finite_field(n, R, names) if is_RationalField(R): return ProjectiveSpace_rational_field(n, R, names) else: return ProjectiveSpace_field(n, R, names) elif is_CommutativeRing(R): return ProjectiveSpace_ring(n, R, names) else: raise TypeError("R (=%s) must be a commutative ring" % R)
def ProjectiveSpace(n, R=None, names='x'): r""" Return projective space of dimension `n` over the ring `R`. EXAMPLES: The dimension and ring can be given in either order. :: sage: ProjectiveSpace(3, QQ) Projective Space of dimension 3 over Rational Field sage: ProjectiveSpace(5, QQ) Projective Space of dimension 5 over Rational Field sage: P = ProjectiveSpace(2, QQ, names='XYZ'); P Projective Space of dimension 2 over Rational Field sage: P.coordinate_ring() Multivariate Polynomial Ring in X, Y, Z over Rational Field The divide operator does base extension. :: sage: ProjectiveSpace(5)/GF(17) Projective Space of dimension 5 over Finite Field of size 17 The default base ring is `\ZZ`. :: sage: ProjectiveSpace(5) Projective Space of dimension 5 over Integer Ring There is also an projective space associated each polynomial ring. :: sage: R = GF(7)['x,y,z'] sage: P = ProjectiveSpace(R); P Projective Space of dimension 2 over Finite Field of size 7 sage: P.coordinate_ring() Multivariate Polynomial Ring in x, y, z over Finite Field of size 7 sage: P.coordinate_ring() is R True :: sage: ProjectiveSpace(3, Zp(5), 'y') Projective Space of dimension 3 over 5-adic Ring with capped relative precision 20 :: sage: ProjectiveSpace(2,QQ,'x,y,z') Projective Space of dimension 2 over Rational Field :: sage: PS.<x,y>=ProjectiveSpace(1,CC) sage: PS Projective Space of dimension 1 over Complex Field with 53 bits of precision Projective spaces are not cached, i.e., there can be several with the same base ring and dimension (to facilitate gluing constructions). """ if is_MPolynomialRing(n) and R is None: A = ProjectiveSpace(n.ngens()-1, n.base_ring()) A._coordinate_ring = n return A if isinstance(R, (int, long, Integer)): n, R = R, n if R is None: R = ZZ # default is the integers if R in _Fields: if is_FiniteField(R): return ProjectiveSpace_finite_field(n, R, names) if is_RationalField(R): return ProjectiveSpace_rational_field(n, R, names) else: return ProjectiveSpace_field(n, R, names) elif is_CommutativeRing(R): return ProjectiveSpace_ring(n, R, names) else: raise TypeError("R (=%s) must be a commutative ring"%R)
def has_rational_point(self, point=False, obstruction=False, algorithm='default', read_cache=True): r""" Returns ``True`` if and only if ``self`` has a point defined over its base field `B`. If ``point`` and ``obstruction`` are both False (default), then the output is a boolean ``out`` saying whether ``self`` has a rational point. If ``point`` or ``obstruction`` is True, then the output is a pair ``(out, S)``, where ``out`` is as above and: - if ``point`` is True and ``self`` has a rational point, then ``S`` is a rational point, - if ``obstruction`` is True, ``self`` has no rational point, then ``S`` is a prime or infinite place of `B` such that no rational point exists over the completion at ``S``. Points and obstructions are cached whenever they are found. Cached information is used for the output if available, but only if ``read_cache`` is True. ALGORITHM: The parameter ``algorithm`` specifies the algorithm to be used: - ``'rnfisnorm'`` -- Use PARI's rnfisnorm (cannot be combined with ``obstruction = True``) - ``'local'`` -- Check if a local solution exists for all primes and infinite places of `B` and apply the Hasse principle. (Cannot be combined with ``point = True``.) - ``'default'`` -- Use algorithm ``'rnfisnorm'`` first. Then, if no point exists and obstructions are requested, use algorithm ``'local'`` to find an obstruction. - ``'magma'`` (requires Magma to be installed) -- delegates the task to the Magma computer algebra system. EXAMPLES: An example over `\QQ` :: sage: C = Conic(QQ, [1, 113922743, -310146482690273725409]) sage: C.has_rational_point(point = True) (True, (-76842858034579/5424 : -5316144401/5424 : 1)) sage: C.has_rational_point(algorithm = 'local', read_cache = False) True Examples over number fields:: sage: K.<i> = QuadraticField(-1) sage: C = Conic(K, [1, 3, -5]) sage: C.has_rational_point(point = True, obstruction = True) (False, Fractional ideal (-i - 2)) sage: C.has_rational_point(algorithm = "rnfisnorm") False sage: C.has_rational_point(algorithm = "rnfisnorm", obstruction = True, read_cache=False) Traceback (most recent call last): ... ValueError: Algorithm rnfisnorm cannot be combined with obstruction = True in has_rational_point sage: P.<x> = QQ[] sage: L.<b> = NumberField(x^3-5) sage: C = Conic(L, [1, 2, -3]) sage: C.has_rational_point(point = True, algorithm = 'rnfisnorm') (True, (5/3 : -1/3 : 1)) sage: K.<a> = NumberField(x^4+2) sage: Conic(QQ, [4,5,6]).has_rational_point() False sage: Conic(K, [4,5,6]).has_rational_point() True sage: Conic(K, [4,5,6]).has_rational_point(algorithm='magma', read_cache=False) # optional - magma True TESTS: Create a bunch of conics over number fields and check whether ``has_rational_point`` runs without errors for algorithms ``'rnfisnorm'`` and ``'local'``. Check if all points returned are valid. If Magma is available, then also check if the output agrees with Magma. :: sage: P.<X> = QQ[] sage: Q = P.fraction_field() sage: c = [1, X/2, 1/X] sage: l = Sequence(cartesian_product_iterator([c for i in range(3)])) sage: l = l + [[X, 1, 1, 1, 1, 1]] + [[X, 1/5, 1, 1, 2, 1]] sage: K.<a> = QuadraticField(-23) sage: L.<b> = QuadraticField(19) sage: M.<c> = NumberField(X^3+3*X+1) sage: m = [[Q(b)(F.gen()) for b in a] for a in l for F in [K, L, M]] sage: d = [] sage: c = [] sage: c = [Conic(a) for a in m if a != [0,0,0]] sage: d = [C.has_rational_point(algorithm = 'rnfisnorm', point = True) for C in c] # long time: 3.3 seconds sage: all(c[k].defining_polynomial()(Sequence(d[k][1])) == 0 for k in range(len(d)) if d[k][0]) True sage: [C.has_rational_point(algorithm='local', read_cache=False) for C in c] == [o[0] for o in d] # long time: 5 seconds True sage: [C.has_rational_point(algorithm = 'magma', read_cache=False) for C in c] == [o[0] for o in d] # long time: 3 seconds, optional - magma True Create a bunch of conics that are known to have rational points already over `\QQ` and check if points are found by ``has_rational_point``. :: sage: l = Sequence(cartesian_product_iterator([[-1, 0, 1] for i in range(3)])) sage: K.<a> = QuadraticField(-23) sage: L.<b> = QuadraticField(19) sage: M.<c> = NumberField(x^5+3*x+1) sage: m = [[F(b) for b in a] for a in l for F in [K, L, M]] sage: c = [Conic(a) for a in m if a != [0,0,0] and a != [1,1,1] and a != [-1,-1,-1]] sage: assert all(C.has_rational_point(algorithm = 'rnfisnorm') for C in c) sage: assert all(C.defining_polynomial()(Sequence(C.has_rational_point(point = True)[1])) == 0 for C in c) sage: assert all(C.has_rational_point(algorithm='local', read_cache=False) for C in c) # long time: 1 second """ if read_cache: if self._rational_point is not None: if point or obstruction: return True, self._rational_point else: return True if self._local_obstruction is not None: if point or obstruction: return False, self._local_obstruction else: return False if (not point and not self._finite_obstructions and not self._infinite_obstructions): if obstruction: return True, None return True if self.has_singular_point(): if point: return self.has_singular_point(point=True) if obstruction: return True, None return True B = self.base_ring() if algorithm == 'default': ret = self.has_rational_point(point=True, obstruction=False, algorithm='rnfisnorm', read_cache=False) if ret[0]: if point or obstruction: return ret return True if obstruction: ret = self.has_rational_point(point=False, obstruction=True, algorithm='local', read_cache=False) if ret[0]: raise RuntimeError("Outputs of algorithms in " "has_rational_point disagree " "for conic %s" % self) return ret if point: return False, None return False if algorithm == 'local': if point: raise ValueError("Algorithm 'local' cannot be combined " "with point = True in has_rational_point") obs = self.local_obstructions(infinite=True, finite=False, read_cache=read_cache) if obs: if obstruction: return False, obs[0] return False obs = self.local_obstructions(read_cache=read_cache) if not obs: if obstruction: return True, None return True if obstruction: return False, obs[0] return False if algorithm == 'rnfisnorm': from sage.modules.free_module_element import vector if obstruction: raise ValueError("Algorithm rnfisnorm cannot be combined " "with obstruction = True in " "has_rational_point") D, T = self.diagonal_matrix() abc = [D[0, 0], D[1, 1], D[2, 2]] for j in range(3): if abc[j] == 0: pt = self.point(T * vector({2: 0, j: 1})) if point or obstruction: return True, pt return True if (-abc[1] / abc[0]).is_square(): pt = self.point(T * vector([(-abc[1] / abc[0]).sqrt(), 1, 0])) if point or obstruction: return True, pt return True if (-abc[2] / abc[0]).is_square(): pt = self.point(T * vector([(-abc[2] / abc[0]).sqrt(), 0, 1])) if point or obstruction: return True, pt return True if is_RationalField(B): K = B [KtoB, BtoK] = [K.hom(K) for i in range(2)] else: K = B.absolute_field('Y') [KtoB, BtoK] = K.structure() X = PolynomialRing(K, 'X').gen() d = BtoK(-abc[1] / abc[0]) den = d.denominator() L = K.extension(X**2 - d * den**2, names='y') isnorm = BtoK(-abc[2] / abc[0]).is_norm(L, element=True) if isnorm[0]: pt = self.point( T * vector([KtoB(isnorm[1][0]), KtoB(isnorm[1][1] * den), 1])) if point: return True, pt return True if point: return False, None return False if algorithm == 'qfsolve': raise TypeError("Algorithm qfsolve in has_rational_point only " "for conics over QQ, not over %s" % B) if obstruction: raise ValueError("Invalid combination: obstruction=True and " "algorithm=%s" % algorithm) return ProjectiveConic_field.has_rational_point(self, point=point, algorithm=algorithm, read_cache=False)
def points_of_bounded_height(self,bound, prec=53): r""" Returns an iterator of the points in self of absolute height of at most the given bound. Bound check is strict for the rational field. Requires self to be projective space over a number field. Uses the Doyle-Krumm algorithm for computing algebraic numbers up to a given height [Doyle-Krumm]. INPUT: - ``bound`` - a real number - ``prec`` - the precision to use to compute the elements of bounded height for number fields OUTPUT: - an iterator of points in self .. WARNING:: In the current implementation, the output of the [Doyle-Krumm] algorithm cannot be guaranteed to be correct due to the necessity of floating point computations. In some cases, the default 53-bit precision is considerably lower than would be required for the algorithm to generate correct output. EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: list(P.points_of_bounded_height(5)) [(0 : 1), (1 : 1), (-1 : 1), (1/2 : 1), (-1/2 : 1), (2 : 1), (-2 : 1), (1/3 : 1), (-1/3 : 1), (3 : 1), (-3 : 1), (2/3 : 1), (-2/3 : 1), (3/2 : 1), (-3/2 : 1), (1/4 : 1), (-1/4 : 1), (4 : 1), (-4 : 1), (3/4 : 1), (-3/4 : 1), (4/3 : 1), (-4/3 : 1), (1 : 0)] :: sage: u = QQ['u'].0 sage: P.<x,y,z> = ProjectiveSpace(NumberField(u^2 - 2,'v'), 2) sage: len(list(P.points_of_bounded_height(1.5))) 57 """ if (is_RationalField(self.base_ring())): ftype = False # stores whether the field is a number field or the rational field elif (self.base_ring() in NumberFields()): # true for rational field as well, so check is_RationalField first ftype = True else: raise NotImplementedError("self must be projective space over a number field.") bound = bound**(self.base_ring().absolute_degree()) # convert to relative height n = self.dimension_relative() R = self.base_ring() zero = R(0) i = n while not i < 0: P = [ zero for _ in range(i) ] + [ R(1) ] + [ zero for _ in range(n-i) ] yield self(P) if (ftype == False): # if rational field iters = [ R.range_by_height(bound) for _ in range(i) ] else: # if number field iters = [ R.elements_of_bounded_height(bound, precision=prec) for _ in range(i) ] for x in iters: next(x) # put at zero j = 0 while j < i: try: P[j] = next(iters[j]) yield self(P) j = 0 except StopIteration: if (ftype == False): # if rational field iters[j] = R.range_by_height(bound) # reset else: # if number field iters[j] = R.elements_of_bounded_height(bound, precision=prec) # reset next(iters[j]) # put at zero P[j] = zero j += 1 i -= 1
def points_of_bounded_height(self,bound): r""" Returns an iterator of the points in this affine space of absolute height of at most the given bound. Bound check is strict for the rational field. Requires this space to be affine space over a number field. Uses the Doyle-Krumm algorithm for computing algebraic numbers up to a given height [Doyle-Krumm]_. INPUT: - ``bound`` - a real number. OUTPUT: - an iterator of points in self. EXAMPLES:: sage: A.<x,y> = AffineSpace(QQ, 2) sage: list(A.points_of_bounded_height(3)) [(0, 0), (1, 0), (-1, 0), (1/2, 0), (-1/2, 0), (2, 0), (-2, 0), (0, 1), (1, 1), (-1, 1), (1/2, 1), (-1/2, 1), (2, 1), (-2, 1), (0, -1), (1, -1), (-1, -1), (1/2, -1), (-1/2, -1), (2, -1), (-2, -1), (0, 1/2), (1, 1/2), (-1, 1/2), (1/2, 1/2), (-1/2, 1/2), (2, 1/2), (-2, 1/2), (0, -1/2), (1, -1/2), (-1, -1/2), (1/2, -1/2), (-1/2, -1/2), (2, -1/2), (-2, -1/2), (0, 2), (1, 2), (-1, 2), (1/2, 2), (-1/2, 2), (2, 2), (-2, 2), (0, -2), (1, -2), (-1, -2), (1/2, -2), (-1/2, -2), (2, -2), (-2, -2)] :: sage: u = QQ['u'].0 sage: A.<x,y> = AffineSpace(NumberField(u^2 - 2, 'v'), 2) sage: len(list(A.points_of_bounded_height(6))) 121 """ if (is_RationalField(self.base_ring())): ftype = False # stores whether field is a number field or the rational field elif (self.base_ring() in NumberFields()): # true for rational field as well, so check is_RationalField first ftype = True else: raise NotImplementedError("self must be affine space over a number field.") bound = bound**(1/self.base_ring().absolute_degree()) # convert to relative height n = self.dimension_relative() R = self.base_ring() zero = R(0) P = [ zero for _ in range(n) ] yield self(P) if not ftype: iters = [ R.range_by_height(bound) for _ in range(n) ] else: iters = [ R.elements_of_bounded_height(bound) for _ in range(n) ] for x in iters: next(x) # put at zero i = 0 while i < n: try: P[i] = next(iters[i]) yield self(P) i = 0 except StopIteration: if not ftype: iters[i] = R.range_by_height(bound) # reset else: iters[i] = R.elements_of_bounded_height(bound) next(iters[i]) # put at zero P[i] = zero i += 1
def HyperellipticCurve(f, h=None, names=None, PP=None, check_squarefree=True): r""" Returns the hyperelliptic curve `y^2 + h y = f`, for univariate polynomials `h` and `f`. If `h` is not given, then it defaults to 0. INPUT: - ``f`` - univariate polynomial - ``h`` - optional univariate polynomial - ``names`` (default: ``["x","y"]``) - names for the coordinate functions - ``check_squarefree`` (default: ``True``) - test if the input defines a hyperelliptic curve when f is homogenized to degree `2g+2` and h to degree `g+1` for some g. .. WARNING:: When setting ``check_squarefree=False`` or using a base ring that is not a field, the output curves are not to be trusted. For example, the output of ``is_singular`` is always ``False``, without this being properly tested in that case. .. NOTE:: The words "hyperelliptic curve" are normally only used for curves of genus at least two, but this class allows more general smooth double covers of the projective line (conics and elliptic curves), even though the class is not meant for those and some outputs may be incorrect. EXAMPLES: Basic examples:: sage: R.<x> = QQ[] sage: HyperellipticCurve(x^5 + x + 1) Hyperelliptic Curve over Rational Field defined by y^2 = x^5 + x + 1 sage: HyperellipticCurve(x^19 + x + 1, x-2) Hyperelliptic Curve over Rational Field defined by y^2 + (x - 2)*y = x^19 + x + 1 sage: k.<a> = GF(9); R.<x> = k[] sage: HyperellipticCurve(x^3 + x - 1, x+a) Hyperelliptic Curve over Finite Field in a of size 3^2 defined by y^2 + (x + a)*y = x^3 + x + 2 Characteristic two:: sage: P.<x> = GF(8,'a')[] sage: HyperellipticCurve(x^7+1, x) Hyperelliptic Curve over Finite Field in a of size 2^3 defined by y^2 + x*y = x^7 + 1 sage: HyperellipticCurve(x^8+x^7+1, x^4+1) Hyperelliptic Curve over Finite Field in a of size 2^3 defined by y^2 + (x^4 + 1)*y = x^8 + x^7 + 1 sage: HyperellipticCurve(x^8+1, x) Traceback (most recent call last): ... ValueError: Not a hyperelliptic curve: highly singular at infinity. sage: HyperellipticCurve(x^8+x^7+1, x^4) Traceback (most recent call last): ... ValueError: Not a hyperelliptic curve: singularity in the provided affine patch. sage: F.<t> = PowerSeriesRing(FiniteField(2)) sage: P.<x> = PolynomialRing(FractionField(F)) sage: HyperellipticCurve(x^5+t, x) Hyperelliptic Curve over Laurent Series Ring in t over Finite Field of size 2 defined by y^2 + x*y = x^5 + t We can change the names of the variables in the output:: sage: k.<a> = GF(9); R.<x> = k[] sage: HyperellipticCurve(x^3 + x - 1, x+a, names=['X','Y']) Hyperelliptic Curve over Finite Field in a of size 3^2 defined by Y^2 + (X + a)*Y = X^3 + X + 2 This class also allows curves of genus zero or one, which are strictly speaking not hyperelliptic:: sage: P.<x> = QQ[] sage: HyperellipticCurve(x^2+1) Hyperelliptic Curve over Rational Field defined by y^2 = x^2 + 1 sage: HyperellipticCurve(x^4-1) Hyperelliptic Curve over Rational Field defined by y^2 = x^4 - 1 sage: HyperellipticCurve(x^3+2*x+2) Hyperelliptic Curve over Rational Field defined by y^2 = x^3 + 2*x + 2 Double roots:: sage: P.<x> = GF(7)[] sage: HyperellipticCurve((x^3-x+2)^2*(x^6-1)) Traceback (most recent call last): ... ValueError: Not a hyperelliptic curve: singularity in the provided affine patch. sage: HyperellipticCurve((x^3-x+2)^2*(x^6-1), check_squarefree=False) Hyperelliptic Curve over Finite Field of size 7 defined by y^2 = x^12 + 5*x^10 + 4*x^9 + x^8 + 3*x^7 + 3*x^6 + 2*x^4 + 3*x^3 + 6*x^2 + 4*x + 3 The input for a (smooth) hyperelliptic curve of genus `g` should not contain polynomials of degree greater than `2g+2`. In the following example, the hyperelliptic curve has genus 2 and there exists a model `y^2 = F` of degree 6, so the model `y^2 + yh = f` of degree 200 is not allowed.:: sage: P.<x> = QQ[] sage: h = x^100 sage: F = x^6+1 sage: f = F-h^2/4 sage: HyperellipticCurve(f, h) Traceback (most recent call last): ... ValueError: Not a hyperelliptic curve: highly singular at infinity. sage: HyperellipticCurve(F) Hyperelliptic Curve over Rational Field defined by y^2 = x^6 + 1 An example with a singularity over an inseparable extension of the base field:: sage: F.<t> = GF(5)[] sage: P.<x> = F[] sage: HyperellipticCurve(x^5+t) Traceback (most recent call last): ... ValueError: Not a hyperelliptic curve: singularity in the provided affine patch. Input with integer coefficients creates objects with the integers as base ring, but only checks smoothness over `\QQ`, not over Spec(`\ZZ`). In other words, it is checked that the discriminant is non-zero, but it is not checked whether the discriminant is a unit in `\ZZ^*`.:: sage: P.<x> = ZZ[] sage: HyperellipticCurve(3*x^7+6*x+6) Hyperelliptic Curve over Integer Ring defined by y^2 = 3*x^7 + 6*x + 6 """ if (not is_Polynomial(f)) or f == 0: raise TypeError, "Arguments f (=%s) and h (= %s) must be polynomials " \ "and f must be non-zero" % (f,h) P = f.parent() if h is None: h = P(0) try: h = P(h) except TypeError: raise TypeError, \ "Arguments f (=%s) and h (= %s) must be polynomials in the same ring"%(f,h) df = f.degree() dh_2 = 2*h.degree() if dh_2 < df: g = (df-1)//2 else: g = (dh_2-1)//2 if check_squarefree: # Assuming we are working over a field, this checks that after # resolving the singularity at infinity, we get a smooth double cover # of P^1. if P(2) == 0: # characteristic 2 if h == 0: raise ValueError, \ "In characteristic 2, argument h (= %s) must be non-zero."%h if h[g+1] == 0 and f[2*g+1]**2 == f[2*g+2]*h[g]**2: raise ValueError, "Not a hyperelliptic curve: " \ "highly singular at infinity." should_be_coprime = [h, f*h.derivative()**2+f.derivative()**2] else: # characteristic not 2 F = f + h**2/4 if not F.degree() in [2*g+1, 2*g+2]: raise ValueError, "Not a hyperelliptic curve: " \ "highly singular at infinity." should_be_coprime = [F, F.derivative()] try: smooth = should_be_coprime[0].gcd(should_be_coprime[1]).degree()==0 except (AttributeError, NotImplementedError, TypeError): try: smooth = should_be_coprime[0].resultant(should_be_coprime[1])!=0 except (AttributeError, NotImplementedError, TypeError): raise NotImplementedError, "Cannot determine whether " \ "polynomials %s have a common root. Use " \ "check_squarefree=False to skip this check." % \ should_be_coprime if not smooth: raise ValueError, "Not a hyperelliptic curve: " \ "singularity in the provided affine patch." R = P.base_ring() PP = ProjectiveSpace(2, R) if names is None: names = ["x","y"] if is_FiniteField(R): if g == 2: return HyperellipticCurve_g2_finite_field(PP, f, h, names=names, genus=g) else: return HyperellipticCurve_finite_field(PP, f, h, names=names, genus=g) elif is_RationalField(R): if g == 2: return HyperellipticCurve_g2_rational_field(PP, f, h, names=names, genus=g) else: return HyperellipticCurve_rational_field(PP, f, h, names=names, genus=g) elif is_pAdicField(R): if g == 2: return HyperellipticCurve_g2_padic_field(PP, f, h, names=names, genus=g) else: return HyperellipticCurve_padic_field(PP, f, h, names=names, genus=g) else: if g == 2: return HyperellipticCurve_g2_generic(PP, f, h, names=names, genus=g) else: return HyperellipticCurve_generic(PP, f, h, names=names, genus=g)
def points(self, B=0, tolerance=0.9): r""" Return some or all rational points of an affine scheme. INPUT: - ``B`` -- integer (optional, default: 0). The bound for the height of the coordinates. OUTPUT: - If the base ring is a finite field: all points of the scheme, given by coordinate tuples. - If the base ring is `\QQ` or `\ZZ`: the subset of points whose coordinates have height ``B`` or less. EXAMPLES: The bug reported at #11526 is fixed:: sage: A2 = AffineSpace(ZZ, 2) sage: F = GF(3) sage: A2(F).points() [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)] sage: R = ZZ sage: A.<x,y> = R[] sage: I = A.ideal(x^2-y^2-1) sage: V = AffineSpace(R, 2) sage: X = V.subscheme(I) sage: M = X(R) sage: M.points(1) [(-1, 0), (1, 0)] :: sage: u = QQ['u'].0 sage: K.<v> = NumberField(u^2 + 3) sage: A.<x,y> = AffineSpace(K, 2) sage: len(A(K).points(9)) 361 :: sage: A.<x,y> = AffineSpace(QQ, 2) sage: E = A.subscheme([x^2 + y^2 - 1, y^2 - x^3 + x^2 + x - 1]) sage: E(A.base_ring()).points() [(-1, 0), (0, -1), (0, 1), (1, 0)] :: sage: A.<x,y> = AffineSpace(CC, 2) sage: E = A.subscheme([y^3-x^3-x^2, x*y]) sage: E(A.base_ring()).points() [(-1.00000000000000, 0.000000000000000), (0.000000000000000, 0.000000000000000)] :: sage: A.<x1,x2> = AffineSpace(CDF, 2) sage: E = A.subscheme([x1^2+x2^2+x1*x2, x1+x2]) sage: E(A.base_ring()).points() [(0.0, 0.0)] """ from sage.schemes.affine.affine_space import is_AffineSpace from sage.rings.all import CC, CDF, RR X = self.codomain() tolerance_RR = RR(tolerance) if tolerance_RR.sign() != 1: raise ValueError("Tolerance must be positive") if not is_AffineSpace(X) and X.base_ring() in Fields(): if X.base_ring() == CC or X.base_ring() == CDF: complex = True else: complex = False # Then X must be a subscheme dim_ideal = X.defining_ideal().dimension() if dim_ideal < 0: # no points return [] if dim_ideal == 0: # if X zero-dimensional rat_points = [] PS = X.ambient_space() N = PS.dimension_relative() BR = X.base_ring() #need a lexicographic ordering for elimination R = PolynomialRing(BR, N, PS.gens(), order='lex') I = R.ideal(X.defining_polynomials()) I0 = R.ideal(0) #Determine the points through elimination #This is much faster than using the I.variety() function on each affine chart. G = I.groebner_basis() if G != [1]: P = {} points = [P] #work backwards from solving each equation for the possible #values of the next coordinate for i in range(len(G) - 1, -1, -1): new_points = [] good = 0 for P in points: #substitute in our dictionary entry that has the values #of coordinates known so far. This results in a single #variable polynomial (by elimination) L = G[i].substitute(P) if R(L).degree() > 0: if complex: for pol in L.univariate_polynomial().roots( multiplicities=False): r = L.variables()[0] varindex = R.gens().index(r) P.update({R.gen(varindex): pol}) new_points.append(copy(P)) good = 1 else: L = L.factor() #the linear factors give the possible rational values of #this coordinate for pol, pow in L: if pol.degree() == 1 and len( pol.variables()) == 1: good = 1 r = pol.variables()[0] varindex = R.gens().index(r) #add this coordinates information to #each dictionary entry P.update({ R.gen(varindex): -pol.constant_coefficient() / pol.monomial_coefficient(r) }) new_points.append(copy(P)) else: new_points.append(P) good = 1 if good: points = new_points #the dictionary entries now have values for all coordinates #they are the rational solutions to the equations #make them into projective points for i in range(len(points)): if complex: if len(points[i]) == N: S = X.ambient_space()( [points[i][R.gen(j)] for j in range(N)]) #S.normalize_coordinates() rat_points.append(S) else: if len(points[i]) == N and I.subs(points[i]) == I0: S = X([points[i][R.gen(j)] for j in range(N)]) #S.normalize_coordinates() rat_points.append(S) # remove duplicate element using tolerance if complex: tol = (X.base_ring().precision() * tolerance_RR).floor() dupl_points = list(rat_points) for i in range(len(dupl_points)): u = dupl_points[i] for j in range(i + 1, len(dupl_points)): v = dupl_points[j] if all([(u[k] - v[k]).abs() < 2**(-tol) for k in range(len(u))]): rat_points.remove(u) break rat_points = sorted(rat_points) return rat_points R = self.value_ring() if is_RationalField(R) or R == ZZ: if not B > 0: raise TypeError("a positive bound B (= %s) must be specified" % B) from sage.schemes.affine.affine_rational_point import enum_affine_rational_field return enum_affine_rational_field(self, B) if R in NumberFields(): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified" % B) from sage.schemes.affine.affine_rational_point import enum_affine_number_field return enum_affine_number_field(self, B) elif is_FiniteField(R): from sage.schemes.affine.affine_rational_point import enum_affine_finite_field return enum_affine_finite_field(self) else: raise TypeError("unable to enumerate points over %s" % R)
def points_of_bounded_height(self, **kwds): r""" Returns an iterator of the points in this affine space of absolute height of at most the given bound. Bound check is strict for the rational field. Requires this space to be affine space over a number field. Uses the Doyle-Krumm algorithm 4 (algorithm 5 for imaginary quadratic) for computing algebraic numbers up to a given height [Doyle-Krumm]_. The algorithm requires floating point arithmetic, so the user is allowed to specify the precision for such calculations. Additionally, due to floating point issues, points slightly larger than the bound may be returned. This can be controlled by lowering the tolerance. INPUT: kwds: - ``bound`` - a real number - ``tolerance`` - a rational number in (0,1] used in doyle-krumm algorithm-4 - ``precision`` - the precision to use for computing the elements of bounded height of number fields OUTPUT: - an iterator of points in self EXAMPLES:: sage: A.<x,y> = AffineSpace(QQ, 2) sage: list(A.points_of_bounded_height(bound=3)) [(0, 0), (1, 0), (-1, 0), (1/2, 0), (-1/2, 0), (2, 0), (-2, 0), (0, 1), (1, 1), (-1, 1), (1/2, 1), (-1/2, 1), (2, 1), (-2, 1), (0, -1), (1, -1), (-1, -1), (1/2, -1), (-1/2, -1), (2, -1), (-2, -1), (0, 1/2), (1, 1/2), (-1, 1/2), (1/2, 1/2), (-1/2, 1/2), (2, 1/2), (-2, 1/2), (0, -1/2), (1, -1/2), (-1, -1/2), (1/2, -1/2), (-1/2, -1/2), (2, -1/2), (-2, -1/2), (0, 2), (1, 2), (-1, 2), (1/2, 2), (-1/2, 2), (2, 2), (-2, 2), (0, -2), (1, -2), (-1, -2), (1/2, -2), (-1/2, -2), (2, -2), (-2, -2)] :: sage: u = QQ['u'].0 sage: A.<x,y> = AffineSpace(NumberField(u^2 - 2, 'v'), 2) sage: len(list(A.points_of_bounded_height(bound=2, tolerance=0.1))) 529 """ if (is_RationalField(self.base_ring())): ftype = False # stores whether field is a number field or the rational field elif (self.base_ring() in NumberFields()): # true for rational field as well, so check is_RationalField first ftype = True else: raise NotImplementedError("self must be affine space over a number field.") bound = kwds.pop('bound') B = bound**self.base_ring().absolute_degree() # convert to relative height n = self.dimension_relative() R = self.base_ring() zero = R(0) P = [ zero for _ in range(n) ] yield self(P) if not ftype: iters = [ R.range_by_height(B) for _ in range(n) ] else: tol = kwds.pop('tolerance', 1e-2) prec = kwds.pop('precision', 53) iters = [ R.elements_of_bounded_height(bound=B, tolerance=tol, precision=prec) for _ in range(n) ] for x in iters: next(x) # put at zero i = 0 while i < n: try: P[i] = next(iters[i]) yield self(P) i = 0 except StopIteration: if not ftype: iters[i] = R.range_by_height(B) # reset else: iters[i] = R.elements_of_bounded_height(bound=B, tolerance=tol, precision=prec) next(iters[i]) # put at zero P[i] = zero i += 1
def dual(self): r""" Return the projective dual of the given subscheme of projective space. INPUT: - ``X`` -- A subscheme of projective space. At present, ``X`` is required to be an irreducible and reduced hypersurface defined over `\QQ` or a finite field. OUTPUT: - The dual of ``X`` as a subscheme of the dual projective space. EXAMPLES: The dual of a smooth conic in the plane is also a smooth conic:: sage: R.<x, y, z> = QQ[] sage: P.<x, y, z> = ProjectiveSpace(2, QQ) sage: I = R.ideal(x^2 + y^2 + z^2) sage: X = P.subscheme(I) sage: X.dual() Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: y0^2 + y1^2 + y2^2 The dual of the twisted cubic curve in projective 3-space is a singular quartic surface. In the following example, we compute the dual of this surface, which by double duality is equal to the twisted cubic itself. The output is the twisted cubic as an intersection of three quadrics:: sage: R.<x, y, z, w> = QQ[] sage: P.<x, y, z, w> = ProjectiveSpace(3, QQ) sage: I = R.ideal(y^2*z^2 - 4*x*z^3 - 4*y^3*w + 18*x*y*z*w - 27*x^2*w^2) sage: X = P.subscheme(I) sage: X.dual() Closed subscheme of Projective Space of dimension 3 over Rational Field defined by: y2^2 - y1*y3, y1*y2 - y0*y3, y1^2 - y0*y2 The singular locus of the quartic surface in the last example is itself supported on a twisted cubic:: sage: X.Jacobian().radical() Ideal (z^2 - 3*y*w, y*z - 9*x*w, y^2 - 3*x*z) of Multivariate Polynomial Ring in x, y, z, w over Rational Field An example over a finite field:: sage: R = PolynomialRing(GF(61), 'a,b,c') sage: P.<a, b, c> = ProjectiveSpace(2, R.base_ring()) sage: X = P.subscheme(R.ideal(a*a+2*b*b+3*c*c)) sage: X.dual() Closed subscheme of Projective Space of dimension 2 over Finite Field of size 61 defined by: y0^2 - 30*y1^2 - 20*y2^2 TESTS:: sage: R = PolynomialRing(Qp(3), 'a,b,c') sage: P.<a, b, c> = ProjectiveSpace(2, R.base_ring()) sage: X = P.subscheme(R.ideal(a*a+2*b*b+3*c*c)) sage: X.dual() Traceback (most recent call last): ... NotImplementedError: base ring must be QQ or a finite field """ from sage.libs.singular.function_factory import ff K = self.base_ring() if not (is_RationalField(K) or is_FiniteField(K)): raise NotImplementedError("base ring must be QQ or a finite field") I = self.defining_ideal() m = I.ngens() n = I.ring().ngens() - 1 if (m != 1 or (n < 1) or I.is_zero() or I.is_trivial() or not I.is_prime()): raise NotImplementedError("At the present, the method is only" " implemented for irreducible and" " reduced hypersurfaces and the given" " list of generators for the ideal must" " have exactly one element.") R = PolynomialRing(K, 'x', n + 1) from sage.schemes.projective.projective_space import ProjectiveSpace Pd = ProjectiveSpace(n, K, 'y') Rd = Pd.coordinate_ring() x = R.variable_names() y = Rd.variable_names() S = PolynomialRing(K, x + y + ('t', )) if S.has_coerce_map_from(I.ring()): T = PolynomialRing(K, 'w', n + 1) I_S = (I.change_ring(T)).change_ring(S) else: I_S = I.change_ring(S) f_S = I_S.gens()[0] z = S.gens() J = I_S for i in range(n + 1): J = J + S.ideal(z[-1] * f_S.derivative(z[i]) - z[i + n + 1]) sat = ff.elim__lib.sat max_ideal = S.ideal(z[n + 1:2 * n + 2]) J_sat_gens = sat(J, max_ideal)[0] J_sat = S.ideal(J_sat_gens) L = J_sat.elimination_ideal(z[0:n + 1] + (z[-1], )) return Pd.subscheme(L.change_ring(Rd))
def points(self, B=0): r""" Return some or all rational points of an affine scheme. INPUT: - ``B`` -- integer (optional, default: 0). The bound for the height of the coordinates. OUTPUT: - If the base ring is a finite field: all points of the scheme, given by coordinate tuples. - If the base ring is `\QQ` or `\ZZ`: the subset of points whose coordinates have height ``B`` or less. EXAMPLES: The bug reported at #11526 is fixed:: sage: A2 = AffineSpace(ZZ, 2) sage: F = GF(3) sage: A2(F).points() [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)] sage: R = ZZ sage: A.<x,y> = R[] sage: I = A.ideal(x^2-y^2-1) sage: V = AffineSpace(R, 2) sage: X = V.subscheme(I) sage: M = X(R) sage: M.points(1) [(-1, 0), (1, 0)] :: sage: u = QQ['u'].0 sage: K.<v> = NumberField(u^2 + 3) sage: A.<x,y> = AffineSpace(K, 2) sage: len(A(K).points(9)) 361 :: sage: A.<x,y> = AffineSpace(QQ, 2) sage: E = A.subscheme([x^2 + y^2 - 1, y^2 - x^3 + x^2 + x - 1]) sage: E(A.base_ring()).points() [(-1, 0), (0, -1), (0, 1), (1, 0)] """ X = self.codomain() from sage.schemes.affine.affine_space import is_AffineSpace if not is_AffineSpace(X) and X.base_ring() in Fields(): # Then X must be a subscheme dim_ideal = X.defining_ideal().dimension() if dim_ideal < 0: # no points return [] if dim_ideal == 0: # if X zero-dimensional N = len(X.ambient_space().gens()) S = X.defining_polynomials()[0].parent() R = PolynomialRing(S.base_ring(), 's', N, order='lex') phi = S.hom(R.gens(), R) J = R.ideal([phi(t) for t in X.defining_polynomials()]) D = J.variety() points = [] for d in D: P = [d[t] for t in R.gens()] points.append(X(P)) points.sort() return points R = self.value_ring() if is_RationalField(R) or R == ZZ: if not B > 0: raise TypeError("a positive bound B (= %s) must be specified" % B) from sage.schemes.affine.affine_rational_point import enum_affine_rational_field return enum_affine_rational_field(self, B) if R in NumberFields(): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified" % B) from sage.schemes.affine.affine_rational_point import enum_affine_number_field return enum_affine_number_field(self, B) elif is_FiniteField(R): from sage.schemes.affine.affine_rational_point import enum_affine_finite_field return enum_affine_finite_field(self) else: raise TypeError("unable to enumerate points over %s" % R)