Skip to content
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2368519
completion of polynomial rings and their fraction fields
Aug 19, 2025
573dae0
file morphism.py
Aug 19, 2025
ce72980
fix failing doctests
Aug 20, 2025
d990b8f
fix doctest
Aug 20, 2025
eff3e22
avoid recursive loop in coercion
Aug 20, 2025
2f06fbc
address easy remarks by user202729
Aug 20, 2025
e6a8942
typo
Aug 20, 2025
5f008e6
remove class MorphismToCompletion
Aug 20, 2025
7468d4a
typo
Aug 20, 2025
779cb91
documentation of the function morphism_to_completion
Aug 20, 2025
aa9e296
back to the initial implementation of _first_ngens
Aug 24, 2025
5388244
gives generator to completion functor
Aug 24, 2025
a5d3d7d
a specific class for completions
Aug 25, 2025
6791145
implementation using RingExtension
Aug 26, 2025
dede4c3
fix default precision
Aug 26, 2025
e31740c
use quotient instead of extension
Aug 27, 2025
d0bf69c
set up correctly the generator
Aug 27, 2025
1177266
Merge branch 'develop' of https://github.com/sagemath/sage into compl…
Aug 27, 2025
b2ee24f
completion of rational function field
Aug 27, 2025
4dea431
infinite place
Aug 28, 2025
0f8b78d
bigoh
Aug 28, 2025
c84ffe6
implement quasi-linear algorithm for expansion (and printing)
Aug 28, 2025
ee59022
doctests
Aug 29, 2025
d816dc5
completion -> completion_polynomial_ring
Aug 30, 2025
973dffa
oops
Aug 30, 2025
065f97f
address some comments by Travis
Sep 3, 2025
9a276cc
more comments
Sep 3, 2025
94d109f
Merge branch 'develop' into completion
Sep 11, 2025
4f03b1d
Merge branch 'develop' into completion
xcaruso Oct 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions src/sage/rings/fraction_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -1206,6 +1206,97 @@ def _coerce_map_from_(self, R):

return super()._coerce_map_from_(R)

def completion(self, p=None, prec=20, name=None, residue_name=None, names=None):
r"""
Return the completion of this rational functions ring with
respect to the irreducible polynomial `p`.

INPUT:

- ``p`` (default: ``None``) -- an irreduclible polynomial or
``Infiniity``; if ``None``, the generator of this polynomial
ring

- ``prec`` (default: 20) -- an integer or ``Infinity``; if
``Infinity``, return a
:class:`sage.rings.lazy_series_ring.LazyPowerSeriesRing`.

- ``name`` (default: ``None``) -- a string, the variable name;
if ``None`` and the completion is at `0`, the name of the
variable is this polynomial ring is reused

- ``residue_name`` (default: ``None``) -- a string, the variable
name for the residue field (only relevant for places of degree
at least `2`)

- ``names`` (default: ``None``) -- a tuple of strings with the
previous variable names

EXAMPLES::

sage: A.<x> = PolynomialRing(QQ)
sage: K = A.fraction_field()

Without any argument, this method constructs the completion at
the ideal `x`::

sage: Kx = K.completion()
sage: Kx
Laurent Series Ring in x over Rational Field

We can construct the completion at other ideals by passing in an
irreducible polynomial. In this case, we should also provide a name
for the uniformizer (set to be `x - a` where `a` is a root of the
given polynomial)::

sage: K1.<u> = K.completion(x - 1)
sage: K1
Laurent Series Ring in u over Rational Field
sage: x - u
1

::

sage: K2.<v, a> = K.completion(x^2 + x + 1)
sage: K2
Laurent Series Ring in v over Number Field in a with defining polynomial x^2 + x + 1
sage: a^2 + a + 1
0
sage: x - v
a

When the precision is infinity, a lazy series ring is returned::

sage: # needs sage.combinat
sage: L = K.completion(x, prec=oo); L
Lazy Laurent Series Ring in x over Rational Field
"""
from sage.rings.polynomial.morphism import MorphismToCompletion
if names is not None:
if name is not None or residue_name is not None:
raise ValueError("")
if not isinstance(names, (list, tuple)):
raise TypeError("names must a a list or a tuple")
name = names[0]
if len(names) > 1:
residue_name = names[1]
x = self.gen()
if p is None:
p = x
if p == x and name is None:
name = self.variable_name()
incl = MorphismToCompletion(self, p, prec, name, residue_name)
C = incl.codomain()
if C.has_coerce_map_from(self):
if C(x) != incl(x):
raise ValueError("a different coercion map is already set; try to change the variable name")
else:
C.register_coercion(incl)
ring = self.ring()
if not C.has_coerce_map_from(ring):
C.register_coercion(incl * self.coerce_map_from(ring))
return C


class FractionFieldEmbedding(DefaultConvertMap_unique):
r"""
Expand Down
6 changes: 4 additions & 2 deletions src/sage/rings/laurent_series_ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,12 +221,14 @@ def __classcall__(cls, *args, **kwds):
'q'
"""
from .power_series_ring import PowerSeriesRing

if 'default_prec' in kwds and kwds['default_prec'] is infinity:
from sage.rings.lazy_series_ring import LazyLaurentSeriesRing
del kwds['default_prec']
return LazyLaurentSeriesRing(*args, **kwds)
if not kwds and len(args) == 1 and isinstance(args[0], (PowerSeriesRing_generic, LazyPowerSeriesRing)):
power_series = args[0]
else:
power_series = PowerSeriesRing(*args, **kwds)

