Skip to content

Commit

Permalink
Add pull_from_function_field
Browse files Browse the repository at this point in the history
  • Loading branch information
kwankyu committed Oct 31, 2023
1 parent 07a2afd commit fb0ccfd
Show file tree
Hide file tree
Showing 2 changed files with 201 additions and 35 deletions.
168 changes: 134 additions & 34 deletions src/sage/schemes/curves/affine_curve.py
Original file line number Diff line number Diff line change
Expand Up @@ -2112,9 +2112,11 @@ def function(self, f):
INPUT:
- ``f`` -- an element of the coordinate ring of either the curve or its
- ``f`` -- an element of the fraction field of the coordinate ring of the curve or its
ambient space.
OUTPUT: An element of the function field of this curve.
EXAMPLES::
sage: # needs sage.rings.finite_rings
Expand Down Expand Up @@ -2155,38 +2157,76 @@ def coordinate_functions(self):
"""
return self._coordinate_functions

def pull_from_function_field(self, f):
"""
Return the fraction corresponding to ``f``.
INPUT:
- ``f`` -- an element of the function field
OUTPUT:
A fraction of polynomials in the coordinate ring of this curve.
EXAMPLES::
sage: # needs sage.rings.finite_rings
sage: A.<x,y> = AffineSpace(GF(8), 2)
sage: C = Curve(x^5 + y^5 + x*y + 1)
sage: F = C.function_field()
sage: C.pull_from_function_field(F.gen())
y
sage: C.pull_from_function_field(F.one())
1
sage: C.pull_from_function_field(F.zero())
0
sage: f1 = F.gen()
sage: f2 = F.base_ring().gen()
sage: C.function(C.pull_from_function_field(f1)) == f1
True
sage: C.function(C.pull_from_function_field(f2)) == f2
True
"""
return self._pull_from_function_field(f)

@lazy_attribute
def _nonsingular_model(self):
"""
Return the data of a nonsingular model of the curve.
The data consists of an abstract function field `M` and a map from the
coordinate ring `R` of the ambient space of the curve into the function
field. The coordinate ring of the curve is thus the quotient of `R` by
the kernel of the map.
fraction field of the coordinate ring `R` of the ambient space of the curve to the function
field. The coordinate ring of the curve is the quotient of `R` by
the kernel of the map restricted to `R`.
TESTS::
sage: A.<x,y,z> = AffineSpace(GF(11), 3)
sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A)
sage: C._nonsingular_model
(Function field in z defined by z^3 + 10*x,
Ring morphism:
From: Multivariate Polynomial Ring in x, y, z
over Finite Field of size 11
From: Fraction Field of Multivariate Polynomial Ring in x, y, z over Finite Field of size 11
To: Function field in z defined by z^3 + 10*x
Defn: x |--> x
y |--> z^2
z |--> z)
z |--> z,
Ring morphism:
From: Function field in z defined by z^3 + 10*x
To: Fraction Field of Multivariate Polynomial Ring in x, y, z over Finite Field of size 11)
"""
from sage.structure.sequence import Sequence
from sage.rings.fraction_field import FractionField
from sage.rings.function_field.constructor import FunctionField
from sage.rings.function_field.maps import FunctionFieldRingMorphism

k = self.base_ring()
I0 = self.defining_ideal()
I = self.defining_ideal()

# invlex is the lex order with x < y < z for R = k[x,y,z] for instance
R = I0.parent().ring().change_ring(order='invlex')
I0 = I0.change_ring(R)
R = I.parent().ring().change_ring(order='invlex')
I0 = I.change_ring(R)
n = R.ngens()

names = R.variable_names()
Expand Down Expand Up @@ -2222,6 +2262,7 @@ def _nonsingular_model(self):
# syzygy for z. Now x is the generator of a rational function field F0;
# y is the generator of the extension F1 of F0 by f3; z is the
# generator of the extension F2 of F1 by f2.

basis = list(gbasis)
syzygy = {}
for i in range(n):
Expand All @@ -2235,11 +2276,11 @@ def _nonsingular_model(self):
basis.append(f)
break

indep = [i for i in range(n) if i not in syzygy]
if len(indep) != 1:
# sanity check
indeps = [i for i in range(n) if i not in syzygy]
if len(indeps) != 1:
raise TypeError("not a curve")
else:
indep = indep[0]
indep = indeps[0]

F = FunctionField(k, names[indep])
coords = {indep: F.gen()}
Expand All @@ -2252,20 +2293,60 @@ def _nonsingular_model(self):
F = F.extension(f, names[i])
coords[i] = F.gen()

if F.base_field() is not F: # proper extension
proper_extension = F.base_field() is not F # and n is 1

if proper_extension:
N, from_N, to_N = F.simple_model()
M, from_M, to_M = N.separable_model()
coordinate_functions = tuple([to_M(to_N(F(coords[i]))) for i in range(n)])
else: # rational function field
M = F
else:
M = F # is rational function field
coordinate_functions = tuple([coords[i] for i in range(n)])

lift_to_function_field = hom(R, M, coordinate_functions)
# map to M

FR = FractionField(I.ring())
map_to_function_field = hom(FR, M, coordinate_functions)

# map from M

def convert(f, i):
if i == indep:
i = i - 1
if i < 0:
return f._x # fraction representing rational function field element
fx = f._x # polynomial representing function field element
if not fx:
fxlist = [fx.base_ring().zero()]
else:
fxlist = fx.list()
coeffs = Sequence(convert(c, i - 1) for c in fxlist)
B = coeffs.universe()
S = B[names[i]]
return S(coeffs)

z = M.gen()

if proper_extension:
Z = FR(convert(from_N(from_M(z)), n - 1))

def evaluate(f):
coeffs = f._x.list()
v = 0
while coeffs:
v = v * Z + coeffs.pop()._x
return FR(v)
else:
def evaluate(f):
return FR(f._x)

map_from_function_field = FunctionFieldRingMorphism(Hom(M, FR), evaluate)

# sanity check
assert all(lift_to_function_field(f).is_zero() for f in I0.gens())
assert all(map_to_function_field(f).is_zero() for f in I.gens())
assert map_to_function_field(map_from_function_field(z)) == z

return M, lift_to_function_field
return M, map_to_function_field, map_from_function_field

@lazy_attribute
def _function_field(self):
Expand All @@ -2284,16 +2365,15 @@ def _function_field(self):
@lazy_attribute
def _lift_to_function_field(self):
"""
Return the map to function field of the curve.
Return the map to the function field of the curve.
TESTS::
sage: A.<x,y,z> = AffineSpace(GF(11), 3)
sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A)
sage: C._lift_to_function_field
Ring morphism:
From: Multivariate Polynomial Ring in x, y, z
over Finite Field of size 11
From: Fraction Field of Multivariate Polynomial Ring in x, y, z over Finite Field of size 11
To: Function field in z defined by z^3 + 10*x
Defn: x |--> x
y |--> z^2
Expand All @@ -2315,16 +2395,34 @@ def _coordinate_functions(self):
"""
return self._nonsingular_model[1].im_gens()

@lazy_attribute
def _pull_from_function_field(self):
"""
Return the map from the function field of the curve.
TESTS::
sage: A.<x,y,z> = AffineSpace(GF(11), 3)
sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A)
sage: C._pull_from_function_field
Ring morphism:
From: Function field in z defined by z^3 + 10*x
To: Fraction Field of Multivariate Polynomial Ring in x, y, z over Finite Field of size 11
"""
return self._nonsingular_model[2]

@lazy_attribute
def _singularities(self):
"""
Return a list of the pairs of singular closed points and the places above it.
Return a list of the pairs of a singular closed point and the places
above it.
TESTS::
sage: A.<x,y> = AffineSpace(GF(7^2), 2) # needs sage.rings.finite_rings
sage: C = Curve(x^2 - x^4 - y^4) # needs sage.rings.finite_rings
sage: C._singularities # long time # needs sage.rings.finite_rings
sage: # needs sage.rings.finite_rings
sage: A.<x,y> = AffineSpace(GF(7^2), 2)
sage: C = Curve(x^2 - x^4 - y^4)
sage: C._singularities # long time
[(Point (x, y),
[Place (x, 1/x*y^3 + 1/x*y^2 + 1), Place (x, 1/x*y^3 + 1/x*y^2 + 6)])]
"""
Expand Down Expand Up @@ -2364,9 +2462,10 @@ def singular_closed_points(self):
EXAMPLES::
sage: A.<x,y> = AffineSpace(GF(7^2), 2) # needs sage.rings.finite_rings
sage: C = Curve(x^2 - x^4 - y^4) # needs sage.rings.finite_rings
sage: C.singular_closed_points() # needs sage.rings.finite_rings
sage: # needs sage.rings.finite_rings
sage: A.<x,y> = AffineSpace(GF(7^2), 2)
sage: C = Curve(x^2 - x^4 - y^4)
sage: C.singular_closed_points()
[Point (x, y)]
::
Expand Down Expand Up @@ -2720,11 +2819,12 @@ class IntegralAffinePlaneCurve_finite_field(AffinePlaneCurve_finite_field, Integ
EXAMPLES::
sage: A.<x,y> = AffineSpace(GF(8), 2) # needs sage.rings.finite_rings
sage: C = Curve(x^5 + y^5 + x*y + 1); C # needs sage.rings.finite_rings
sage: # needs sage.rings.finite_rings
sage: A.<x,y> = AffineSpace(GF(8), 2)
sage: C = Curve(x^5 + y^5 + x*y + 1); C
Affine Plane Curve over Finite Field in z3 of size 2^3
defined by x^5 + y^5 + x*y + 1
sage: C.function_field() # needs sage.rings.finite_rings
sage: C.function_field()
Function field in y defined by y^5 + x*y + x^5 + 1
"""
_point = IntegralAffinePlaneCurvePoint_finite_field
68 changes: 67 additions & 1 deletion src/sage/schemes/curves/projective_curve.py
Original file line number Diff line number Diff line change
Expand Up @@ -2325,7 +2325,13 @@ def __call__(self, *args):

