diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index e6a0e5c2000..118f8422a04 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -3679,6 +3679,10 @@ REFERENCES: and its use for certified homotopy continuation of systems of plane algebraic curves, :arxiv:`1505.03432` +.. [Krumm2016] Daid Krumm, *Computing Points of Bounded Height in Projective Space over a Number Field*, + MATHEMATICS OF COMPUTATION, Volume 85, Number 297, January 2016, Pages 423–447. + http://dx.doi.org/10.1090/mcom/2984 + .. [KR2001] \J. Kahane and A. Ryba. *The hexad game*, Electronic Journal of Combinatorics, **8** (2001). http://www.combinatorics.org/Volume_8/Abstracts/v8i2r11.html diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index f47d5d36249..65d936337bc 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -3369,13 +3369,18 @@ def affine_preperiodic_model(self, m, n, return_conjugation=False): sage: g = f.affine_preperiodic_model(0, 1); g Dynamical System of Projective Space of dimension 2 over Rational Field Defn: Defined on coordinates by sending (x : y : z) to - (-x^2 : 2*x^2 + 2*x*y + y^2 : 2*x^2 + 2*x*y + 2*y^2 - 2*y*z + z^2) + (-x^2 : -2*x^2 + 2*x*y - y^2 : 2*x^2 - 2*x*y + 2*y^2 + 2*y*z + z^2) We can check that ``g`` has affine fixed points:: sage: g.periodic_points(1) - [(-1 : 1 : 1), (-1/2 : 1/2 : 1), (-1/2 : 1 : 1), (-1/3 : 2/3 : 1), (0 : 0 : 1), - (0 : 1/2 : 1), (0 : 1 : 1)] + [(-1 : -1 : 1), + (-1/2 : -1 : 1), + (-1/2 : -1/2 : 1), + (-1/3 : -2/3 : 1), + (0 : -1 : 1), + (0 : -1/2 : 1), + (0 : 0 : 1)] :: @@ -3384,8 +3389,8 @@ def affine_preperiodic_model(self, m, n, return_conjugation=False): sage: f.affine_preperiodic_model(0, 1) Dynamical System of Projective Space of dimension 2 over Finite Field in z2 of size 3^2 Defn: Defined on coordinates by sending (x : y : z) to - ((z2 + 1)*x^2 : (z2 + 1)*x^2 + (z2 + 1)*x*y + (-z2 - 1)*y^2 : - (z2 - 1)*x^2 + (z2 - 1)*x*y - y^2 + (-z2)*y*z + z^2) + ((-z2)*x^2 : z2*x^2 + (-z2)*x*y + (-z2)*y^2 : + (-z2)*x^2 + z2*x*y + (z2 + 1)*y^2 - y*z + z^2) :: @@ -3396,9 +3401,8 @@ def affine_preperiodic_model(self, m, n, return_conjugation=False): Dynamical System of Projective Space of dimension 2 over Univariate Polynomial Ring in c over Finite Field of size 3 Defn: Defined on coordinates by sending (x : y : z) to - ((2*c^4 + c^3)*x^2 : (2*c^4 + c^3)*x^2 + (2*c^4 + c^3)*x*y + (c^4 + 2*c^3)*y^2 : - c^3*x^2 + c^3*x*y + (2*c^3 + 2*c^2)*y^2 + (c^3 + 2*c^2)*y*z + (2*c^4 + 2*c^3 + - 2*c^2)*z^2) + (2*c^3*x^2 : c^3*x^2 + 2*c^3*x*y + 2*c^3*y^2 : + 2*c^3*x^2 + c^3*x*y + (c^3 + c^2)*y^2 + 2*c^2*y*z + c^2*z^2) :: @@ -3409,8 +3413,7 @@ def affine_preperiodic_model(self, m, n, return_conjugation=False): Dynamical System of Projective Space of dimension 2 over Cyclotomic Field of order 3 and degree 2 Defn: Defined on coordinates by sending (x : y : z) to - (x^2 + y^2 + (-k + 2)*x*z - 2*y*z + (-k + 3)*z^2 : - -2*x^2 + (k - 4)*x*z + (k - 3)*z^2 : -x^2 + (k - 2)*x*z + (k - 2)*z^2) + (-y^2 : x^2 : x^2 + (-k)*x*z + z^2) :: @@ -3429,8 +3432,7 @@ def affine_preperiodic_model(self, m, n, return_conjugation=False): Dynamical System of Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: 2*y - z Defn: Defined on coordinates by sending (x : y : z) to - (2*x^2 + y^2 + 4*x*z - 2*y*z + 4*z^2 : -x^2 - y^2 - 2*x*z + 2*y*z - 3*z^2 : - -x^2 - 2*x*z - 2*z^2) + (-x^2 - y^2 : y^2 : x^2 + z^2) TESTS:: diff --git a/src/sage/schemes/projective/proj_bdd_height.py b/src/sage/schemes/projective/proj_bdd_height.py new file mode 100644 index 00000000000..33d5ec1d3bc --- /dev/null +++ b/src/sage/schemes/projective/proj_bdd_height.py @@ -0,0 +1,349 @@ +r""" +Points of bounded height in projective spaces + +This module defines functions to compute points of bounded height of a given +number field with height less than a specified bound in projective spaces. + +Sage functions to list all elements of a given number field with height less +than a specified bound. + +AUTHORS: + +- Jing Guo (2022): initial version based on David Krumm's code + +REFERENCES: + +- [Krumm2016] + +""" + +import itertools + +from math import floor + +from sage.schemes.projective.projective_space import ProjectiveSpace +from sage.rings.rational_field import QQ +from sage.rings.all import RealField +from sage.rings.number_field.unit_group import UnitGroup +from sage.arith.all import gcd +from sage.matrix.constructor import matrix, column_matrix +from sage.libs.pari.all import pari +from sage.modules.free_module_element import vector +from sage.rings.integer import Integer +from sage.geometry.polyhedron.constructor import Polyhedron + + +def QQ_points_of_bounded_height(dim, bound): + r""" + Return an iterator of the points in ``self`` of absolute multiplicative + height of at most ``bound`` in the rational field. + + INPUT: + + - ``dim`` -- a positive integer + + - ``bound`` -- a real number + + OUTPUT: + + - an iterator of points of bounded height + + EXAMPLES: + + sage: from sage.schemes.projective.proj_bdd_height import QQ_points_of_bounded_height + sage: sorted(list(QQ_points_of_bounded_height(1, 1))) + [(-1 : 1), (0 : 1), (1 : 0), (1 : 1)] + sage: len(list(QQ_points_of_bounded_height(1, 5))) + 40 + + There are no points of negative height:: + + sage: from sage.schemes.projective.proj_bdd_height import QQ_points_of_bounded_height + sage: list(QQ_points_of_bounded_height(1, -3)) + [] + """ + if bound < 1: + return iter(set([])) + + PN = ProjectiveSpace(QQ, dim) + unit_tuples = list(itertools.product([-1, 1], repeat=dim)) + points_of_bounded_height = set([]) + increasing_tuples = itertools.combinations_with_replacement(range(floor(bound + 1)), dim + 1) + for t in increasing_tuples: + if gcd(t) == 1: + for p in itertools.permutations(t): + for u in unit_tuples: + point = PN([a*b for a, b in zip(u, p)] + [p[dim]]) + if point not in points_of_bounded_height: + points_of_bounded_height.add(point) + yield point + + +def IQ_points_of_bounded_height(PN, K, dim, bound): + r""" + Return an iterator of the points in ``self`` of absolute multiplicative + height of at most ``bound`` in the imaginary quadratic field ``K``. + + INPUT: + + - ``PN`` -- a projective space + + - ``K`` -- a number field + + - ``dim`` -- a positive interger + + - ``bound`` -- a real number + + OUTPUT: + + - an iterator of points of bounded height + + EXAMPLES: + + sage: from sage.schemes.projective.proj_bdd_height import IQ_points_of_bounded_height + sage: CF. = CyclotomicField(3) + sage: P. = ProjectiveSpace(CF, 2) + sage: len(list(IQ_points_of_bounded_height(P, CF, 2, -1))) + 0 + sage: len(list(IQ_points_of_bounded_height(P, CF, 2, 1))) + 57 + """ + if bound < 1: + return iter([]) + + unit_tuples = list(itertools.product(K.roots_of_unity(), repeat=dim)) + + class_group_ideals = [c.ideal() for c in K.class_group()] + class_group_ideal_norms = [i.norm() for i in class_group_ideals] + class_number = len(class_group_ideals) + + possible_norm_set = set([]) + for i in range(class_number): + for k in range(1, floor(bound + 1)): + possible_norm_set.add(k*class_group_ideal_norms[i]) + + coordinate_space = dict() + coordinate_space[0] = [K(0)] + for m in possible_norm_set: + coordinate_space[m] = K.elements_of_norm(m) + + for i in range(class_number): + a = class_group_ideals[i] + a_norm = class_group_ideal_norms[i] + a_norm_bound = bound * a_norm + a_coordinates = [] + + for m in coordinate_space: + if m <= a_norm_bound: + for x in coordinate_space[m]: + if x in a: + a_coordinates.append(x) + + points_in_class_a = set([]) + t = len(a_coordinates) - 1 + increasing_tuples = itertools.combinations_with_replacement(range(t + 1), dim + 1) + for index_tuple in increasing_tuples: + point_coordinates = [a_coordinates[i] for i in index_tuple] + if a == K.ideal(point_coordinates): + for p in itertools.permutations(point_coordinates): + for u in unit_tuples: + point = PN([i*j for i, j in zip(u, p)] + [p[dim]]) + if point not in points_in_class_a: + points_in_class_a.add(point) + yield point + + +def points_of_bounded_height(PN, K, dim, bound, prec=53): + r""" + Return an iterator of the points in ``K`` with dimension ``dim`` of + absolute multiplicative height of at most ``bound``. + + ALGORITHM: + + This is an implementation of Algorithm 6 in [Krumm2016]_. + + INPUT: + + - ``PN`` -- a projective space + + - ``K`` -- a number field + + - ``dim`` -- a positive interger + + - ``bound`` -- a real number + + - ``prec`` -- (default: 53) a positive integer + + OUTPUT: + + - an iterator of points of bounded height + + EXAMPLES: + + sage: from sage.schemes.projective.proj_bdd_height import points_of_bounded_height + sage: K. = NumberField(x^3 - 7) + sage: P. = ProjectiveSpace(K, 2) + sage: len(list(points_of_bounded_height(P, K, 2, 1))) + 13 + """ + if bound < 1: + return iter([]) + + r1, r2 = K.signature() + r = r1 + r2 - 1 + + if K.is_relative(): + K_degree = K.relative_degree() + else: + K_degree = K.degree() + + roots_of_unity = K.roots_of_unity() + unit_tuples = list(itertools.product(roots_of_unity, repeat=dim)) + + log_embed = K.logarithmic_embedding() + + Reals = RealField(prec) + logB = Reals(bound).log() + + class_group_ideals = [c.ideal() for c in K.class_group()] + class_number = len(class_group_ideals) + + if K.is_relative(): + class_group_ideal_norms = [i.absolute_norm() for i in class_group_ideals] + else: + class_group_ideal_norms = [i.norm() for i in class_group_ideals] + + norm_bound = bound * max(class_group_ideal_norms) + fundamental_units = UnitGroup(K).fundamental_units() + fund_unit_logs = list(map(log_embed, fundamental_units)) + mat = column_matrix(fund_unit_logs) + + test_matrix = mat + try: + test_matrix.change_ring(QQ) + except ValueError: + raise ValueError('prec too low.') + + cut_fund_unit_logs = mat.delete_rows([r]) + lll_fund_units = [] + for c in pari(cut_fund_unit_logs).qflll().python(): + new_unit = 1 + for i in range(r): + new_unit *= fundamental_units[i]**c[i] + lll_fund_units.append(new_unit) + fundamental_units = lll_fund_units + fund_unit_logs = list(map(log_embed, fundamental_units)) + + possible_norm_set = set([]) + for i in range(class_number): + for k in range(1, floor(bound + 1)): + possible_norm_set.add(k*class_group_ideal_norms[i]) + + principal_ideal_gens = dict() + negative_norm_units = K.elements_of_norm(-1) + if len(negative_norm_units) == 0: + for m in possible_norm_set: + principal_ideal_gens[m] = K.elements_of_norm(m) + K.elements_of_norm(-m) + else: + for m in possible_norm_set: + principal_ideal_gens[m] = K.elements_of_norm(m) + + pr_ideal_gen_logs = dict() + for key in principal_ideal_gens: + for y in principal_ideal_gens[key]: + pr_ideal_gen_logs[y] = log_embed(y) + + fund_parallelotope_vertices = [] + for coefficient_tuple in itertools.product([-1/2, 1/2], repeat=r): + vertex = sum([coefficient_tuple[i]*fund_unit_logs[i] for i in range(r)]) + fund_parallelotope_vertices.append(vertex) + + D_numbers = [] + for v in range(r + 1): + D_numbers.append(max([vertex[v] for vertex in fund_parallelotope_vertices])) + + A_numbers = [] + for v in range(r + 1): + A_numbers.append(min([pr_ideal_gen_logs[y][v] for y in pr_ideal_gen_logs])) + + aux_constant = (1/K_degree) * Reals(norm_bound).log() + + L_numbers = [] + for v in range(r1): + L_numbers.append(aux_constant + D_numbers[v] - A_numbers[v]) + for v in range(r1, r + 1): + L_numbers.append(2*aux_constant + D_numbers[v] - A_numbers[v]) + L_numbers = vector(L_numbers).change_ring(QQ) + + T = column_matrix(fund_unit_logs).delete_rows([r]).change_ring(QQ) + + # insert_row only takes integers, see https://trac.sagemath.org/ticket/11328 + M = ((-1)*matrix.identity(r)).insert_row(r, [Integer(1) for i in range(r)]) + M = M.transpose().insert_row(0, [Integer(0) for i in range(r + 1)]).transpose() + M = M.change_ring(QQ) + M.set_column(0, L_numbers) + vertices = map(vector, Polyhedron(ieqs=list(M)).vertices()) + + T_it = T.inverse().transpose() + unit_polytope = Polyhedron([v*T_it for v in vertices]) + + coordinate_space = dict() + coordinate_space[0] = [[K(0), log_embed(0)]] + int_points = unit_polytope.integral_points() + + units_with_logs = dict() + for n in int_points: + new_unit = 1 + for j in range(r): + new_unit *= fundamental_units[j]**n[j] + new_unit_log = sum([n[j]*fund_unit_logs[j] for j in range(r)]) + units_with_logs[n] = [new_unit, new_unit_log] + + for norm in principal_ideal_gens: + coordinate_list = [] + for y in principal_ideal_gens[norm]: + for n in int_points: + unit, unit_log = units_with_logs[n] + y_log = pr_ideal_gen_logs[y] + g_log = unit_log + y_log + bool1 = all(g_log[i] <= aux_constant + D_numbers[i] for i in range(r1)) + bool2 = all(g_log[j] <= 2 * aux_constant + D_numbers[j] for j in range(r1, r + 1)) + if bool1 and bool2: + g = unit * y + coordinate_list.append([g, g_log]) + if len(coordinate_list) > 0: + coordinate_space[norm] = coordinate_list + + for m in range(class_number): + a = class_group_ideals[m] + a_norm = class_group_ideal_norms[m] + log_a_norm = Reals(a_norm).log() + a_const = (logB + log_a_norm)/K_degree + a_coordinates = [] + + for k in range(floor(bound + 1)): + norm = k * a_norm + if norm in coordinate_space: + for pair in coordinate_space[norm]: + g, g_log = pair + if g in a: + bool1 = all(g_log[i] <= a_const + D_numbers[i] for i in range(r1)) + bool2 = all(g_log[j] <= 2 * a_const + D_numbers[j] for j in range(r1, r + 1)) + if bool1 and bool2: + a_coordinates.append(pair) + + t = len(a_coordinates) - 1 + points_in_class_a = set([]) + increasing_tuples = itertools.combinations_with_replacement(range(t + 1), dim + 1) + log_arch_height_bound = logB + log_a_norm + for index_tuple in increasing_tuples: + point_coordinates = [a_coordinates[i][0] for i in index_tuple] + point_coordinate_logs = [a_coordinates[i][1] for i in index_tuple] + log_arch_height = sum([max([x[i] for x in point_coordinate_logs]) for i in range(r + 1)]) + if log_arch_height <= log_arch_height_bound and a == K.ideal(point_coordinates): + for p in itertools.permutations(point_coordinates): + for u in unit_tuples: + point = PN([i*j for i, j in zip(u, p)] + [p[dim]]) + if point not in points_in_class_a: + points_in_class_a.add(point) + yield point diff --git a/src/sage/schemes/projective/projective_homset.py b/src/sage/schemes/projective/projective_homset.py index 0f7b6abce52..f9a8e69bd4b 100644 --- a/src/sage/schemes/projective/projective_homset.py +++ b/src/sage/schemes/projective/projective_homset.py @@ -124,7 +124,7 @@ def points(self, **kwds): sage: K. = NumberField(u^2 + 3) sage: P. = ProjectiveSpace(K,2) sage: len(P(K).points(bound=1.8)) - 381 + 309 :: diff --git a/src/sage/schemes/projective/projective_rational_point.py b/src/sage/schemes/projective/projective_rational_point.py index 93789be8d65..cb12e30c873 100644 --- a/src/sage/schemes/projective/projective_rational_point.py +++ b/src/sage/schemes/projective/projective_rational_point.py @@ -191,8 +191,7 @@ def enum_projective_number_field(X, **kwds): sage: P. = ProjectiveSpace(K, 2) sage: X = P.subscheme([x - y]) sage: enum_projective_number_field(X(K), bound=RR(5^(1/3)), prec=2^10) - [(0 : 0 : 1), (-1 : -1 : 1), (1 : 1 : 1), (-1/5*v^2 : -1/5*v^2 : 1), (-v : -v : 1), - (1/5*v^2 : 1/5*v^2 : 1), (v : v : 1), (1 : 1 : 0)] + [(0 : 0 : 1), (1 : 1 : 0), (-1 : -1 : 1), (1 : 1 : 1)] :: diff --git a/src/sage/schemes/projective/projective_space.py b/src/sage/schemes/projective/projective_space.py index ed44ebcd6aa..e4d100caa1e 100644 --- a/src/sage/schemes/projective/projective_space.py +++ b/src/sage/schemes/projective/projective_space.py @@ -80,7 +80,6 @@ # **************************************************************************** from sage.arith.misc import gcd, binomial -from sage.arith.srange import srange from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.rings.integer import Integer @@ -1547,8 +1546,8 @@ def hyperplane_transformation_matrix(self, plane_1, plane_2): sage: plane1 = P.subscheme(x) sage: plane2 = P.subscheme(y) sage: m = P.hyperplane_transformation_matrix(plane1, plane2); m - [-1 -1] - [ 1 0] + [0 1] + [1 0] sage: plane2(m*P((0,1))) (1 : 0) @@ -1558,10 +1557,10 @@ def hyperplane_transformation_matrix(self, plane_1, plane_2): sage: plane1 = P.subscheme(x + 2*y + z) sage: plane2 = P.subscheme(2*x + y + z) sage: P.hyperplane_transformation_matrix(plane1, plane2) - [ -3 0 0 0] - [ 9 6 0 0] - [-3/2 -3 3/2 0] - [-1/2 -1 -1/2 1] + [1 0 0 0] + [0 4 0 0] + [0 0 2 0] + [0 0 0 1] :: @@ -1569,8 +1568,8 @@ def hyperplane_transformation_matrix(self, plane_1, plane_2): sage: plane1 = P.subscheme(x + y) sage: plane2 = P.subscheme(y) sage: P.hyperplane_transformation_matrix(plane1, plane2) - [ 1 0] - [-1 -1] + [-1 0] + [ 1 1] :: @@ -1580,9 +1579,9 @@ def hyperplane_transformation_matrix(self, plane_1, plane_2): sage: plane2 = P.subscheme(x + v*y + v*z) sage: m = P.hyperplane_transformation_matrix(plane1, plane2) sage: m - [ -6/7*v - 2/7 0 0] - [ 2/7*v + 10/7 -4/7*v + 8/7 0] - [ -4/7*v + 1/7 -10/7*v - 8/7 1] + [ v 0 0] + [ 0 -2*v 0] + [ 0 0 1] :: @@ -1592,10 +1591,10 @@ def hyperplane_transformation_matrix(self, plane_1, plane_2): sage: plane1 = P.subscheme(k*x + 2*k*y + z) sage: plane2 = P.subscheme(7*k*x + y + 9*z) sage: m = P.hyperplane_transformation_matrix(plane1, plane2); m - [ 297/410*k + 279/410 0 0 0] - [-3609/410*k + 4437/410 -1656/205*k + 2358/205 0 0] - [ 511/410*k - 24/205 511/205*k - 48/205 -107/205*k + 327/410 0] - [ 83/410*k - 107/205 83/205*k - 214/205 107/205*k + 83/410 1] + [ 1 0 0 0] + [ 0 14*k 0 0] + [ 0 0 7/9 0] + [ 0 0 0 1] :: @@ -1627,9 +1626,9 @@ def hyperplane_transformation_matrix(self, plane_1, plane_2): sage: plane1 = P.subscheme(x + 9*t*y + z) sage: plane2 = P.subscheme(x + z) sage: P.hyperplane_transformation_matrix(plane1, plane2) - [ -1/9*t -t^2 0] - [ -t^2 + 1/9*t 0 0] - [ 1/81 1/9*t -1/9*t + 1/81] + [ 1 9*t 0] + [ 1 0 0] + [ 0 0 1] TESTS:: @@ -1698,7 +1697,7 @@ def hyperplane_transformation_matrix(self, plane_1, plane_2): source_points.append(self(point)) base_list = [list(s) for s in source_points] elif len(source_points) == N + 1: - Ms = matrix(base_list + [point]) + Ms = matrix(base_list + [point.change_ring(self.base_ring())]) if not any([m == 0 for m in Ms.minors(N + 1)]): source_points.append(self(point)) break @@ -1852,18 +1851,12 @@ def _morphism(self, *args, **kwds): def points_of_bounded_height(self, **kwds): r""" - Returns an iterator of the points in self of absolute height of at most the given bound. + Return an iterator of the points in ``self`` of absolute multiplicative + 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 4 (algorithm 5 for imaginary quadratic) for - computing algebraic numbers up to a given height [DK2013]_. + ALGORITHM: - 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. + This is an implementation of Algorithm 6 in [Krumm2016]_. INPUT: @@ -1871,74 +1864,113 @@ def points_of_bounded_height(self, **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. + - ``precision`` - (default: 53) a positive integer OUTPUT: - - an iterator of points in this space + - an iterator of points of bounded height EXAMPLES:: sage: P. = ProjectiveSpace(QQ, 1) - sage: sorted(list(P.points_of_bounded_height(bound=5))) - [(0 : 1), (1 : -5), (1 : -4), (1 : -3), (1 : -2), (1 : -1), (1 : 0), - (1 : 1), (1 : 2), (1 : 3), (1 : 4), (1 : 5), (2 : -5), (2 : -3), - (2 : -1), (2 : 1), (2 : 3), (2 : 5), (3 : -5), (3 : -4), (3 : -2), - (3 : -1), (3 : 1), (3 : 2), (3 : 4), (3 : 5), (4 : -5), (4 : -3), - (4 : -1), (4 : 1), (4 : 3), (4 : 5), (5 : -4), (5 : -3), (5 : -2), - (5 : -1), (5 : 1), (5 : 2), (5 : 3), (5 : 4)] + sage: sorted(list(P.points_of_bounded_height(bound=2))) + [(-2 : 1), (-1 : 1), (-1/2 : 1), (0 : 1), + (1/2 : 1), (1 : 0), (1 : 1), (2 : 1)] :: sage: u = QQ['u'].0 sage: P. = ProjectiveSpace(NumberField(u^2 - 2, 'v'), 2) - sage: len(list(P.points_of_bounded_height(bound=1.5, tolerance=0.1))) + sage: len(list(P.points_of_bounded_height(bound=2))) + 265 + + :: + + sage: CF. = CyclotomicField(3) + sage: R. = CF[] + sage: L. = CF.extension(x^3 + 2) + sage: Q. = ProjectiveSpace(L, 1) + sage: sorted(list(Q.points_of_bounded_height(bound=1))) + [(0 : 1), (1 : 0), (a + 1 : 1), (a : 1), + (-1 : 1), (-a - 1 : 1), (-a : 1), (1 : 1)] + + :: + + sage: R. = QQ[] + sage: F. = NumberField(x^4 - 8*x^2 + 3) + sage: P. = ProjectiveSpace(F, 2) + sage: all([exp(p.global_height()) <= 1 for p in P.points_of_bounded_height(bound=1)]) + True + + :: + + sage: K. = CyclotomicField(3) + sage: P. = ProjectiveSpace(K, 2) + sage: len(list(P.points_of_bounded_height(bound=1))) 57 + + :: + + sage: u = QQ['u'].0 + sage: K. = NumberField(u^2 - 2) + sage: P. = ProjectiveSpace(K, 1) + sage: len(list(P.points_of_bounded_height(bound=2))) + 24 + + :: + + sage: R. = QQ[] + sage: K. = NumberField(x^4 - 8*x^2 + 3) + sage: P. = ProjectiveSpace(K, 1) + sage: len(list(P.points_of_bounded_height(bound=2))) + 108 + + :: + + sage: R. = QQ[] + sage: K. = NumberField(x^5 + x^3 + 1) + sage: P. = ProjectiveSpace(K, 2) + sage: L = P.points_of_bounded_height(bound=1.2) + sage: len(list(L)) + 109 """ - 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 + from sage.schemes.projective.proj_bdd_height import QQ_points_of_bounded_height, IQ_points_of_bounded_height, points_of_bounded_height + + R = self.base_ring() + + # whether the field is a number field or the rational field + if is_RationalField(R): + field_type = False + elif R in NumberFields(): + # true for rational field as well, so check is_RationalField first + field_type = True else: raise NotImplementedError("self must be projective space over a number field") bound = kwds.pop('bound') - B = bound**(self.base_ring().absolute_degree()) # convert to relative height + prec = kwds.pop('precision', 53) - n = self.dimension_relative() - R = self.base_ring() - if ftype: - zero = R.zero() - i = n - while not i < 0: - P = [zero for _ in range(i)] + [R.one()] - P += [zero for _ in range(n - i)] - yield self(P) - 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(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: - iters[j] = R.elements_of_bounded_height(bound=B, tolerance=tol, precision=prec) # reset - next(iters[j]) # put at zero - P[j] = zero - j += 1 - i -= 1 - else: # base ring QQ - zero = (0,) * (n + 1) - for c in cartesian_product_iterator([srange(-B, B + 1) - for _ in range(n + 1)]): - if gcd(c) == 1 and c > zero: - yield self.point(c, check=False) + # Convert between absolute and relative height for calling Krumm's algorithm + bound = bound**R.absolute_degree() + + dim = self.dimension_relative() + + if field_type: + # for imaginary quadratic field + r1, r2 = R.signature() + r = r1 + r2 - 1 + + if R.is_relative(): + deg = R.relative_degree() + else: + deg = R.degree() + + if deg == 2 and r == 0: + return IQ_points_of_bounded_height(self, R, dim, bound) + + return points_of_bounded_height(self, R, dim, bound, prec) + else: + return QQ_points_of_bounded_height(dim, bound) def subscheme_from_Chow_form(self, Ch, dim): r"""