From bb4b129364ed7f653bf1d30c86f858496b8844f2 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Thu, 8 May 2025 10:47:22 +0100 Subject: [PATCH 01/16] add basic weighted projective space functionality --- src/sage/schemes/all.py | 2 + src/sage/schemes/meson.build | 1 + .../schemes/weighted_projective/__init__.py | 1 + src/sage/schemes/weighted_projective/all.py | 23 + .../weighted_projective_curve.py | 25 ++ .../weighted_projective_homset.py | 38 ++ .../weighted_projective_point.py | 297 +++++++++++++ .../weighted_projective_space.py | 420 ++++++++++++++++++ 8 files changed, 807 insertions(+) create mode 100644 src/sage/schemes/weighted_projective/__init__.py create mode 100644 src/sage/schemes/weighted_projective/all.py create mode 100644 src/sage/schemes/weighted_projective/weighted_projective_curve.py create mode 100644 src/sage/schemes/weighted_projective/weighted_projective_homset.py create mode 100644 src/sage/schemes/weighted_projective/weighted_projective_point.py create mode 100644 src/sage/schemes/weighted_projective/weighted_projective_space.py 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/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/__init__.py b/src/sage/schemes/weighted_projective/__init__.py new file mode 100644 index 00000000000..9530e959fd3 --- /dev/null +++ b/src/sage/schemes/weighted_projective/__init__.py @@ -0,0 +1 @@ +# Here so that cython creates the correct module name \ No newline at end of file diff --git a/src/sage/schemes/weighted_projective/all.py b/src/sage/schemes/weighted_projective/all.py new file mode 100644 index 00000000000..cc7d9c99adb --- /dev/null +++ b/src/sage/schemes/weighted_projective/all.py @@ -0,0 +1,23 @@ +"""nodoctest +all.py -- export of projective schemes to Sage +""" + +# ***************************************************************************** +# +# Sage: Open Source Mathematical Software +# +# Copyright (C) 2005 William Stein +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# This code is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# The full text of the GPL is available at: +# +# 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_curve.py b/src/sage/schemes/weighted_projective/weighted_projective_curve.py new file mode 100644 index 00000000000..d555f8ffc36 --- /dev/null +++ b/src/sage/schemes/weighted_projective/weighted_projective_curve.py @@ -0,0 +1,25 @@ +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: P. = WeightedProjectiveSpace([1, 3, 1], QQ) + sage: C = P.curve(y^2 - x^5 * z - 3 * x^2 * z^4 - 2 * z^6); C + """ + def __init__(self, A, X, *kwargs): + # TODO ensure that A is the right type? + # Something like a `is_WeightProjectiveSpace` which means making a + # WeightProjectiveSpace class? + if not isinstance(A, WeightedProjectiveSpace_ring): + raise TypeError(f"A(={A}) is not a weighted projective space") + super().__init__(A, X, *kwargs) + + def curve(self): + return 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..ab3c7a2e491 --- /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) 2024 Gareth Ma +# Copyright (C) 2011 Volker Braun +# Copyright (C) 2006 William Stein +# +# 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_generic`. + + 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..91b1a87423e --- /dev/null +++ b/src/sage/schemes/weighted_projective/weighted_projective_point.py @@ -0,0 +1,297 @@ +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=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 + """ + SchemeMorphism.__init__(self, X) + + self._normalized = False + + if check: + # check parent + from 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() + 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") + + # over other rings, we do not have a generic method to check + # whether the given coordinates is a multiple of a zero divisor + # so we just let it pass. + 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): + return "({})".format(" : ".join(map(repr, self._coords))) + + def _richcmp_(self, other, op): + """ + 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 + """ + assert isinstance(other, SchemeMorphism_point) + + 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 + prod_weights = prod(weights) + # check weights + bw = weights == other.codomain()._weights + if bw != (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 + 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): + """ + 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): + """ + 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..d4060163834 --- /dev/null +++ b/src/sage/schemes/weighted_projective/weighted_projective_space.py @@ -0,0 +1,420 @@ +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): + @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. + + 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 + """ + 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"): + 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): + r""" + Return a LaTeX representation of this 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 ( + f"{{\\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. + """ + # raise NotImplementedError("_point_homset not implemented for weighted projective space") + return SchemeHomset_points_weighted_projective_ring(*args, **kwds) + + def point(self, v, check=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 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 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`` + + .. 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 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(QQ, 2) + sage: P.curve(y^2 - x^5 * z - 3 * x^2 * z^4 - 2 * z^6) # needs sage.schemes + Projective Plane 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) + From 8aae3d4e21a60757ce20b5eab56590e8b91f7117 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Thu, 8 May 2025 10:47:48 +0100 Subject: [PATCH 02/16] remove finished TODO --- .../schemes/weighted_projective/weighted_projective_curve.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/schemes/weighted_projective/weighted_projective_curve.py b/src/sage/schemes/weighted_projective/weighted_projective_curve.py index d555f8ffc36..9e6f0724642 100644 --- a/src/sage/schemes/weighted_projective/weighted_projective_curve.py +++ b/src/sage/schemes/weighted_projective/weighted_projective_curve.py @@ -14,9 +14,6 @@ class WeightedProjectiveCurve(Curve_generic): sage: C = P.curve(y^2 - x^5 * z - 3 * x^2 * z^4 - 2 * z^6); C """ def __init__(self, A, X, *kwargs): - # TODO ensure that A is the right type? - # Something like a `is_WeightProjectiveSpace` which means making a - # WeightProjectiveSpace class? if not isinstance(A, WeightedProjectiveSpace_ring): raise TypeError(f"A(={A}) is not a weighted projective space") super().__init__(A, X, *kwargs) From ebe3a942c36e3ca894d3fee73184cb24f5289a82 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Thu, 8 May 2025 19:52:58 +0100 Subject: [PATCH 03/16] clean up implementation of weighted (plane) curves and other docs --- src/sage/schemes/curves/constructor.py | 16 ++++++ .../curves/weighted_projective_curve.py | 57 +++++++++++++++++++ .../weighted_projective_curve.py | 22 ------- .../weighted_projective_homset.py | 2 +- .../weighted_projective_point.py | 4 +- .../weighted_projective_space.py | 14 ++--- 6 files changed, 83 insertions(+), 32 deletions(-) create mode 100644 src/sage/schemes/curves/weighted_projective_curve.py delete mode 100644 src/sage/schemes/weighted_projective/weighted_projective_curve.py diff --git a/src/sage/schemes/curves/constructor.py b/src/sage/schemes/curves/constructor.py index beff4dd649d..659ec49ed96 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..39b92b8461b --- /dev/null +++ b/src/sage/schemes/curves/weighted_projective_curve.py @@ -0,0 +1,57 @@ +# 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 +""" + +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): + 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 curve(self): + return self diff --git a/src/sage/schemes/weighted_projective/weighted_projective_curve.py b/src/sage/schemes/weighted_projective/weighted_projective_curve.py deleted file mode 100644 index 9e6f0724642..00000000000 --- a/src/sage/schemes/weighted_projective/weighted_projective_curve.py +++ /dev/null @@ -1,22 +0,0 @@ -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: P. = WeightedProjectiveSpace([1, 3, 1], QQ) - sage: C = P.curve(y^2 - x^5 * z - 3 * x^2 * z^4 - 2 * z^6); C - """ - 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 curve(self): - return diff --git a/src/sage/schemes/weighted_projective/weighted_projective_homset.py b/src/sage/schemes/weighted_projective/weighted_projective_homset.py index ab3c7a2e491..024384883db 100644 --- a/src/sage/schemes/weighted_projective/weighted_projective_homset.py +++ b/src/sage/schemes/weighted_projective/weighted_projective_homset.py @@ -26,7 +26,7 @@ class SchemeHomset_points_weighted_projective_ring(SchemeHomset_points): INPUT: - See :class:`SchemeHomset_generic`. + See :class:`SchemeHomset_points`. EXAMPLES:: diff --git a/src/sage/schemes/weighted_projective/weighted_projective_point.py b/src/sage/schemes/weighted_projective/weighted_projective_point.py index 91b1a87423e..006bb759de7 100644 --- a/src/sage/schemes/weighted_projective/weighted_projective_point.py +++ b/src/sage/schemes/weighted_projective/weighted_projective_point.py @@ -45,7 +45,7 @@ class SchemeMorphism_point_weighted_projective_ring(SchemeMorphism_point): - ``v`` -- a list or tuple of coordinates in `R`. - - ``check`` -- boolean (default:``True``). Whether to check the input for consistency. + - ``check`` -- boolean (default: ``True``). Whether to check the input for consistency. EXAMPLES:: @@ -83,7 +83,7 @@ def __init__(self, X, v, check=True): if check: # check parent - from weighted_projective_homset import SchemeHomset_points_weighted_projective_ring + 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") diff --git a/src/sage/schemes/weighted_projective/weighted_projective_space.py b/src/sage/schemes/weighted_projective/weighted_projective_space.py index d4060163834..2dc6d39b9a4 100644 --- a/src/sage/schemes/weighted_projective/weighted_projective_space.py +++ b/src/sage/schemes/weighted_projective/weighted_projective_space.py @@ -242,7 +242,7 @@ def _validate(self, polynomials): def _latex_(self): r""" - Return a LaTeX representation of this projective space. + Return a LaTeX representation of this weighted projective space. EXAMPLES:: @@ -323,12 +323,12 @@ def _point(self, *args, **kwds): For internal use only. See :mod:`morphism` for details. """ - from weighted_projective_point import SchemeMorphism_point_weighted_projective_ring + 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 projective space. + Return a string representation of this weighted projective space. EXAMPLES:: @@ -379,7 +379,7 @@ def _an_element_(self): This is used both for illustration and testing purposes. - OUTPUT: a point in this projective space. + OUTPUT: a point in this weighted projective space. EXAMPLES:: @@ -408,9 +408,9 @@ def curve(self, F): EXAMPLES:: - sage: WP. = WeightedProjectiveSpace(QQ, 2) - sage: P.curve(y^2 - x^5 * z - 3 * x^2 * z^4 - 2 * z^6) # needs sage.schemes - Projective Plane Curve over Rational Field defined by y^2 - x^5*z - 3*x^2*z^4 - 2*z^6 + 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" From 00dce0abdc6f7253e1ffdb413f242c18729eeb12 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Fri, 16 May 2025 01:40:29 +0100 Subject: [PATCH 04/16] remove useless .curve method --- src/sage/schemes/curves/weighted_projective_curve.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/schemes/curves/weighted_projective_curve.py b/src/sage/schemes/curves/weighted_projective_curve.py index 39b92b8461b..228db81cb1e 100644 --- a/src/sage/schemes/curves/weighted_projective_curve.py +++ b/src/sage/schemes/curves/weighted_projective_curve.py @@ -52,6 +52,3 @@ def _repr_type(self): 'Weighted Projective' """ return "Weighted Projective" - - def curve(self): - return self From 633004320225c03271cd81a7e41ec569cc4b2455 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Fri, 16 May 2025 02:03:11 +0100 Subject: [PATCH 05/16] implement converting wp curve to proj curve --- .../curves/weighted_projective_curve.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/sage/schemes/curves/weighted_projective_curve.py b/src/sage/schemes/curves/weighted_projective_curve.py index 228db81cb1e..a61d95fbfb6 100644 --- a/src/sage/schemes/curves/weighted_projective_curve.py +++ b/src/sage/schemes/curves/weighted_projective_curve.py @@ -52,3 +52,39 @@ def _repr_type(self): '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) From 657d42f873e62f4f588b85e46510d15bd7f597c2 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Fri, 14 Nov 2025 21:32:05 +0700 Subject: [PATCH 06/16] Delete a __init__.py file --- .gitignore | 1 + src/sage/schemes/weighted_projective/__init__.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 src/sage/schemes/weighted_projective/__init__.py 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/sage/schemes/weighted_projective/__init__.py b/src/sage/schemes/weighted_projective/__init__.py deleted file mode 100644 index 9530e959fd3..00000000000 --- a/src/sage/schemes/weighted_projective/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# Here so that cython creates the correct module name \ No newline at end of file From 8c4f225cbef44722735f572e85db365b44eb1b89 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Fri, 14 Nov 2025 21:38:48 +0700 Subject: [PATCH 07/16] Fix lint --- .../schemes/weighted_projective/weighted_projective_space.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/schemes/weighted_projective/weighted_projective_space.py b/src/sage/schemes/weighted_projective/weighted_projective_space.py index 2dc6d39b9a4..75d0baac3b0 100644 --- a/src/sage/schemes/weighted_projective/weighted_projective_space.py +++ b/src/sage/schemes/weighted_projective/weighted_projective_space.py @@ -16,7 +16,6 @@ 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``. @@ -417,4 +416,3 @@ def curve(self, F): "rings not implemented") from sage.schemes.curves.constructor import Curve return Curve(F, self) - From b44aef47d1ceec61c1b55e5a77157199af93e4b1 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 15 Nov 2025 03:17:34 +0700 Subject: [PATCH 08/16] Update .rst files --- src/doc/en/reference/curves/index.rst | 1 + src/doc/en/reference/schemes/index.rst | 10 ++++++++++ 2 files changed, 11 insertions(+) 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 ----------------------------- From 04585f0c74f39be20f05a28b62c6044990035329 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 15 Nov 2025 03:23:01 +0700 Subject: [PATCH 09/16] Add some type annotations --- .../weighted_projective/weighted_projective_point.py | 8 ++++---- .../weighted_projective/weighted_projective_space.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/weighted_projective/weighted_projective_point.py b/src/sage/schemes/weighted_projective/weighted_projective_point.py index 006bb759de7..cf00d6839a7 100644 --- a/src/sage/schemes/weighted_projective/weighted_projective_point.py +++ b/src/sage/schemes/weighted_projective/weighted_projective_point.py @@ -125,10 +125,10 @@ def __init__(self, X, v, check=True): else: self._coords = tuple(v) - def _repr_(self): + def _repr_(self) -> str: return "({})".format(" : ".join(map(repr, self._coords))) - def _richcmp_(self, other, op): + def _richcmp_(self, other, op) -> bool: """ Test the weighted projective equality of two points. @@ -225,7 +225,7 @@ def __hash__(self): # if there is no good way to normalize return a constant value return hash(self.codomain()) - def scale_by(self, t): + def scale_by(self, t) -> None: """ Scale the coordinates of the point by ``t``. @@ -256,7 +256,7 @@ def scale_by(self, t): self._coords = tuple([R(u * t**w) for u, w in zip(self._coords, self.codomain()._weights)]) self._normalized = False - def normalize_coordinates(self): + def normalize_coordinates(self) -> None: """ Normalise coordinates of this weighted projective point if possible. diff --git a/src/sage/schemes/weighted_projective/weighted_projective_space.py b/src/sage/schemes/weighted_projective/weighted_projective_space.py index 75d0baac3b0..49ef3fab847 100644 --- a/src/sage/schemes/weighted_projective/weighted_projective_space.py +++ b/src/sage/schemes/weighted_projective/weighted_projective_space.py @@ -239,7 +239,7 @@ def _validate(self, polynomials): return polynomials - def _latex_(self): + def _latex_(self) -> str: r""" Return a LaTeX representation of this weighted projective space. From 4da03f1c7c0b2a7484453304413e43ee94d2899a Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 15 Nov 2025 03:44:13 +0700 Subject: [PATCH 10/16] Use super() to avoid confusion --- .../schemes/weighted_projective/weighted_projective_point.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/weighted_projective/weighted_projective_point.py b/src/sage/schemes/weighted_projective/weighted_projective_point.py index cf00d6839a7..7a8c3c1d3fe 100644 --- a/src/sage/schemes/weighted_projective/weighted_projective_point.py +++ b/src/sage/schemes/weighted_projective/weighted_projective_point.py @@ -77,7 +77,7 @@ def __init__(self, X, v, check=True): sage: P == WP(1, 3 / 2, 1, 1) True """ - SchemeMorphism.__init__(self, X) + super().__init__(X) self._normalized = False From 33bbb0909b5e339e39f1d1d667719978410cbaf6 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 15 Nov 2025 03:46:14 +0700 Subject: [PATCH 11/16] Fix a minor typo --- .../schemes/weighted_projective/weighted_projective_point.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/weighted_projective/weighted_projective_point.py b/src/sage/schemes/weighted_projective/weighted_projective_point.py index 7a8c3c1d3fe..18f677d6046 100644 --- a/src/sage/schemes/weighted_projective/weighted_projective_point.py +++ b/src/sage/schemes/weighted_projective/weighted_projective_point.py @@ -45,7 +45,7 @@ class SchemeMorphism_point_weighted_projective_ring(SchemeMorphism_point): - ``v`` -- a list or tuple of coordinates in `R`. - - ``check`` -- boolean (default: ``True``). Whether to check the input for consistency. + - ``check`` -- boolean (default: ``True``); Whether to check the input for consistency. EXAMPLES:: From 0b2d4b6d601bdaf13269b02b8fda557e3391e545 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 15 Nov 2025 14:05:18 +0700 Subject: [PATCH 12/16] Apply suggestions from code review Co-authored-by: Vincent Macri --- src/sage/schemes/curves/weighted_projective_curve.py | 2 +- .../weighted_projective/weighted_projective_point.py | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/curves/weighted_projective_curve.py b/src/sage/schemes/curves/weighted_projective_curve.py index a61d95fbfb6..a57fc71b1b4 100644 --- a/src/sage/schemes/curves/weighted_projective_curve.py +++ b/src/sage/schemes/curves/weighted_projective_curve.py @@ -39,7 +39,7 @@ def __init__(self, A, X, *kwargs): raise TypeError(f"A(={A}) is not a weighted projective space") super().__init__(A, X, *kwargs) - def _repr_type(self): + def _repr_type(self) -> str: r""" Return a string representation of the type of this curve. diff --git a/src/sage/schemes/weighted_projective/weighted_projective_point.py b/src/sage/schemes/weighted_projective/weighted_projective_point.py index 18f677d6046..baf52322733 100644 --- a/src/sage/schemes/weighted_projective/weighted_projective_point.py +++ b/src/sage/schemes/weighted_projective/weighted_projective_point.py @@ -54,7 +54,7 @@ class SchemeMorphism_point_weighted_projective_ring(SchemeMorphism_point): (2 : 3 : 4) """ - def __init__(self, X, v, check=True): + def __init__(self, X, v, check: bool = True): """ The Python constructor. @@ -168,13 +168,12 @@ def _richcmp_(self, other, op) -> bool: if space is not other.codomain(): return op == op_NE - if op in [op_EQ, op_NE]: + if op in (op_EQ, op_NE): weights = space._weights # (other[i] / self[i])^(1 / weight[i]) all equal prod_weights = prod(weights) # check weights - bw = weights == other.codomain()._weights - if bw != (op == op_EQ): + if (weights == other.codomain()._weights) != (op == op_EQ): return False # check zeros From 428671f58a41fdcbe4248caa4b409e4acce3b6a0 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sun, 16 Nov 2025 14:52:22 +0700 Subject: [PATCH 13/16] Apply suggestions --- .../curves/weighted_projective_curve.py | 15 ++++++++ src/sage/schemes/weighted_projective/all.py | 23 +++++------- .../weighted_projective_homset.py | 4 +-- .../weighted_projective_point.py | 6 +++- .../weighted_projective_space.py | 36 +++++++++---------- 5 files changed, 48 insertions(+), 36 deletions(-) diff --git a/src/sage/schemes/curves/weighted_projective_curve.py b/src/sage/schemes/curves/weighted_projective_curve.py index a57fc71b1b4..78c75b69e10 100644 --- a/src/sage/schemes/curves/weighted_projective_curve.py +++ b/src/sage/schemes/curves/weighted_projective_curve.py @@ -16,8 +16,23 @@ 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 diff --git a/src/sage/schemes/weighted_projective/all.py b/src/sage/schemes/weighted_projective/all.py index cc7d9c99adb..e9d239e8251 100644 --- a/src/sage/schemes/weighted_projective/all.py +++ b/src/sage/schemes/weighted_projective/all.py @@ -2,22 +2,15 @@ all.py -- export of projective schemes to Sage """ -# ***************************************************************************** -# -# Sage: Open Source Mathematical Software -# -# Copyright (C) 2005 William Stein -# -# Distributed under the terms of the GNU General Public License (GPL) -# -# This code is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# The full text of the GPL is available at: +# **************************************************************************** +# 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 index 024384883db..30445816837 100644 --- a/src/sage/schemes/weighted_projective/weighted_projective_homset.py +++ b/src/sage/schemes/weighted_projective/weighted_projective_homset.py @@ -7,9 +7,9 @@ """ # ***************************************************************************** -# Copyright (C) 2024 Gareth Ma -# Copyright (C) 2011 Volker Braun # 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 diff --git a/src/sage/schemes/weighted_projective/weighted_projective_point.py b/src/sage/schemes/weighted_projective/weighted_projective_point.py index baf52322733..68f6bda7d99 100644 --- a/src/sage/schemes/weighted_projective/weighted_projective_point.py +++ b/src/sage/schemes/weighted_projective/weighted_projective_point.py @@ -128,6 +128,10 @@ def __init__(self, X, v, check: bool = True): 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, op) -> bool: """ Test the weighted projective equality of two points. @@ -171,7 +175,6 @@ def _richcmp_(self, other, op) -> bool: if op in (op_EQ, op_NE): weights = space._weights # (other[i] / self[i])^(1 / weight[i]) all equal - prod_weights = prod(weights) # check weights if (weights == other.codomain()._weights) != (op == op_EQ): return False @@ -184,6 +187,7 @@ def _richcmp_(self, other, op) -> bool: 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] diff --git a/src/sage/schemes/weighted_projective/weighted_projective_space.py b/src/sage/schemes/weighted_projective/weighted_projective_space.py index 49ef3fab847..aeffeda0c14 100644 --- a/src/sage/schemes/weighted_projective/weighted_projective_space.py +++ b/src/sage/schemes/weighted_projective/weighted_projective_space.py @@ -25,10 +25,7 @@ def WeightedProjectiveSpace(weights, R=None, names=None): 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 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 @@ -73,6 +70,20 @@ def WeightedProjectiveSpace(weights, R=None, names=None): 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 @@ -90,16 +101,6 @@ def __classcall__(cls, weights: tuple[Integer], R=ZZ, names=None): def __init__(self, weights: tuple[Integer], R=ZZ, names=None): """ Initialization function. - - 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 """ AmbientSpace.__init__(self, len(weights) - 1, R) self._weights = weights @@ -186,7 +187,7 @@ def coordinate_ring(self) -> PolynomialRing_generic: sage: R.term_order() Weighted degree reverse lexicographic term order with weights (1, 1, 1) """ - if not hasattr(self, "_coordinate_ring"): + 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(), @@ -254,7 +255,7 @@ def _latex_(self) -> str: '{\\mathbf P}_{\\Bold{Z}_{5}}^{[2, 1, 3]}' """ return ( - f"{{\\mathbf P}}_{{{latex(self.base_ring())}}}^{{{list(self.weights())}}}" + fr"{{\mathbf P}}_{{{latex(self.base_ring())}}}^{{{list(self.weights())}}}" ) def _morphism(self, *_, **__): @@ -281,10 +282,9 @@ def _point_homset(self, *args, **kwds): For internal use only. See :mod:`morphism` for details. """ - # raise NotImplementedError("_point_homset not implemented for weighted projective space") return SchemeHomset_points_weighted_projective_ring(*args, **kwds) - def point(self, v, check=True): + def point(self, v, check: bool = True): """ Create a point on this weighted projective space. From f22d08d5d8c278bf45c0823ef17ff8394d86726b Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sun, 16 Nov 2025 16:04:32 +0700 Subject: [PATCH 14/16] Apply suggested change --- .../weighted_projective/weighted_projective_point.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/schemes/weighted_projective/weighted_projective_point.py b/src/sage/schemes/weighted_projective/weighted_projective_point.py index 68f6bda7d99..3a8c137ce03 100644 --- a/src/sage/schemes/weighted_projective/weighted_projective_point.py +++ b/src/sage/schemes/weighted_projective/weighted_projective_point.py @@ -165,6 +165,12 @@ def _richcmp_(self, other, op) -> bool: 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 """ assert isinstance(other, SchemeMorphism_point) From 803a2fe3638a308bcd5a539ef40129eb5258c1a9 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Mon, 17 Nov 2025 18:58:32 +0700 Subject: [PATCH 15/16] Apply suggestions --- .../schemes/weighted_projective/weighted_projective_point.py | 4 +--- .../schemes/weighted_projective/weighted_projective_space.py | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/weighted_projective/weighted_projective_point.py b/src/sage/schemes/weighted_projective/weighted_projective_point.py index 3a8c137ce03..e6c27aa6452 100644 --- a/src/sage/schemes/weighted_projective/weighted_projective_point.py +++ b/src/sage/schemes/weighted_projective/weighted_projective_point.py @@ -132,7 +132,7 @@ def _latex_(self) -> str: from sage.misc.latex import latex return r"\left({}\right)".format(" : ".join(map(latex, self._coords))) - def _richcmp_(self, other, op) -> bool: + def _richcmp_(self, other: SchemeMorphism_point, op) -> bool: """ Test the weighted projective equality of two points. @@ -172,8 +172,6 @@ def _richcmp_(self, other, op) -> bool: sage: WP(2, 3, 4) == WeightedProjectiveSpace([3, 4, 5], ZZ)(2, 3, 4) False """ - assert isinstance(other, SchemeMorphism_point) - space = self.codomain() if space is not other.codomain(): return op == op_NE diff --git a/src/sage/schemes/weighted_projective/weighted_projective_space.py b/src/sage/schemes/weighted_projective/weighted_projective_space.py index aeffeda0c14..d6fdefc750e 100644 --- a/src/sage/schemes/weighted_projective/weighted_projective_space.py +++ b/src/sage/schemes/weighted_projective/weighted_projective_space.py @@ -348,7 +348,8 @@ def change_ring(self, R): - ``R`` -- commutative ring or morphism - OUTPUT: weighted projective space over ``R`` + OUTPUT: weighted projective space over ``R``. + If ``R`` is a morphism, return a weighted projective space over its codomain. .. NOTE:: From 2306b386d0cdf63053a227ba97b8f6a3c5d43e10 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Fri, 21 Nov 2025 20:16:49 +0700 Subject: [PATCH 16/16] Apply suggested changes --- .../weighted_projective/weighted_projective_point.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/weighted_projective/weighted_projective_point.py b/src/sage/schemes/weighted_projective/weighted_projective_point.py index e6c27aa6452..59737afbe86 100644 --- a/src/sage/schemes/weighted_projective/weighted_projective_point.py +++ b/src/sage/schemes/weighted_projective/weighted_projective_point.py @@ -103,6 +103,9 @@ def __init__(self, X, v, check: bool = True): 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()) @@ -112,9 +115,6 @@ def __init__(self, X, v, check: bool = True): raise ValueError(f"{v} does not define a valid projective " "point since all entries are zero") - # over other rings, we do not have a generic method to check - # whether the given coordinates is a multiple of a zero divisor - # so we just let it pass. X.extended_codomain()._check_satisfies_equations(v) self._coords = tuple(v)