diff --git a/.gitignore b/.gitignore index bce06356df6..eb17641b0c4 100644 --- a/.gitignore +++ b/.gitignore @@ -243,6 +243,7 @@ src/sage/schemes/hyperelliptic_curves/__init__.py src/sage/schemes/berkovich/__init__.py src/sage/schemes/generic/__init__.py src/sage/schemes/projective/__init__.py +src/sage/schemes/weighted_projective/__init__.py src/sage/schemes/__init__.py src/sage/schemes/affine/__init__.py src/sage/modular/hecke/__init__.py diff --git a/src/doc/en/reference/curves/index.rst b/src/doc/en/reference/curves/index.rst index fbe7626b4e5..abc0e1985ee 100644 --- a/src/doc/en/reference/curves/index.rst +++ b/src/doc/en/reference/curves/index.rst @@ -15,6 +15,7 @@ Curves sage/schemes/curves/affine_curve sage/schemes/curves/plane_curve_arrangement sage/schemes/curves/projective_curve + sage/schemes/curves/weighted_projective_curve sage/schemes/curves/point sage/schemes/curves/closed_point sage/schemes/curves/zariski_vankampen diff --git a/src/doc/en/reference/schemes/index.rst b/src/doc/en/reference/schemes/index.rst index a0a9b9279d7..711558b7604 100644 --- a/src/doc/en/reference/schemes/index.rst +++ b/src/doc/en/reference/schemes/index.rst @@ -47,6 +47,16 @@ Projective Schemes sage/schemes/projective/projective_homset sage/schemes/projective/proj_bdd_height +Weighted Projective Schemes +--------------------------- + +.. toctree:: + :maxdepth: 1 + + sage/schemes/weighted_projective/weighted_projective_space + sage/schemes/weighted_projective/weighted_projective_point + sage/schemes/weighted_projective/weighted_projective_homset + Products of Projective Spaces ----------------------------- diff --git a/src/sage/schemes/all.py b/src/sage/schemes/all.py index f5126a0dee1..9c9fbc871dd 100644 --- a/src/sage/schemes/all.py +++ b/src/sage/schemes/all.py @@ -42,6 +42,8 @@ from sage.schemes.product_projective.all import * +from sage.schemes.weighted_projective.all import * + from sage.schemes.cyclic_covers.all import * from sage.schemes.berkovich.all import * diff --git a/src/sage/schemes/curves/constructor.py b/src/sage/schemes/curves/constructor.py index 2c407a4e7be..51abc8f5a13 100644 --- a/src/sage/schemes/curves/constructor.py +++ b/src/sage/schemes/curves/constructor.py @@ -18,6 +18,12 @@ Projective Plane Curve over Finite Field of size 5 defined by -x^9 + y^2*z^7 - x*z^8 +Here, we construct a hyperelliptic curve manually:: + + sage: WP. = WeightedProjectiveSpace([1, 3, 1], GF(103)) + sage: Curve(y^2 - (x^5*z + 17*x^2*z^4 + 92*z^6), WP) + Weighted Projective Curve over Finite Field of size 103 defined by y^2 - x^5*z - 17*x^2*z^4 + 11*z^6 + AUTHORS: - William Stein (2005-11-13) @@ -49,6 +55,7 @@ from sage.schemes.generic.algebraic_scheme import AlgebraicScheme from sage.schemes.affine.affine_space import AffineSpace, AffineSpace_generic from sage.schemes.projective.projective_space import ProjectiveSpace, ProjectiveSpace_ring +from sage.schemes.weighted_projective.weighted_projective_space import WeightedProjectiveSpace_ring from sage.schemes.plane_conics.constructor import Conic from .projective_curve import (ProjectiveCurve, @@ -71,6 +78,8 @@ IntegralAffinePlaneCurve, IntegralAffinePlaneCurve_finite_field) +from .weighted_projective_curve import WeightedProjectiveCurve + def _is_irreducible_and_reduced(F) -> bool: """ @@ -376,5 +385,12 @@ def Curve(F, A=None): return ProjectivePlaneCurve_field(A, F) return ProjectivePlaneCurve(A, F) + elif isinstance(A, WeightedProjectiveSpace_ring): + # currently, we only support curves in a weighted projective plane + if n != 2: + raise NotImplementedError("ambient space has to be a weighted projective plane") + # currently, we do not perform checks on weighted projective curves + return WeightedProjectiveCurve(A, F) + else: raise TypeError('ambient space neither affine nor projective') diff --git a/src/sage/schemes/curves/weighted_projective_curve.py b/src/sage/schemes/curves/weighted_projective_curve.py new file mode 100644 index 00000000000..78c75b69e10 --- /dev/null +++ b/src/sage/schemes/curves/weighted_projective_curve.py @@ -0,0 +1,105 @@ +# sage.doctest: needs sage.libs.singular +r""" +Weighted projective curves + +Weighted projective curves in Sage are curves in a weighted projective space or +a weighted projective plane. + +EXAMPLES: + +For now, only curves in weighted projective plane is supported:: + + sage: WP. = WeightedProjectiveSpace([1, 3, 1], QQ) + sage: C1 = WP.curve(y^2 - x^5 * z - 3 * x^2 * z^4 - 2 * z^6); C1 + Weighted Projective Curve over Rational Field defined by y^2 - x^5*z - 3*x^2*z^4 - 2*z^6 + sage: C2 = Curve(y^2 - x^5 * z - 3 * x^2 * z^4 - 2 * z^6, WP); C2 + Weighted Projective Curve over Rational Field defined by y^2 - x^5*z - 3*x^2*z^4 - 2*z^6 + sage: C1 == C2 + True + +AUTHORS: + +- Gareth Ma (2025) +""" + +# **************************************************************************** +# Copyright (C) 2005 William Stein +# Copyright (C) 2025 Gareth Ma +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.schemes.curves.curve import Curve_generic +from sage.schemes.weighted_projective.weighted_projective_space import WeightedProjectiveSpace_ring + + +class WeightedProjectiveCurve(Curve_generic): + """ + Curves in weighted projective spaces. + + EXAMPLES: + + We construct a hyperelliptic curve manually:: + + sage: WP. = WeightedProjectiveSpace([1, 3, 1], QQ) + sage: C = Curve(y^2 - x^5 * z - 3 * x^2 * z^4 - 2 * z^6, WP); C + Weighted Projective Curve over Rational Field defined by y^2 - x^5*z - 3*x^2*z^4 - 2*z^6 + """ + def __init__(self, A, X, *kwargs): + if not isinstance(A, WeightedProjectiveSpace_ring): + raise TypeError(f"A(={A}) is not a weighted projective space") + super().__init__(A, X, *kwargs) + + def _repr_type(self) -> str: + r""" + Return a string representation of the type of this curve. + + EXAMPLES:: + + sage: WP. = WeightedProjectiveSpace([1, 3, 1], QQ) + sage: C = Curve(y^2 - x^5 * z - 3 * x^2 * z^4 - 2 * z^6, WP); C + Weighted Projective Curve over Rational Field defined by y^2 - x^5*z - 3*x^2*z^4 - 2*z^6 + sage: C._repr_type() + 'Weighted Projective' + """ + return "Weighted Projective" + + def projective_curve(self): + r""" + Return this weighted projective curve as a projective curve. + + A weighted homogeneous polynomial `f(x_1, \ldots, x_n)`, where `x_i` has + weight `w_i`, can be viewed as an unweighted homogeneous polynomial + `f(y_1^{w_1}, \ldots, y_n^{w_n})`. This correspondence extends to + varieties. + + .. TODO: + + Implement homsets for weighted projective spaces and implement this + as a ``projective_embedding`` method instead. + + EXAMPLES:: + + sage: WP = WeightedProjectiveSpace([1, 3, 1], QQ, "x, y, z") + sage: x, y, z = WP.gens() + sage: C = WP.curve(y^2 - (x^5*z + 3*x^2*z^4 - 2*x*z^5 + 4*z^6)); C + Weighted Projective Curve over Rational Field defined by y^2 - x^5*z - 3*x^2*z^4 + 2*x*z^5 - 4*z^6 + sage: C.projective_curve() + Projective Plane Curve over Rational Field defined by y^6 - x^5*z - 3*x^2*z^4 + 2*x*z^5 - 4*z^6 + """ + from sage.schemes.projective.projective_space import ProjectiveSpace + + WP = self.ambient_space() + PP = ProjectiveSpace(WP.dimension_relative(), WP.base_ring(), WP.variable_names()) + PP_ring = PP.coordinate_ring() + subs_dict = {name: var**weight for (name, var), weight in + zip(WP.gens_dict().items(), WP.weights())} + + wp_polys = self.defining_polynomials() + pp_polys = [PP_ring(poly.subs(**subs_dict)) for poly in wp_polys] + + return PP.curve(pp_polys) diff --git a/src/sage/schemes/meson.build b/src/sage/schemes/meson.build index 4dac80900e9..63e470443b6 100644 --- a/src/sage/schemes/meson.build +++ b/src/sage/schemes/meson.build @@ -18,4 +18,5 @@ install_subdir('plane_quartics', install_dir: sage_install_dir / 'schemes') install_subdir('product_projective', install_dir: sage_install_dir / 'schemes') install_subdir('projective', install_dir: sage_install_dir / 'schemes') install_subdir('riemann_surfaces', install_dir: sage_install_dir / 'schemes') +install_subdir('weighted_projective', install_dir: sage_install_dir / 'schemes') subdir('toric') diff --git a/src/sage/schemes/weighted_projective/all.py b/src/sage/schemes/weighted_projective/all.py new file mode 100644 index 00000000000..e9d239e8251 --- /dev/null +++ b/src/sage/schemes/weighted_projective/all.py @@ -0,0 +1,16 @@ +"""nodoctest +all.py -- export of projective schemes to Sage +""" + +# **************************************************************************** +# Copyright (C) 2005 William Stein +# Copyright (C) 2025 Gareth Ma +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.schemes.weighted_projective.weighted_projective_space import WeightedProjectiveSpace diff --git a/src/sage/schemes/weighted_projective/weighted_projective_homset.py b/src/sage/schemes/weighted_projective/weighted_projective_homset.py new file mode 100644 index 00000000000..30445816837 --- /dev/null +++ b/src/sage/schemes/weighted_projective/weighted_projective_homset.py @@ -0,0 +1,38 @@ +""" +Hom-sets of weighted projective schemes + +AUTHORS: + +- Gareth Ma (2024): initial version, based on unweighted version. +""" + +# ***************************************************************************** +# Copyright (C) 2006 William Stein +# Copyright (C) 2011 Volker Braun +# Copyright (C) 2024 Gareth Ma +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +# ***************************************************************************** + +from sage.schemes.generic.homset import SchemeHomset_points + + +class SchemeHomset_points_weighted_projective_ring(SchemeHomset_points): + """ + Set of rational points of a weighted projective variety over a ring. + + INPUT: + + See :class:`SchemeHomset_points`. + + EXAMPLES:: + + sage: W = WeightedProjectiveSpace([3, 4, 5], QQ) + sage: W.point_homset() + Set of rational points of Weighted Projective Space of dimension 2 with weights (3, 4, 5) over Rational Field + sage: W.an_element().parent() is W.point_homset() + True + """ diff --git a/src/sage/schemes/weighted_projective/weighted_projective_point.py b/src/sage/schemes/weighted_projective/weighted_projective_point.py new file mode 100644 index 00000000000..59737afbe86 --- /dev/null +++ b/src/sage/schemes/weighted_projective/weighted_projective_point.py @@ -0,0 +1,304 @@ +r""" +Points on projective schemes + +This module implements scheme morphism for points on weighted projective schemes. + +AUTHORS: + +- Gareth Ma (2024): initial version, based on unweighted version. +""" + +# **************************************************************************** +# Copyright (C) 2006 David Kohel +# Copyright (C) 2006 William Stein +# Copyright (C) 2011 Volker Braun +# Copyright (C) 2024 Gareth Ma +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.categories.fields import Fields +from sage.rings.fraction_field import FractionField +from sage.misc.misc_c import prod + +from sage.schemes.generic.morphism import (SchemeMorphism, + SchemeMorphism_point) +from sage.structure.sequence import Sequence +from sage.structure.richcmp import richcmp, op_EQ, op_NE + + +# -------------------- +# Projective varieties +# -------------------- + +class SchemeMorphism_point_weighted_projective_ring(SchemeMorphism_point): + """ + A rational point of weighted projective space over a ring. + + INPUT: + + - ``X`` -- a homset of a subscheme of an ambient weighted projective space over a ring `R`. + + - ``v`` -- a list or tuple of coordinates in `R`. + + - ``check`` -- boolean (default: ``True``); Whether to check the input for consistency. + + EXAMPLES:: + + sage: WP = WeightedProjectiveSpace(2, ZZ) + sage: WP(2, 3, 4) + (2 : 3 : 4) + """ + + def __init__(self, X, v, check: bool = True): + """ + The Python constructor. + + EXAMPLES:: + + sage: WP = WeightedProjectiveSpace([3, 4, 5], QQ) + sage: P1 = WP(2, 3, 4); P1 + (2 : 3 : 4) + sage: P2 = WP(2000, 30000, 400000); P2 + (2000 : 30000 : 400000) + sage: P1 == P2 + True + + The point constructor normalises coordinates at the last position with + weight `1`:: + + sage: WP = WeightedProjectiveSpace([3, 1, 4, 1], QQ) + sage: P = WP(8, 3, 16, 2); P + (1 : 3/2 : 1 : 1) + sage: P == WP(1, 3 / 2, 1, 1) + True + """ + super().__init__(X) + + self._normalized = False + + if check: + # check parent + from sage.schemes.weighted_projective.weighted_projective_homset import SchemeHomset_points_weighted_projective_ring + if not isinstance(X, SchemeHomset_points_weighted_projective_ring): + raise TypeError(f"ambient space {X} must be a weighted projective space") + + from sage.rings.ring import CommutativeRing + d = X.codomain().ambient_space().ngens() + if isinstance(v, SchemeMorphism): + v = list(v) + else: + try: + if isinstance(v.parent(), CommutativeRing): + v = [v] + except AttributeError: + pass + if not isinstance(v, (list, tuple)): + raise TypeError("argument v (= %s) must be a scheme point, list, or tuple" % str(v)) + if len(v) != d and len(v) != d-1: + raise TypeError("v (=%s) must have %s components" % (v, d)) + + R = X.value_ring() + if not R.is_integral_domain(): + raise ValueError("cannot validate point over a ring that is not an integral domain, " + "pass check=False to construct the point") + v = Sequence(v, R) + if len(v) == d-1: # very common special case + v.append(R.one()) + + # (0 : 0 : ... : 0) is not a valid (weighted) projective point + if not any(v): + raise ValueError(f"{v} does not define a valid projective " + "point since all entries are zero") + + X.extended_codomain()._check_satisfies_equations(v) + + self._coords = tuple(v) + + # we (try to) normalise coordinates! + self.normalize_coordinates() + + else: + self._coords = tuple(v) + + def _repr_(self) -> str: + return "({})".format(" : ".join(map(repr, self._coords))) + + def _latex_(self) -> str: + from sage.misc.latex import latex + return r"\left({}\right)".format(" : ".join(map(latex, self._coords))) + + def _richcmp_(self, other: SchemeMorphism_point, op) -> bool: + """ + Test the weighted projective equality of two points. + + INPUT: + + - ``other`` -- a point on weighted projective space + + OUTPUT: + + Boolean + + EXAMPLES:: + + sage: WP = WeightedProjectiveSpace([3, 4, 5], QQ) + sage: u = abs(QQ.random_element()) + 1 / 4 # nonzero + sage: P, Q = WP(2, 3, 4), WP(2 * u^3, 3 * u^4, 4 * u^5) + sage: P == Q + True + sage: P != Q + False + + :: + + sage: P, Q = WP(2, 3, 4), WP(2, 3, 5) + sage: P < Q + True + sage: P <= Q + True + sage: P > Q + False + sage: P >= Q + False + + In the current implementation, points over different base rings + are never equal (this may change later):: + + sage: WP(2, 3, 4) == WeightedProjectiveSpace([3, 4, 5], ZZ)(2, 3, 4) + False + """ + space = self.codomain() + if space is not other.codomain(): + return op == op_NE + + if op in (op_EQ, op_NE): + weights = space._weights + # (other[i] / self[i])^(1 / weight[i]) all equal + # check weights + if (weights == other.codomain()._weights) != (op == op_EQ): + return False + + # check zeros + b1 = all(c1 == c2 + for c1, c2 in zip(self._coords, other._coords) + if c1 == 0 or c2 == 0) + if b1 != (op == op_EQ): + return False + + # check nonzeros + prod_weights = prod(weights) + ratio = [(c1 / c2) ** (prod_weights // w) + for c1, c2, w in zip(self._coords, other._coords, weights) + if c1 != 0 and c2 != 0] + r0 = ratio[0] + b2 = all(r == r0 for r in ratio) + return b2 == (op == op_EQ) + + return richcmp(self._coords, other._coords, op) + + def __hash__(self): + """ + Compute the hash value of this point. + + We attempt to normalise the coordinates of this point over the field of + fractions of the base ring. If this is not possible, return the hash of + the parent. See :meth:`normalize_coordinates` for more details. + + OUTPUT: Integer. + + .. SEEALSO :: + + :meth:`normalize_coordinates` + + EXAMPLES:: + + sage: WP = WeightedProjectiveSpace([1, 3, 1], QQ) + sage: hash(WP(6, 24, 2)) == hash(WP(3, 3, 1)) + True + sage: WP = WeightedProjectiveSpace([1, 3, 1], ZZ) + sage: hash(WP(6, 24, 2)) == hash(WP(3, 3, 1)) + True + """ + R = self.codomain().base_ring() + P = self.change_ring(FractionField(R)) + P.normalize_coordinates() + if P._normalized: + return hash(tuple(P)) + # if there is no good way to normalize return a constant value + return hash(self.codomain()) + + def scale_by(self, t) -> None: + """ + Scale the coordinates of the point by ``t``. + + A :exc:`TypeError` occurs if the point is not in the + base_ring of the codomain after scaling. + + INPUT: + + - ``t`` -- a ring element + + OUTPUT: none + + EXAMPLES:: + + sage: WP = WeightedProjectiveSpace([3, 4, 5], ZZ) + sage: P = WP([8, 16, 32]); P + (8 : 16 : 32) + sage: P.scale_by(1 / 2); P + (1 : 1 : 1) + sage: P.scale_by(1 / 3); P + Traceback (most recent call last): + ... + TypeError: ... + """ + if t.is_zero(): + raise ValueError("Cannot scale by 0") + R = self.codomain().base_ring() + self._coords = tuple([R(u * t**w) for u, w in zip(self._coords, self.codomain()._weights)]) + self._normalized = False + + def normalize_coordinates(self) -> None: + """ + Normalise coordinates of this weighted projective point if possible. + + Currently, this method checks if (1) the ambient weighted projective + space is defined over a field and (2) the weight of any index is `1`. + If so, the last of which is rescaled to `1`. + + EXAMPLES:: + + sage: WP = WeightedProjectiveSpace([3, 1, 5], QQ) + sage: P = WP([8, 16, 32]); P + (1/512 : 1 : 1/32768) + sage: P.scale_by(13); P + (2197/512 : 13 : 371293/32768) + sage: P.normalize_coordinates(); P + (1/512 : 1 : 1/32768) + + :: + + sage: WP = WeightedProjectiveSpace([3, 4, 5], ZZ) + sage: P = WP([8, 16, 32]); P + (8 : 16 : 32) + sage: P.normalize_coordinates(); P + (8 : 16 : 32) + """ + if self._normalized: + return + + if self.base_ring() in Fields(): + weights = self.codomain()._weights + coords = self._coords + for i in reversed(range(len(coords))): + w, c = weights[i], coords[i] + if w.is_one() and not c.is_zero(): + # we normalise w.r.t this coordinate + self.scale_by(~c) + self._normalized = True + return diff --git a/src/sage/schemes/weighted_projective/weighted_projective_space.py b/src/sage/schemes/weighted_projective/weighted_projective_space.py new file mode 100644 index 00000000000..d6fdefc750e --- /dev/null +++ b/src/sage/schemes/weighted_projective/weighted_projective_space.py @@ -0,0 +1,419 @@ +from sage.categories.fields import Fields +from sage.categories.map import Map +from sage.misc.latex import latex +from sage.misc.prandom import shuffle +from sage.rings.integer import Integer +from sage.rings.integer_ring import ZZ +from sage.rings.polynomial.multi_polynomial_ring_base import MPolynomialRing_base +from sage.rings.polynomial.polynomial_ring import PolynomialRing_generic +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.polynomial.term_order import TermOrder +from sage.schemes.generic.ambient_space import AmbientSpace +from sage.schemes.projective.projective_space import ProjectiveSpace, _CommRings +from sage.structure.all import UniqueRepresentation +from sage.structure.category_object import normalize_names + +from sage.schemes.weighted_projective.weighted_projective_homset import SchemeHomset_points_weighted_projective_ring + + +def WeightedProjectiveSpace(weights, R=None, names=None): + r""" + Return a weighted projective space with the given ``weights`` over the ring ``R``. + + EXAMPLES:: + + sage: WP = WeightedProjectiveSpace([1, 3, 1]); WP + Weighted Projective Space of dimension 2 with weights (1, 3, 1) over Integer Ring + """ + if isinstance(weights, (MPolynomialRing_base, PolynomialRing_generic)) and R is None: + if names is not None: + # Check for the case that the user provided a variable name + # That does not match what we wanted to use from R + names = normalize_names(weights.ngens(), names) + if weights.variable_names() != names: + # The provided name doesn't match the name of R's variables + raise NameError( + "variable names passed to ProjectiveSpace conflict with names in ring" + ) + A = WeightedProjectiveSpace( + weights.ngens() - 1, weights.base_ring(), names=weights.variable_names() + ) + A._coordinate_ring = weights + return A + + if isinstance(R, (int, Integer, list, tuple)): + weights, R = R, weights + elif R is None: + R = ZZ + + # WeightedProjectiveSpace(5) -> just return unweighted version + if isinstance(weights, (int, Integer)): + return ProjectiveSpace(weights, R=R, names=names) + elif isinstance(weights, (list, tuple)): + # Make it hashable + weights = tuple(map(Integer, weights)) + if any(w <= 0 for w in weights): + raise TypeError( + f"weights(={weights}) should only consist of positive integers" + ) + else: + raise TypeError(f"weights={weights} must be an integer, a list or a tuple") + + if names is None: + names = "x" + + # TODO: Specialise implementation to projective spaces over non-rings. + if R in _CommRings: + return WeightedProjectiveSpace_ring(weights, R=R, names=names) + + raise TypeError(f"R (={R}) must be a commutative ring") + + +class WeightedProjectiveSpace_ring(UniqueRepresentation, AmbientSpace): + """ + Weighted projective space with the given ``weights`` over the ring `R`. + + EXAMPLES:: + + sage: WeightedProjectiveSpace(Zp(5), [1, 3, 1], 'y') # needs sage.rings.padics + Weighted Projective Space of dimension 2 with weights (1, 3, 1) over 5-adic Ring with + capped relative precision 20 + sage: WeightedProjectiveSpace(QQ, 5, 'y') + Projective Space of dimension 5 over Rational Field + sage: _ is ProjectiveSpace(QQ, 5, 'y') + True + """ + + @staticmethod + def __classcall__(cls, weights: tuple[Integer], R=ZZ, names=None): + # __classcall_ is the "preprocessing" step for UniqueRepresentation + # see docs of CachedRepresentation + # weights should be a tuple, also because it should be hashable + if not isinstance(weights, tuple): + raise TypeError( + f"weights(={weights}) is not a tuple. Please use the" + "`WeightedProjectiveSpace` constructor" + ) + + normalized_names = normalize_names(len(weights), names) + return super().__classcall__(cls, weights, R, normalized_names) + + def __init__(self, weights: tuple[Integer], R=ZZ, names=None): + """ + Initialization function. + """ + AmbientSpace.__init__(self, len(weights) - 1, R) + self._weights = weights + self._assign_names(names) + + def weights(self) -> tuple[Integer]: + """ + Return the tuple of weights of this weighted projective space. + + EXAMPLES:: + + sage: WeightedProjectiveSpace(QQ, [1, 3, 1]).weights() + (1, 3, 1) + """ + return self._weights + + def ngens(self) -> Integer: + """ + Return the number of generators of this weighted projective space. + + This is the number of variables in the coordinate ring of ``self``. + + EXAMPLES:: + + sage: WeightedProjectiveSpace(QQ, [1, 3, 1]).ngens() + 3 + sage: WeightedProjectiveSpace(ZZ, 5).ngens() + 6 + """ + return self.dimension_relative() + 1 + + def _check_satisfies_equations(self, v: list[Integer] | tuple[Integer]) -> bool: + """ + Return ``True`` if ``v`` defines a point on the weighted projective + plane; raise a :class:`TypeError` otherwise. + + EXAMPLES:: + + sage: P = WeightedProjectiveSpace(ZZ, [1, 3, 1]) + sage: P._check_satisfies_equations([1, 1, 0]) + True + + sage: P._check_satisfies_equations([1, 0]) + Traceback (most recent call last): + ... + TypeError: the list v=[1, 0] must have 3 components + + sage: P._check_satisfies_equations([1/2, 0, 1]) + Traceback (most recent call last): + ... + TypeError: no conversion of this rational to integer + """ + if not isinstance(v, (list, tuple)): + raise TypeError(f"the argument v={v} must be a list or tuple") + + n = self.ngens() + if len(v) != n: + raise TypeError(f"the list v={v} must have {n} components") + + v = list(map(self.base_ring(), v)) + if all(vi.is_zero() for vi in v): + raise TypeError("the zero vector is not a point in projective space") + + return True + + def coordinate_ring(self) -> PolynomialRing_generic: + """ + Return the coordinate ring of this weighted projective space. + + EXAMPLES:: + + sage: WP = WeightedProjectiveSpace(GF(19^2, 'α'), [1, 3, 4, 1], 'abcd') + sage: # needs sage.rings.finite_rings + sage: R = WP.coordinate_ring(); R + Multivariate Polynomial Ring in a, b, c, d over Finite Field in α of size 19^2 + sage: R.term_order() + Weighted degree reverse lexicographic term order with weights (1, 3, 4, 1) + + :: + + sage: WP = WeightedProjectiveSpace(QQ, [1, 1, 1], ['alpha', 'beta', 'gamma']) + sage: R = WP.coordinate_ring(); R + Multivariate Polynomial Ring in alpha, beta, gamma over Rational Field + sage: R.term_order() + Weighted degree reverse lexicographic term order with weights (1, 1, 1) + """ + if not hasattr(self, "_coordinate_ring"): # we don't use cached_method to allow override in WeightedProjectiveSpace + term_order = TermOrder("wdegrevlex", self.weights()) + self._coordinate_ring = PolynomialRing( + self.base_ring(), + self.dimension_relative() + 1, + names=self.variable_names(), + order=term_order, + ) + return self._coordinate_ring + + def _validate(self, polynomials): + """ + If ``polynomials`` is a tuple of valid polynomial functions on ``self``, + return ``polynomials``, otherwise raise ``TypeError``. + + Since this is a weighted projective space, polynomials must be + homogeneous with respect to the grading of this space. + + INPUT: + + - ``polynomials`` -- tuple of polynomials in the coordinate ring of + this space. + + OUTPUT: + + - tuple of polynomials in the coordinate ring of this space. + + EXAMPLES:: + + sage: P. = WeightedProjectiveSpace(QQ, [1, 3, 1]) + sage: P._validate([x*y - z^4, x]) + [x*y - z^4, x] + sage: P._validate([x*y - z^2, x]) + Traceback (most recent call last): + ... + TypeError: x*y - z^2 is not homogeneous with weights (1, 3, 1) + sage: P._validate(x*y - z) + Traceback (most recent call last): + ... + TypeError: the argument polynomials=x*y - z must be a list or tuple + """ + if not isinstance(polynomials, (list, tuple)): + raise TypeError( + f"the argument polynomials={polynomials} must be a list or tuple" + ) + + R = self.coordinate_ring() + for f in map(R, polynomials): + if not f.is_homogeneous(): + raise TypeError(f"{f} is not homogeneous with weights {self.weights()}") + + return polynomials + + def _latex_(self) -> str: + r""" + Return a LaTeX representation of this weighted projective space. + + EXAMPLES:: + + sage: print(latex(WeightedProjectiveSpace(ZZ, [1, 3, 1], 'x'))) + {\mathbf P}_{\Bold{Z}}^{[1, 3, 1]} + + TESTS:: + + sage: WeightedProjectiveSpace(Zp(5), [2, 1, 3], 'y')._latex_() # needs sage.rings.padics + '{\\mathbf P}_{\\Bold{Z}_{5}}^{[2, 1, 3]}' + """ + return ( + fr"{{\mathbf P}}_{{{latex(self.base_ring())}}}^{{{list(self.weights())}}}" + ) + + def _morphism(self, *_, **__): + """ + Construct a morphism. + + For internal use only. See :mod:`morphism` for details. + """ + raise NotImplementedError( + "_morphism not implemented for weighted projective space" + ) + + def _homset(self, *_, **__): + """ + Construct the Hom-set. + """ + raise NotImplementedError( + "_homset not implemented for weighted projective space" + ) + + def _point_homset(self, *args, **kwds): + """ + Construct a point Hom-set. + + For internal use only. See :mod:`morphism` for details. + """ + return SchemeHomset_points_weighted_projective_ring(*args, **kwds) + + def point(self, v, check: bool = True): + """ + Create a point on this weighted projective space. + + INPUT: + + INPUT: + + - ``v`` -- anything that defines a point + + - ``check`` -- boolean (default: ``True``); whether + to check the defining data for consistency + + OUTPUT: A point of this weighted projective space. + + EXAMPLES:: + + sage: WP = WeightedProjectiveSpace(QQ, [1, 3, 1]) + sage: WP.point([2, 3, 1]) + (2 : 3 : 1) + """ + from sage.rings.infinity import infinity + + if v is infinity or ( + isinstance(v, (list, tuple)) and len(v) == 1 and v[0] is infinity + ): + if self.dimension_relative() > 1: + raise ValueError("%s not well defined in dimension > 1" % v) + v = [1, 0] + + return self.point_homset()(v, check=check) + + def _point(self, *args, **kwds): + """ + Construct a point. + + For internal use only. See :mod:`morphism` for details. + """ + from sage.schemes.weighted_projective.weighted_projective_point import SchemeMorphism_point_weighted_projective_ring + return SchemeMorphism_point_weighted_projective_ring(*args, **kwds) + + def _repr_(self) -> str: + """ + Return a string representation of this weighted projective space. + + EXAMPLES:: + + sage: WeightedProjectiveSpace(Qp(5), [1, 3, 1], 'x') # needs sage.rings.padics + Weighted Projective Space of dimension 2 with weights (1, 3, 1) + over 5-adic Field with capped relative precision 20 + """ + return ( + f"Weighted Projective Space of dimension {self.dimension_relative()} with weights" + f" {self.weights()} over {self.base_ring()}" + ) + + def change_ring(self, R): + r""" + Return a weighted projective space over ring ``R``. + + INPUT: + + - ``R`` -- commutative ring or morphism + + OUTPUT: weighted projective space over ``R``. + If ``R`` is a morphism, return a weighted projective space over its codomain. + + .. NOTE:: + + There is no need to have any relation between ``R`` and the base ring + of this space, if you want to have such a relation, use + ``self.base_extend(R)`` instead. + + EXAMPLES:: + + sage: WP = WeightedProjectiveSpace([1, 3, 1], ZZ); WP + Weighted Projective Space of dimension 2 with weights (1, 3, 1) over Integer Ring + sage: WP.change_ring(QQ) + Weighted Projective Space of dimension 2 with weights (1, 3, 1) over Rational Field + sage: WP.change_ring(GF(5)) + Weighted Projective Space of dimension 2 with weights (1, 3, 1) over Finite Field of size 5 + """ + if isinstance(R, Map): + return WeightedProjectiveSpace(self.weights(), R.codomain(), + self.variable_names()) + else: + return WeightedProjectiveSpace(self.weights(), R, + self.variable_names()) + + def _an_element_(self): + r""" + Return a (preferably typical) element of this space. + + This is used both for illustration and testing purposes. + + OUTPUT: a point in this weighted projective space. + + EXAMPLES:: + + sage: WeightedProjectiveSpace(ZZ, [1, 3, 1], 'x').an_element() # random + (1 : 2 : 3) + sage: WeightedProjectiveSpace(ZZ["y"], [2, 3, 1], 'x').an_element() # random + (3*y : 2*y : y) + """ + n = self.dimension_relative() + R = self.base_ring() + coords = [(n + 1 - i) * R.an_element() for i in range(n + 1)] + shuffle(coords) + return self(coords) + + def subscheme(self, *_, **__): + raise NotImplementedError("subscheme of weighted projective space has not been implemented") + + def curve(self, F): + r""" + Return a curve defined by ``F`` in this weighted projective space. + + INPUT: + + - ``F`` -- a polynomial, or a list or tuple of polynomials in + the coordinate ring of this weighted projective space + + EXAMPLES:: + + sage: WP. = WeightedProjectiveSpace([1, 3, 1], QQ) + sage: WP.curve(y^2 - x^5 * z - 3 * x^2 * z^4 - 2 * z^6) # needs sage.schemes + Weighted Projective Curve over Rational Field defined by y^2 - x^5*z - 3*x^2*z^4 - 2*z^6 + """ + if self.base_ring() not in Fields(): + raise NotImplementedError("curves in weighted projective space over" + "rings not implemented") + from sage.schemes.curves.constructor import Curve + return Curve(F, self)