return UniqueRepresentation.__classcall__(cls, power_series)

def __init__(self, power_series):
Expand Down
1 change: 1 addition & 0 deletions src/sage/rings/polynomial/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ py.install_sources(
'laurent_polynomial_mpair.pxd',
'laurent_polynomial_ring.py',
'laurent_polynomial_ring_base.py',
'morphism.py',
'msolve.py',
'multi_polynomial.pxd',
'multi_polynomial_element.py',
Expand Down
158 changes: 158 additions & 0 deletions src/sage/rings/polynomial/morphism.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
r"""
Morphisms attached to polynomial rings.
"""

# *****************************************************************************
# Copyright (C) 2025 Xavier Caruso <[email protected]>
#
# 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.
# http://www.gnu.org/licenses/
# *****************************************************************************

from sage.structure.category_object import normalize_names
from sage.categories.rings import Rings

from sage.rings.morphism import RingHomomorphism
from sage.rings.integer_ring import ZZ
from sage.rings.infinity import Infinity
from sage.rings.power_series_ring import PowerSeriesRing
from sage.rings.laurent_series_ring import LaurentSeriesRing


class MorphismToCompletion(RingHomomorphism):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feels oddly specific? Can sage/rings/polynomial/polynomial_ring_homomorphism.pyx be reused?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're probably right. I will try to refactor things differently.

r"""
Morphisms from a polynomial ring (or its fraction field)
to the completion at one place.
TESTS::
sage: A.<t> = GF(5)[]
sage: B.<u> = A.completion(t+1)
sage: f = B.coerce_map_from(A)
sage: type(f)
<class 'sage.rings.polynomial.morphism.MorphismToCompletion'>
sage: TestSuite(f).run(skip='_test_category')
"""
def __init__(self, domain, place, prec, name, residue_name):
r"""
Initialize this morphism.
INPUT:
- ``domain`` -- a polynomial ring of its fraction field
- ``place`` -- an irreducible polynomial or ``Infinity``
- ``prec`` -- an integer or ``Infinity``
- ``name`` -- a string, the variable name of the uniformizer
- ``residue_name`` -- a string, the variable name of the
generator of the residue ring
TESTS::
sage: A.<t> = ZZ[]
sage: B.<u> = A.completion(2*t + 1)
Traceback (most recent call last):
...
NotImplementedError: the leading coefficient of the place is not a unit
::
sage: A.<t> = QQ[]
sage: B.<u,a> = A.completion(x^2 + 2*x + 1)
Traceback (most recent call last):
...
ValueError: place must be Infinity or an irreducible polynomial
"""
if domain.is_field():
ring = domain.ring()
SeriesRing = LaurentSeriesRing
else:
ring = domain
SeriesRing = PowerSeriesRing
k = base = ring.base_ring()
x = ring.gen()
sparse = ring.is_sparse()
if place is Infinity:
pass
elif place in ring:
place = ring(place)
if place.leading_coefficient().is_unit():
place = ring(place.monic())
# We do not check irreducibility; it causes too much troubles:
# it can be long, be not implemented and even sometimes fail
else:
raise NotImplementedError("the leading coefficient of the place is not a unit")
else:
raise ValueError("place must be Infinity or an irreducible polynomial")
self._place = place
if name is None:
raise ValueError("you must specify a variable name")
name = normalize_names(1, name)
if place is Infinity:
codomain = LaurentSeriesRing(base, names=name, default_prec=prec, sparse=sparse)
image = codomain.one() >> 1
elif place == ring.gen() or place.degree() == 1: # sometimes ring.gen() has not degree 1
codomain = SeriesRing(base, names=name, default_prec=prec, sparse=sparse)
image = codomain.gen() - place[0]
else:
if residue_name is None:
raise ValueError("you must specify a variable name for the residue field")
residue_name = normalize_names(1, residue_name)
k = base.extension(place, names=residue_name)
codomain = SeriesRing(k, names=name, default_prec=prec, sparse=sparse)
image = codomain.gen() + k.gen()
parent = domain.Hom(codomain, category=Rings())
RingHomomorphism.__init__(self, parent)
self._image = image
self._k = k

def _repr_type(self):
r"""
Return a string that describes the type of this morphism.
EXAMPLES::
sage: A.<x> = QQ[]
sage: B.<u> = A.completion(x + 1)
sage: f = B.coerce_map_from(A)
sage: f # indirect doctest
Completion morphism:
From: Univariate Polynomial Ring in x over Rational Field
To: Power Series Ring in u over Rational Field
"""
return "Completion"

def place(self):
r"""
Return the place at which we completed.
EXAMPLES::
sage: A.<x> = QQ[]
sage: B.<u> = A.completion(x + 1)
sage: f = B.coerce_map_from(A)
sage: f.place()
x + 1
"""
return self._place

def _call_(self, P):
r"""
Return the image of ``P`` under this morphism.
EXAMPLES::
sage: A.<x> = QQ[]
sage: B.<u> = A.completion(x + 1)
sage: f = B.coerce_map_from(A)
sage: f(x) # indirect doctest
-1 + u
"""
return P(self._image)
Loading
Loading