Skip to content
Draft
Show file tree
Hide file tree
Changes from 7 commits
Commits
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::
Comment on lines +1225 to +1227
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
previous variable names
EXAMPLES::
previous variable names
.. SEEALSO::
:mod:`sage.rings.completion_polynomial_ring`
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:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if 'default_prec' in kwds and kwds['default_prec'] is infinity:
if kwds.get('default_prec', None) is infinity:

Personal style choice here.

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