Skip to content

Commit

Permalink
Trac #8389: Implement MatrixSpace(...)['x']
Browse files Browse the repository at this point in the history
...and rationalize the implementation of `__getitem__` for rings.

URL: http://trac.sagemath.org/8389
Reported by: mmezzarobba
Ticket author(s): Marc Mezzarobba
Reviewer(s): Nicolas M. Thiéry
  • Loading branch information
Release Manager authored and vbraun committed Mar 3, 2014
2 parents ccab985 + f4abe5d commit 8f5e7fd
Show file tree
Hide file tree
Showing 7 changed files with 300 additions and 247 deletions.
43 changes: 20 additions & 23 deletions src/doc/en/thematic_tutorials/coercion_and_categories.rst
Original file line number Diff line number Diff line change
Expand Up @@ -105,27 +105,24 @@ it makes sense to build on top of the base class
This base class provides a lot more methods than a general parent::

sage: [p for p in dir(Field) if p not in dir(Parent)]
['__div__', '__fraction_field', '__ideal_monoid', '__iter__',
'__pow__', '__rdiv__', '__rpow__', '__rxor__', '__xor__',
'_an_element', '_an_element_c', '_an_element_impl', '_coerce_',
'_coerce_c', '_coerce_impl', '_coerce_self', '_coerce_try',
'_default_category', '_gens', '_gens_dict',
'_has_coerce_map_from', '_ideal_class_', '_latex_names', '_list',
'_one_element', '_pseudo_fraction_field',
'_random_nonzero_element', '_richcmp', '_unit_ideal',
'_zero_element', '_zero_ideal', 'algebraic_closure',
'base_extend', 'cardinality', 'class_group', 'coerce_map_from_c',
'coerce_map_from_impl', 'content', 'divides', 'extension',
'fraction_field', 'frobenius_endomorphism', 'gcd', 'gen', 'gens',
'get_action_c', 'get_action_impl', 'has_coerce_map_from_c',
'has_coerce_map_from_impl', 'ideal', 'ideal_monoid',
'integral_closure', 'is_commutative', 'is_field', 'is_finite',
'is_integral_domain', 'is_integrally_closed', 'is_noetherian',
'is_prime_field', 'is_ring', 'is_subring', 'is_zero',
'krull_dimension', 'list', 'ngens', 'one', 'one_element',
'order', 'prime_subfield', 'principal_ideal', 'quo', 'quotient',
'quotient_ring', 'random_element', 'unit_ideal', 'zero',
'zero_element', 'zero_ideal', 'zeta', 'zeta_order']
['__div__', '__fraction_field', '__ideal_monoid', '__iter__', '__pow__',
'__rdiv__', '__rpow__', '__rxor__', '__xor__', '_an_element',
'_an_element_c', '_an_element_impl', '_coerce_', '_coerce_c',
'_coerce_impl', '_coerce_self', '_coerce_try', '_default_category', '_gens',
'_gens_dict', '_has_coerce_map_from', '_ideal_class_', '_latex_names',
'_list', '_one_element', '_pseudo_fraction_field',
'_random_nonzero_element', '_richcmp', '_unit_ideal', '_zero_element',
'_zero_ideal', 'algebraic_closure', 'base_extend', 'cardinality',
'class_group', 'coerce_map_from_c', 'coerce_map_from_impl', 'content',
'divides', 'extension', 'fraction_field', 'frobenius_endomorphism', 'gcd',
'gen', 'gens', 'get_action_c', 'get_action_impl', 'has_coerce_map_from_c',
'has_coerce_map_from_impl', 'ideal', 'ideal_monoid', 'integral_closure',
'is_commutative', 'is_field', 'is_finite', 'is_integral_domain',
'is_integrally_closed', 'is_noetherian', 'is_prime_field', 'is_ring',
'is_subring', 'krull_dimension', 'list', 'ngens', 'one', 'one_element',
'order', 'prime_subfield', 'principal_ideal', 'quo', 'quotient',
'quotient_ring', 'random_element', 'unit_ideal', 'zero', 'zero_element',
'zero_ideal', 'zeta', 'zeta_order']