def function(self, f):
"""
Return the function field element coerced from ``x``.
Return the function field element coerced from ``f``.
INPUT:
- ``f`` -- an element in the fraction field of the coordinate ring
OUTPUT: An element of the function field
EXAMPLES::
Expand Down Expand Up @@ -2373,6 +2379,39 @@ def coordinate_functions(self, i=None):
inv = ~coords[i]
return tuple([coords[j]*inv for j in range(len(coords)) if j != i])

def pull_from_function_field(self, f):
"""
Return the fraction corresponding to ``f``.
INPUT:
- ``f`` -- an element of the function field
OUTPUT:
A fraction of homogeneous polynomials in the coordinate ring of this curve.
EXAMPLES::
sage: # needs sage.rings.finite_rings
sage: P.<x,y,z> = ProjectiveSpace(GF(4), 2)
sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5)
sage: F = C.function_field()
sage: C.pull_from_function_field(F.gen())
z/x
sage: C.pull_from_function_field(F.one())
1
sage: C.pull_from_function_field(F.zero())
0
sage: f1 = F.gen()
sage: f2 = F.base_ring().gen()
sage: C.function(C.pull_from_function_field(f1)) == f1
True
sage: C.function(C.pull_from_function_field(f2)) == f2
True
"""
return self._pull_from_function_field(f)

@lazy_attribute
def _function_field(self):
"""
Expand Down Expand Up @@ -2425,6 +2464,33 @@ def _coordinate_functions(self):
coords.insert(self._open_affine_index, self._function_field.one())
return tuple(coords)

@lazy_attribute
def _pull_from_function_field(self):
"""
Return the map to the function field of the curve.
TESTS::
sage: P.<x,y,z> = ProjectiveSpace(GF(5), 2)
sage: C = Curve(y^2*z^7 - x^9 - x*z^8)
sage: F = C.function_field()
sage: f = F.random_element()
sage: C.function(C._pull_from_function_field(f)) == f
True
"""
F = self._function_field
S = self.ambient_space().coordinate_ring()
phi = self._open_affine._nonsingular_model[2]
i = self._open_affine_index

def pull(f):
pf = phi(f)
num = S(pf.numerator()).homogenize(i)
den = S(pf.denominator()).homogenize(i)
return num / den * S.gen(i) ** (den.total_degree() - num.total_degree())

return pull

@lazy_attribute
def _singularities(self):
"""
Expand Down

0 comments on commit fb0ccfd

Please sign in to comment.