Path: blob/master/src/sage/structure/category_object.pyx
8814 views
r"""1Base class for objects of a category23CLASS HIERARCHY:45- :class:`~sage.structure.sage_object.SageObject`67- **CategoryObject**89- :class:`~sage.structure.parent.Parent`1011Many category objects in Sage are equipped with generators, which are12usually special elements of the object. For example, the polynomial ring13`\ZZ[x,y,z]` is generated by `x`, `y`, and `z`. In Sage the ``i`` th14generator of an object ``X`` is obtained using the notation15``X.gen(i)``. From the Sage interactive prompt, the shorthand16notation ``X.i`` is also allowed.1718The following examples illustrate these functions in the context of19multivariate polynomial rings and free modules.2021EXAMPLES::2223sage: R = PolynomialRing(ZZ, 3, 'x')24sage: R.ngens()25326sage: R.gen(0)27x028sage: R.gens()29(x0, x1, x2)30sage: R.variable_names()31('x0', 'x1', 'x2')3233This example illustrates generators for a free module over `\ZZ`.3435::3637sage: M = FreeModule(ZZ, 4)38sage: M39Ambient free module of rank 4 over the principal ideal domain Integer Ring40sage: M.ngens()41442sage: M.gen(0)43(1, 0, 0, 0)44sage: M.gens()45((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1))46"""4748import generators49import sage_object50from sage.categories.category import Category, JoinCategory51from sage.structure.debug_options import debug5253def guess_category(obj):54# this should be obsolete if things declare their categories55try:56if obj.is_field():57from sage.categories.all import Fields58return Fields()59except (AttributeError, NotImplementedError):60pass61try:62if obj.is_ring():63from sage.categories.all import CommutativeAlgebras, Algebras, CommutativeRings, Rings64if obj.is_commutative():65if obj._base is not obj:66return CommutativeAlgebras(obj._base)67else:68return CommutativeRings()69else:70if obj._base is not obj:71return Algebras(obj._base)72else:73return Rings()74except Exception:75pass76from sage.structure.parent import Parent77#if isinstance(obj, Parent):78# import sys79# sys.stderr.write("bla: %s"%obj)80# from sage.categories.all import Sets81# return Sets()82return None # don't want to risk importing stuff...8384cpdef inline check_default_category(default_category, category):85"""8687"""88## The resulting category is guaranteed to be89## a sub-category of the default.90if category is None:91return default_category92return default_category.join([default_category,category])9394cdef class CategoryObject(sage_object.SageObject):95"""96An object in some category.97"""98def __init__(self, category = None, base = None):99"""100Initializes an object in a category101102INPUT:103104- ``category`` - The category this object belongs to. If this object105belongs to multiple categories, those can be passed as a tuple106- ``base`` - If this object has another object that should be107considered a base in its primary category, you can include that base108here.109110EXAMPLES::111112sage: from sage.structure.category_object import CategoryObject113sage: A = CategoryObject()114sage: A.category()115Category of objects116sage: A.base()117118sage: A = CategoryObject(category = Rings(), base = QQ)119sage: A.category()120Category of rings121sage: A.base()122Rational Field123124sage: A = CategoryObject(category = (Semigroups(), CommutativeAdditiveSemigroups()))125sage: A.category()126Join of Category of semigroups and Category of commutative additive semigroups127128FIXME: the base and generators attributes have nothing to do with categories, do they?129"""130if base is not None:131self._base = base132self._generators = {}133if category is not None:134self._init_category_(category)135136def __cinit__(self):137self._hash_value = -1138139def _init_category_(self, category):140"""141Sets the category or categories of this object.142143EXAMPLES::144145sage: A = sage.structure.category_object.CategoryObject()146sage: A._init_category_(Rings())147sage: A.category()148Category of rings149sage: A._init_category_((Semigroups(), CommutativeAdditiveSemigroups()))150sage: A.category()151Join of Category of semigroups and Category of commutative additive semigroups152"""153if category is None:154if debug.bad_parent_warnings:155print "No category for %s" % type(self)156category = guess_category(self) # so generators don't crash157elif (type(category) == tuple or type(category) == list):158assert(len(category)>0) # or we should decide of the semantic for an empty category159if len(category) == 1:160category = category[0]161else:162category = JoinCategory(category)163self._category = category164165def _refine_category_(self, category):166"""167Changes the category of ``self`` into a subcategory.168169INPUT:170171- ``category`` -- a category or list or tuple thereof172173The new category is obtained by adjoining ``category`` to the174current one.175176.. seealso:: :function:`Category.join`177178EXAMPLES::179180sage: P = Parent()181sage: P.category()182Category of sets183sage: P._refine_category_(Magmas())184sage: P.category()185Category of magmas186sage: P._refine_category_(Magmas())187sage: P.category()188Category of magmas189sage: P._refine_category_(EnumeratedSets())190sage: P.category()191Join of Category of magmas and Category of enumerated sets192sage: P._refine_category_([Semigroups(), CommutativeAdditiveSemigroups()])193sage: P.category()194Join of Category of semigroups and Category of commutative additive semigroups and Category of enumerated sets195sage: P._refine_category_((CommutativeAdditiveMonoids(), Monoids()))196sage: P.category()197Join of Category of monoids and Category of commutative additive monoids and Category of enumerated sets198"""199if self._category is None:200self._init_category_(category)201return202if not (type(category) == tuple or type(category) == list):203category = [category]204self._category = self._category.join([self._category]+list(category))205206def _is_category_initialized(self):207return self._category is not None208209def category(self):210if self._category is None:211# COERCE TODO: we shouldn't need this212from sage.categories.objects import Objects213self._category = Objects()214return self._category215216def categories(self):217"""218EXAMPLES::219220sage: ZZ.categories()221[Category of euclidean domains,222Category of principal ideal domains,223Category of unique factorization domains,224Category of gcd domains,225Category of integral domains,226Category of domains,227Category of commutative rings,228Category of rings,229Category of rngs,230Category of semirings,231Category of monoids,232Category of semigroups,233Category of magmas,234Category of commutative additive groups,235Category of commutative additive monoids,236Category of commutative additive semigroups,237Category of additive magmas,238Category of sets,239Category of sets with partial maps,240Category of objects]241"""242return self.category().all_super_categories()243244##############################################################################245# Generators246##############################################################################247248def _populate_generators_(self, gens=None, names=None, normalize = True, category=None):249if self._generators.has_key(category):250raise ValueError, "Generators cannot be changed after object creation."251if category is None:252category = self._category253from sage.structure.sequence import Sequence254if gens is None:255n = self._ngens_()256from sage.rings.infinity import infinity257if n is infinity:258gens = generators.Generators_naturals(self, category)259else:260gens = generators.Generators_finite(self, self._ngens_(), None, category)261elif isinstance(gens, Generators):262pass263elif isinstance(gens, (list, tuple, Sequence)):264if names is None:265names = tuple([str(x) for x in gens])266gens = generators.Generators_list(self, list(gens), category)267else:268gens = generators.Generators_list(self, [gens], category)269self._generators[category] = gens270if category == self._category:271if names is not None and self._names is None:272self._assign_names(names, ngens=gens.count(), normalize=normalize)273self._generators[category] = gens274275# cpdef Generators gens(self, category=None):276# if category is None:277# category = self._categories[0]278# try:279# return self._generators[category]280# except KeyError:281# if category == self._categories[0]:282# n = self._ngens_()283# from sage.rings.infinity import infinity284# if n is infinity:285# gens = generators.Generators_naturals(self, category)286# else:287# gens = generators.Generators_finite(self, self._ngens_(), None, category)288# else:289# gens = self._compute_generators_(category)290# self._generators[category] = gens291# return gens292#293# cpdef gen(self, index=0, category=None):294# return self.gens(category)[index]295#296# cpdef ngens(self, category=None):297# return self.gens(category).count()298299def _ngens_(self):300return 0301302def gens_dict(self):303r"""304Return a dictionary whose entries are ``{var_name:variable,...}``.305"""306if HAS_DICTIONARY(self):307try:308if self._gens_dict is not None:309return self._gens_dict310except AttributeError:311pass312v = {}313for x in self.gens():314v[str(x)] = x315if HAS_DICTIONARY(self):316self._gens_dict = v317return v318319def gens_dict_recursive(self):320r"""321Return the dictionary of generators of ``self`` and its base rings.322323OUTPUT:324325- a dictionary with string names of generators as keys and generators of326``self`` and its base rings as values.327328EXAMPLES::329330sage: R = QQ['x,y']['z,w']331sage: sorted(R.gens_dict_recursive().items())332[('w', w), ('x', x), ('y', y), ('z', z)]333"""334B = self.base_ring()335if B is self:336return {}337GDR = B.gens_dict_recursive()338GDR.update(self.gens_dict())339return GDR340341def objgens(self):342"""343Return the tuple ``(self, self.gens())``.344345EXAMPLES::346347sage: R = PolynomialRing(QQ, 3, 'x'); R348Multivariate Polynomial Ring in x0, x1, x2 over Rational Field349sage: R.objgens()350(Multivariate Polynomial Ring in x0, x1, x2 over Rational Field, (x0, x1, x2))351"""352return self, self.gens()353354def objgen(self):355"""356Return the tuple ``(self, self.gen())``.357358EXAMPLES::359360sage: R, x = PolynomialRing(QQ,'x').objgen()361sage: R362Univariate Polynomial Ring in x over Rational Field363sage: x364x365"""366return self, self.gen()367368def _first_ngens(self, n):369"""370Used by the preparser for R.<x> = ...371"""372return self.gens()[:n]373374#################################################################################################375# Names and Printers376#################################################################################################377378def _assign_names(self, names=None, normalize=True, ngens=None):379"""380Set the names of the generator of this object.381382This can only be done once because objects with generators383are immutable, and is typically done during creation of the object.384385386EXAMPLES:387When we create this polynomial ring, self._assign_names is called by the constructor::388389sage: R = QQ['x,y,abc']; R390Multivariate Polynomial Ring in x, y, abc over Rational Field391sage: R.2392abc393394We can't rename the variables::395396sage: R._assign_names(['a','b','c'])397Traceback (most recent call last):398...399ValueError: variable names cannot be changed after object creation.400"""401# this will eventually all be handled by the printer402if names is None: return403if normalize:404if ngens is None:405if self._generators is None or len(self._generators) == 0:406# not defined yet407if isinstance(names, (tuple, list)) and names is not None:408ngens = len(names)409else:410ngens = 1411else:412ngens = self.ngens()413names = self.normalize_names(ngens, names)414if self._names is not None and names != self._names:415raise ValueError, 'variable names cannot be changed after object creation.'416if PY_TYPE_CHECK(names, str):417names = (names, ) # make it a tuple418elif PY_TYPE_CHECK(names, list):419names = tuple(names)420elif not PY_TYPE_CHECK(names, tuple):421raise TypeError, "names must be a tuple of strings"422self._names = names423424def normalize_names(self, int ngens, names=None):425if names is None:426return None427if ngens == 0:428return ()429if isinstance(names, str) and names.find(',') != -1:430names = names.split(',')431if isinstance(names, str) and ngens > 1 and len(names) == ngens:432names = tuple(names)433if isinstance(names, str):434name = names435import sage.misc.defaults436names = sage.misc.defaults.variable_names(ngens, name)437names = self._certify_names(names)438else:439names = self._certify_names(names)440if not isinstance(names, (list, tuple)):441raise TypeError, "names must be a list or tuple of strings"442for x in names:443if not isinstance(x,str):444raise TypeError, "names must consist of strings"445if len(names) != ngens:446raise IndexError, "the number of names must equal the number of generators"447return names448449def _certify_names(self, names):450v = []451try:452names = tuple(names)453except TypeError:454names = [str(names)]455for N in names:456if not isinstance(N, str):457N = str(N)458N = N.strip().strip("'")459if len(N) == 0:460raise ValueError, "variable name must be nonempty"461if not N.isalnum() and not N.replace("_","").isalnum():462# We must be alphanumeric, but we make an exception for non-leading '_' characters.463raise ValueError, "variable names must be alphanumeric, but one is '%s' which is not."%N464if not N[0].isalpha():465raise ValueError, "first letter of variable name must be a letter: %s" % N466v.append(N)467return tuple(v)468469def variable_names(self):470if self._names != None:471return self._names472raise ValueError, "variable names have not yet been set using self._assign_names(...)"473474def variable_name(self):475return self.variable_names()[0]476477def __temporarily_change_names(self, names, latex_names):478"""479This is used by the variable names context manager.480481TEST:482483In an old version, it was impossible to temporarily change484the names if no names were previously assigned. But if one485wants to print elements of the quotient of such an "unnamed"486ring, an error resulted. That was fixed in trac ticket487#11068.488::489490sage: MS = MatrixSpace(GF(5),2,2)491sage: I = MS*[MS.0*MS.1,MS.2+MS.3]*MS492sage: Q.<a,b,c,d> = MS.quo(I)493sage: a #indirect doctest494[1 0]495[0 0]496497"""498#old = self._names, self._latex_names499# We can not assume that self *has* _latex_variable_names.500# But there is a method that returns them and sets501# the attribute at the same time, if needed.502# Simon King: It is not necessarily the case that variable503# names are assigned. In that case, self._names is None,504# and self.variable_names() raises a ValueError505try:506old = self.variable_names(), self.latex_variable_names()507except ValueError:508old = None, None509self._names, self._latex_names = names, latex_names510return old511512def inject_variables(self, scope=None, verbose=True):513"""514Inject the generators of self with their names into the515namespace of the Python code from which this function is516called. Thus, e.g., if the generators of self are labeled517'a', 'b', and 'c', then after calling this method the518variables a, b, and c in the current scope will be set519equal to the generators of self.520521NOTE: If Foo is a constructor for a Sage object with generators, and522Foo is defined in Cython, then it would typically call523``inject_variables()`` on the object it creates. E.g.,524``PolynomialRing(QQ, 'y')`` does this so that the variable y is the525generator of the polynomial ring.526"""527vs = self.variable_names()528gs = self.gens()529if scope is None:530scope = globals()531if verbose:532print "Defining %s"%(', '.join(vs))533for v, g in zip(vs, gs):534scope[v] = g535536def injvar(self, scope=None, verbose=True):537"""538This is a deprecated synonym for :meth:`.inject_variables`.539"""540from sage.misc.superseded import deprecation541deprecation(4143, 'injvar is deprecated; use inject_variables instead.')542return self.inject_variables(scope=scope, verbose=verbose)543544#################################################################################################545# Bases546#################################################################################################547548# cpdef base(self, category=None):549# if category is None:550# return self._base551# else:552# return category._obj_base(self)553554def has_base(self, category=None):555if category is None:556return self._base is not None557else:558return category._obj_base(self) is not None559560# cpdef base_extend(self, other, category=None):561# """562# EXAMPLES:563# sage: QQ.base_extend(GF(7))564# Traceback (most recent call last):565# ...566# TypeError: base extension not defined for Rational Field567# sage: ZZ.base_extend(GF(7))568# Finite Field of size 7569# """570# try:571# if category is None:572# method = self._category.get_object_method("base_extend") # , self._categories[1:])573# else:574# method = category.get_object_method("base_extend")575# return method(self)576# except AttributeError:577# raise TypeError, "base extension not defined for %s" % self578579# COERCE TODO: When everything has a category, move let it be an optional arg.580def base_ring(self): # This should be in a category or elsewhere, but not here581return self._base582583def base(self):584return self._base585586#################################################################################################587# Automatic lookup of methods on the category.588#################################################################################################589590# def __getattr__(self, name):591# """592# Overriding the __getattr__ method allows one to define methods for objects in a particular593# category by writing a corresponding method on the category.594#595# In order to write a method called FOO that's automatically attached to a category object,596# write a method object_FOO on one of that object's categories.597#598# EXAMPLES:599# sage: G = DirichletGroup(18); G600# Group of Dirichlet characters of modulus 18 over Cyclotomic Field of order 6 and degree 2601# sage: G.generator_orders()602# [1, 6]603# sage: G.category().object_generator_orders(G)604# [1, 6]605# """606# if self._category is not None:607# attr = self._category.get_object_method(name)608# if attr is not None:609# if callable(attr):610# return FillFirstArg(attr, self)611# else:612# return attr613# return object.__getattribute__(self, name)614615############################################################################616# Homomorphism --617############################################################################618def Hom(self, codomain, cat=None):619r"""620Return the homspace ``Hom(self, codomain, cat)`` of all621homomorphisms from self to codomain in the category cat. The622default category is determined by ``self.category()`` and623``codomain.category()``.624625EXAMPLES::626627sage: R.<x,y> = PolynomialRing(QQ, 2)628sage: R.Hom(QQ)629Set of Homomorphisms from Multivariate Polynomial Ring in x, y over Rational Field to Rational Field630631Homspaces are defined for very general Sage objects, even elements of familiar rings.632633::634635sage: n = 5; Hom(n,7)636Set of Morphisms from 5 to 7 in Category of elements of Integer Ring637sage: z=(2/3); Hom(z,8/1)638Set of Morphisms from 2/3 to 8 in Category of elements of Rational Field639640This example illustrates the optional third argument::641642sage: QQ.Hom(ZZ, Sets())643Set of Morphisms from Rational Field to Integer Ring in Category of sets644"""645try:646return self._Hom_(codomain, cat)647except (AttributeError, TypeError):648pass649from sage.categories.all import Hom650return Hom(self, codomain, cat)651652def latex_variable_names(self):653"""654Returns the list of variable names suitable for latex output.655656All ``_SOMETHING`` substrings are replaced by ``_{SOMETHING}``657recursively so that subscripts of subscripts work.658659EXAMPLES::660661sage: R, x = PolynomialRing(QQ,'x',12).objgens()662sage: x663(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11)664sage: print R.latex_variable_names ()665['x_{0}', 'x_{1}', 'x_{2}', 'x_{3}', 'x_{4}', 'x_{5}', 'x_{6}', 'x_{7}', 'x_{8}', 'x_{9}', 'x_{10}', 'x_{11}']666sage: f = x[0]^3 + 15/3 * x[1]^10667sage: print latex(f)6685 x_{1}^{10} + x_{0}^{3}669"""670from sage.misc.latex import latex, latex_variable_name671try:672names = self._latex_names673if names is not None:674return names675except AttributeError:676pass677# Compute the latex versions of the variable names.678self._latex_names = [latex_variable_name(x) for x in self.variable_names()]679return self._latex_names680681def latex_name(self):682return self.latex_variable_names()[0]683684def _temporarily_change_names(self, names):685self._names = names686687#################################################################################688# Give all objects with generators a dictionary, so that attribute setting689# works. It would be nice if this functionality were standard in Cython,690# i.e., just define __dict__ as an attribute and all this code gets generated.691#################################################################################692def __getstate__(self):693d = []694try:695d = list(self.__dict__.copy().iteritems()) # so we can add elements696except AttributeError:697pass698d = dict(d)699d['_generators'] = self._generators700d['_category'] = self._category701d['_base'] = self._base702d['_cdata'] = self._cdata703d['_names'] = self._names704###########705# The _pickle_version ensures that the unpickling for objects created706# in different versions of sage works across versions.707# Update this integer if you change any of these attributes708###########709d['_pickle_version'] = 1710try:711d['_generator_orders'] = self._generator_orders712except AttributeError:713pass714715return d716717def __setstate__(self,d):718try:719version = d['_pickle_version']720except KeyError:721version = 0722try:723if version == 1:724self._generators = d['_generators']725if d['_category'] is not None:726# We must not erase the category information of727# self. Otherwise, pickles break (e.g., QQ should728# be a commutative ring, but when QQ._category is729# None then it only knows that it is a ring!730if self._category is None:731self._category = d['_category']732else:733self._category = self._category.join([self._category,d['_category']])734self._base = d['_base']735self._cdata = d['_cdata']736self._names = d['_names']737try:738self._generator_orders = d['_generator_orders']739except (AttributeError, KeyError):740pass741elif version == 0:742# In the old code, this functionality was in parent_gens,743# but there were parents that didn't inherit from parent_gens.744# If we have such, then we only need to deal with the dictionary.745try:746self._base = d['_base']747self._names = d['_names']748from sage.categories.all import Objects749if d['_gens'] is None:750from sage.structure.generators import Generators751self._generators = Generators(self, None, Objects())752else:753from sage.structure.generators import Generator_list754self._generators = Generator_list(self, d['_gens'], Objects())755self._generator_orders = d['_generator_orders'] # this may raise a KeyError, but that's okay.756# We throw away d['_latex_names'] and d['_list'] and d['_gens_dict']757except (AttributeError, KeyError):758pass759try:760self.__dict__ = d761except AttributeError:762pass763except (AttributeError, KeyError):764raise765#raise RuntimeError, "If you change the pickling code in parent or category_object, you need to update the _pickle_version field"766767def __hash__(self):768"""769A default hash is provide based on the string representation of the770self. It is cached to remain consistent throughout a session, even771if the representation changes.772773EXAMPLES::774775sage: bla = PolynomialRing(ZZ,"x")776sage: hash(bla)777-5279516879544852222 # 64-bit778-1056120574 # 32-bit779sage: bla.rename("toto")780sage: hash(bla)781-5279516879544852222 # 64-bit782-1056120574 # 32-bit783"""784if self._hash_value == -1:785self._hash_value = hash(repr(self))786return self._hash_value787788# #################################################################################789# # Morphisms of objects with generators790# #################################################################################791792793## COERCE TODO: see categories.MultiplicativeAbelianGroups794795# cdef class ParentWithMultiplicativeAbelianGens(Parent):796# def generator_orders(self):797# if self._generator_orders != None:798# return self._generator_orders799# else:800# g = []801# for x in self.gens():802# g.append(x.multiplicative_order())803# self._generator_orders = g804# return g805806# def __iter__(self):807# """808# Return an iterator over the elements in this object.809# """810# return gens_py.multiplicative_iterator(self)811812813814# cdef class ParentWithAdditiveAbelianGens(Parent):815# def generator_orders(self):816# if self._generator_orders != None:817# return self._generator_orders818# else:819# g = []820# for x in self.gens():821# g.append(x.additive_order())822# self._generator_orders = g823# return g824825# def __iter__(self):826# """827# Return an iterator over the elements in this object.828# """829# return gens_py.abelian_iterator(self)830831832833834class localvars:835r"""836Context manager for safely temporarily changing the variables837names of an object with generators.838839Objects with named generators are globally unique in Sage.840Sometimes, though, it is very useful to be able to temporarily841display the generators differently. The new Python ``with``842statement and the localvars context manager make this easy and843safe (and fun!)844845Suppose X is any object with generators. Write846847::848849with localvars(X, names[, latex_names] [,normalize=False]):850some code851...852853and the indented code will be run as if the names in ``X`` are changed to854the new names. If you give ``normalize=True``, then the names are assumed855to be a tuple of the correct number of strings.856857EXAMPLES::858859sage: R.<x,y> = PolynomialRing(QQ,2)860sage: with localvars(R, 'z,w'):861... print x^3 + y^3 - x*y862...863z^3 + w^3 - z*w864865NOTES: I wrote this because it was needed to print elements of the quotient866of a ring `R` by an ideal `I` using the print function for elements of `R`.867See the code in :mod:`sage.rings.quotient_ring_element`.868869AUTHOR: William Stein (2006-10-31)870"""871# fix this so that it handles latex names with the printer framework.872def __init__(self, obj, names, latex_names=None, normalize=True):873self._obj = obj874if normalize:875self._names = obj.normalize_names(obj.ngens(), names)876else:877self._names = names878879def __enter__(self):880self._orig_names = (<CategoryObject?>self._obj)._names881self._obj._temporarily_change_names(self._names)882883def __exit__(self, type, value, traceback):884self._obj._temporarily_change_names(self._orig_names)885886887# This Cython class confuses the hell out of the Sphinx documentation parser888# (because __doc__ is defined but not set). And the only code that refers to it889# is commented out. So I'm commenting it out too. -- David Loeffler 2009-07-06890#cdef class FillFirstArg:891# cdef object arg, f892# cdef public __doc__893# def __init__(self, f, arg):894# self.arg = arg895# self.f = f896# self.__doc__ = f.__doc__897# def __call__(self, *args, **kwds):898# return self.f(self.arg, *args, **kwds)899900901