The following is a very basic implementation of fraction fields, that needs to
be complemented later.
Expand Down Expand Up @@ -404,9 +401,9 @@ coincide::

sage: import inspect
sage: len([s for s in dir(MS1) if inspect.ismethod(getattr(MS1,s,None))])
55
56
sage: len([s for s in dir(MS2) if inspect.ismethod(getattr(MS2,s,None))])
78
80
sage: MS1.__class__ is MS2.__class__
True

Expand Down
226 changes: 226 additions & 0 deletions src/sage/categories/rings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# http://www.gnu.org/licenses/
#******************************************************************************

import re
from sage.categories.rngs import Rngs
from sage.categories.semirings import Semirings
from sage.categories.category import Category
Expand Down Expand Up @@ -66,6 +67,29 @@ def is_ring(self):
"""
return True

def is_zero(self):
"""
Return ``True`` if this is the zero ring.
EXAMPLES::
sage: Integers(1).is_zero()
True
sage: Integers(2).is_zero()
False
sage: QQ.is_zero()
False
sage: R.<x> = ZZ[]
sage: R.quo(1).is_zero()
True
sage: R.<x> = GF(101)[]
sage: R.quo(77).is_zero()
True
sage: R.quo(x^2+1).is_zero()
False
"""
return self.one_element() == self.zero_element()

def bracket(self, x, y):
"""
Returns the Lie bracket `[x, y] = x y - y x` of `x` and `y`.
Expand Down Expand Up @@ -615,6 +639,176 @@ def __div__(self, I):
"""
raise TypeError, "Use self.quo(I) or self.quotient(I) to construct the quotient ring."

def __getitem__(self, arg):
"""
Extend this ring by one or several elements to create a polynomial
ring, a power series ring, or an algebraic extension.
This is a convenience method intended primarily for interactive
use.
.. SEEALSO::
:func:`~sage.rings.polynomial.polynomial_ring_constructor.PolynomialRing`,
:func:`~sage.rings.power_series_ring.PowerSeriesRing`,
:meth:`~sage.rings.ring.Ring.extension`,
:meth:`sage.rings.integer_ring.IntegerRing_class.__getitem__`,
:meth:`sage.rings.matrix_space.MatrixSpace.__getitem__`,
:meth:`sage.structure.parent.Parent.__getitem__`
EXAMPLES:
We create several polynomial rings::
sage: ZZ['x']
Univariate Polynomial Ring in x over Integer Ring
sage: QQ['x']
Univariate Polynomial Ring in x over Rational Field
sage: GF(17)['abc']
Univariate Polynomial Ring in abc over Finite Field of size 17
sage: GF(17)['a,b,c']
Multivariate Polynomial Ring in a, b, c over Finite Field of size 17
sage: GF(17)['a']['b']
Univariate Polynomial Ring in b over Univariate Polynomial Ring in a over Finite Field of size 17
We can also create power series rings by using double brackets::
sage: QQ[['t']]
Power Series Ring in t over Rational Field
sage: ZZ[['W']]
Power Series Ring in W over Integer Ring
sage: ZZ[['x,y,z']]
Multivariate Power Series Ring in x, y, z over Integer Ring
sage: ZZ[['x','T']]
Multivariate Power Series Ring in x, T over Integer Ring
Use :func:`~sage.rings.fraction_field.Frac` or
:meth:`~sage.rings.ring.CommutativeRing.fraction_field` to obtain
the fields of rational functions and Laurent series::
sage: Frac(QQ['t'])
Fraction Field of Univariate Polynomial Ring in t over Rational Field
sage: Frac(QQ[['t']])
Laurent Series Ring in t over Rational Field
sage: QQ[['t']].fraction_field()
Laurent Series Ring in t over Rational Field
Note that the same syntax can be used to create number fields::
sage: QQ[I]
Number Field in I with defining polynomial x^2 + 1
sage: QQ[sqrt(2)]
Number Field in sqrt2 with defining polynomial x^2 - 2
sage: QQ[sqrt(2),sqrt(3)]
Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field
and orders in number fields::
sage: ZZ[I]
Order in Number Field in I with defining polynomial x^2 + 1
sage: ZZ[sqrt(5)]
Order in Number Field in sqrt5 with defining polynomial x^2 - 5
sage: ZZ[sqrt(2)+sqrt(3)]
Order in Number Field in a with defining polynomial x^4 - 10*x^2 + 1
TESTS:
A few corner cases::
sage: QQ[()]
Multivariate Polynomial Ring in no variables over Rational Field
sage: QQ[[]]
Traceback (most recent call last):
...
TypeError: power series rings must have at least one variable
Some flexibility is allowed when specifying variables::
sage: QQ["x", SR.var('y'), polygen(CC, 'z')]
Multivariate Polynomial Ring in x, y, z over Rational Field
sage: QQ[["x", SR.var('y'), polygen(CC, 'z')]]
Multivariate Power Series Ring in x, y, z over Rational Field
but more baroque expressions do not work::
sage: QQ['a,b','c']
Traceback (most recent call last):
...
ValueError: variable names must be alphanumeric, but one is 'a,b' which is not.
sage: QQ[['a,b','c']]
Traceback (most recent call last):
...
ValueError: variable names must be alphanumeric, but one is 'a,b' which is not.
sage: QQ[[['x']]]
Traceback (most recent call last):
...
TypeError: expected R[...] or R[[...]], not R[[[...]]]
Extension towers are built as follows and use distinct generator names::
sage: K = QQ[2^(1/3), 2^(1/2), 3^(1/3)]
sage: K
Number Field in a with defining polynomial x^3 - 2 over its base field
sage: K.base_field()
Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field
sage: K.base_field().base_field()
Number Field in b with defining polynomial x^3 - 3
"""
def normalize_arg(arg):
if isinstance(arg, (tuple, list)):
# Allowing arbitrary iterables would create confusion, but we
# may want to support a few more.
return tuple(arg)
elif isinstance(arg, str):
return tuple(arg.split(','))
else:
return (arg,)

# 1. If arg is a list, try to return a power series ring.

if isinstance(arg, list):
if arg == []:
raise TypeError("power series rings must have at least one variable")
elif len(arg) == 1:
# R[["a,b"]], R[[(a,b)]]...
if isinstance(arg[0], list):
raise TypeError("expected R[...] or R[[...]], not R[[[...]]]")
elts = normalize_arg(arg[0])
else:
elts = normalize_arg(arg)
from sage.rings.power_series_ring import PowerSeriesRing
return PowerSeriesRing(self, elts)

# 2. Otherwise, if all specified elements are algebraic, try to
# return an algebraic extension

elts = normalize_arg(arg)

try:
minpolys = [a.minpoly() for a in elts]
except (AttributeError, NotImplementedError, ValueError, TypeError):
minpolys = None

if minpolys:
# how to pass in names?
# TODO: set up embeddings
names = tuple(_gen_names(elts))
try:
# Doing the extension all at once is best, if possible...
return self.extension(minpolys, names)
except (TypeError, ValueError):
# ...but we can also construct it iteratively
return reduce(lambda R, ext: R.extension(*ext), zip(minpolys, names), self)

# 2. Otherwise, try to return a polynomial ring

from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
return PolynomialRing(self, elts)

class ElementMethods:
def is_unit(self):
r"""
Expand Down Expand Up @@ -648,3 +842,35 @@ def is_unit(self):

class HomCategory(HomCategory):
pass

from sage.structure.parent_gens import _certify_names

def _gen_names(elts):
r"""
Used to find a name for a generator when rings are created using the
``__getitem__`` syntax, e.g. ``ZZ['x']``, ``ZZ[sqrt(2)]``.
EXAMPLES::
sage: from sage.categories.rings import _gen_names
sage: list(_gen_names([sqrt(5)]))
['sqrt5']
sage: list(_gen_names([sqrt(-17), 2^(1/3)]))
['a', 'b']
sage: list(_gen_names((1..27)))[-1]
'aa'
"""
from sage.symbolic.ring import is_SymbolicVariable
from sage.combinat.words.words import Words
it = iter(Words("abcdefghijklmnopqrstuvwxyz"))
it.next() # skip empty word
for x in elts:
name = str(x)
m = re.match('^sqrt\((\d+)\)$', name)
if m:
name = "sqrt%s" % m.groups()[0]
try:
_certify_names([name])
except ValueError, msg:
name = it.next().string_rep()
yield name
41 changes: 41 additions & 0 deletions src/sage/matrix/matrix_space.py
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,47 @@ def __iter__(self):
for iv in sage.combinat.integer_vector.IntegerVectors(weight, number_of_entries, max_part=(order-1)):
yield self(entries=[base_elements[i] for i in iv])

def __getitem__(self, x):
"""
Return a polynomial ring over this ring or the `n`-th element of this ring.
This method implements the syntax ``R['x']`` to define polynomial rings
over matrix rings, while still allowing to get the `n`-th element of a
finite matrix ring with ``R[n]`` for backward compatibility.
(If this behaviour proves desirable for all finite enumerated rings, it
should eventually be implemented in the corresponding category rather
than here.)
..SEEALSO::
:meth:`sage.categories.rings.Rings.ParentMethod.__getitem__`,
:meth:`sage.structure.parent.Parent.__getitem__`
EXAMPLES::
sage: MS = MatrixSpace(GF(3), 2, 2)
sage: MS['x']
Univariate Polynomial Ring in x over Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 3
sage: MS[0]
[0 0]
[0 0]
sage: MS[9]
[0 2]
[0 0]
sage: MS = MatrixSpace(QQ, 7)
sage: MS['x']
Univariate Polynomial Ring in x over Full MatrixSpace of 7 by 7 dense matrices over Rational Field
sage: MS[2]
Traceback (most recent call last):
...
ValueError: since it is infinite, cannot list Full MatrixSpace of 7 by 7 dense matrices over Rational Field
"""
if isinstance(x, (int, long, integer.Integer)):
return self.list()[x]
return Rings.ParentMethods.__getitem__.__func__(self, x)

def _get_matrix_class(self):
r"""
Returns the class of self
Expand Down
11 changes: 2 additions & 9 deletions src/sage/rings/integer_ring.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -435,8 +435,8 @@ cdef class IntegerRing_class(PrincipalIdealDomain):

def __getitem__(self, x):
"""
Return the ring `\ZZ[...]` obtained by adjoining to the integers a list
``x`` of several elements.
Return the ring `\ZZ[...]` obtained by adjoining to the integers one
or several elements.
EXAMPLES::
Expand All @@ -457,15 +457,8 @@ cdef class IntegerRing_class(PrincipalIdealDomain):
"""
if x in self:
return self
if isinstance(x, str):
return PrincipalIdealDomain.__getitem__(self, x)
from sage.symbolic.ring import is_SymbolicVariable

if is_SymbolicVariable(x):
return PrincipalIdealDomain.__getitem__(self, repr(x))

from sage.rings.number_field.all import is_NumberFieldElement

if is_NumberFieldElement(x):
K, from_K = x.parent().subfield(x)
return K.order(K.gen())
Expand Down
2 changes: 1 addition & 1 deletion src/sage/rings/polynomial/polynomial_ring_constructor.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def PolynomialRing(base_ring, arg1=None, arg2=None,
INPUT:
- ``base_ring`` -- a commutative ring
- ``base_ring`` -- a ring
- ``name`` -- a string
- ``names`` -- a list or tuple of names, or a comma separated string
- ``var_array`` -- a list or tuple of names, or a comma separated string
Expand Down

0 comments on commit 8f5e7fd

Please sign in to comment.