| 1 | """ |
|---|
| 2 | Homsets |
|---|
| 3 | |
|---|
| 4 | AUTHORS: |
|---|
| 5 | -- David Kohel and William Stein |
|---|
| 6 | -- David Joyner (2005-12-17): added examples |
|---|
| 7 | -- William Stein (2006-01-14): Changed from Homspace to Homset. |
|---|
| 8 | """ |
|---|
| 9 | |
|---|
| 10 | #***************************************************************************** |
|---|
| 11 | # Copyright (C) 2005 David Kohel <kohel@maths.usyd.edu>, William Stein <wstein@gmail.com> |
|---|
| 12 | # |
|---|
| 13 | # Distributed under the terms of the GNU General Public License (GPL) |
|---|
| 14 | # |
|---|
| 15 | # This code is distributed in the hope that it will be useful, |
|---|
| 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty |
|---|
| 17 | # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
|---|
| 18 | # |
|---|
| 19 | # See the GNU General Public License for more details; the full text |
|---|
| 20 | # is available at: |
|---|
| 21 | # |
|---|
| 22 | # http://www.gnu.org/licenses/ |
|---|
| 23 | #***************************************************************************** |
|---|
| 24 | |
|---|
| 25 | import weakref |
|---|
| 26 | |
|---|
| 27 | import category |
|---|
| 28 | import morphism |
|---|
| 29 | from sage.sets.set import Set_generic |
|---|
| 30 | from sage.structure.parent_base import ParentWithBase |
|---|
| 31 | |
|---|
| 32 | _cache = {} |
|---|
| 33 | def Hom(X, Y, cat=None): |
|---|
| 34 | """ |
|---|
| 35 | Create the space of homomorphisms from X to Y in the category cat. |
|---|
| 36 | |
|---|
| 37 | INPUT: |
|---|
| 38 | X -- anything |
|---|
| 39 | Y -- anything |
|---|
| 40 | cat -- (optional) category in which the morphisms must be |
|---|
| 41 | |
|---|
| 42 | OUTPUT: |
|---|
| 43 | a homset in cat |
|---|
| 44 | |
|---|
| 45 | EXAMPLES: |
|---|
| 46 | sage: V = VectorSpace(QQ,3) |
|---|
| 47 | sage: Hom(V, V) |
|---|
| 48 | Set of Morphisms from Vector space of dimension 3 over Rational |
|---|
| 49 | Field to Vector space of dimension 3 over Rational Field in |
|---|
| 50 | Category of vector spaces over Rational Field |
|---|
| 51 | sage: G = SymmetricGroup(3) |
|---|
| 52 | sage: Hom(G, G) |
|---|
| 53 | Set of Morphisms from SymmetricGroup(3) to SymmetricGroup(3) in Category of groups |
|---|
| 54 | sage: Hom(ZZ, QQ, Sets()) |
|---|
| 55 | Set of Morphisms from Integer Ring to Rational Field in Category of sets |
|---|
| 56 | """ |
|---|
| 57 | global _cache |
|---|
| 58 | key = (X,Y,cat) |
|---|
| 59 | if _cache.has_key(key): |
|---|
| 60 | H = _cache[key]() |
|---|
| 61 | if H: return H |
|---|
| 62 | |
|---|
| 63 | if cat is None or (cat is X.category() and cat is Y.category()): |
|---|
| 64 | try: |
|---|
| 65 | H = X._Hom_(Y) |
|---|
| 66 | except AttributeError: |
|---|
| 67 | pass |
|---|
| 68 | |
|---|
| 69 | cat_X = X.category() |
|---|
| 70 | cat_Y = Y.category() |
|---|
| 71 | if cat is None: |
|---|
| 72 | if cat_X.is_subcategory(cat_Y): |
|---|
| 73 | cat = cat_Y |
|---|
| 74 | elif cat_Y.is_subcategory(cat_X): |
|---|
| 75 | if not (cat is None) and not (cat_X is cat_Y): |
|---|
| 76 | raise ValueError, "No unambiguous category found for Hom from %s to %s."%(X,Y) |
|---|
| 77 | cat = cat_X |
|---|
| 78 | else: |
|---|
| 79 | raise TypeError, "No suitable category found for Hom from %s to %s."%(X,Y) |
|---|
| 80 | |
|---|
| 81 | elif isinstance(cat, category.Category): |
|---|
| 82 | if not isinstance(cat, category.Category): |
|---|
| 83 | raise TypeError, "Argument cat (= %s) must be a category."%cat |
|---|
| 84 | if not cat_X.is_subcategory(cat) \ |
|---|
| 85 | or not cat_Y.is_subcategory(cat): |
|---|
| 86 | raise TypeError, \ |
|---|
| 87 | "Argument cat (= %s) is incompatible with %s and %s."%(cat, X, Y) |
|---|
| 88 | else: |
|---|
| 89 | raise TypeError, "Argument cat (= %s) must be a category."%cat |
|---|
| 90 | |
|---|
| 91 | |
|---|
| 92 | # coercing would be incredibly annoying, since the domain and codomain |
|---|
| 93 | # are totally different objects |
|---|
| 94 | #X = cat(X); Y = cat(Y) |
|---|
| 95 | |
|---|
| 96 | # construct H |
|---|
| 97 | if cat._is_subclass(category.HeckeModules): |
|---|
| 98 | |
|---|
| 99 | from sage.modular.hecke.homspace import HeckeModuleHomspace |
|---|
| 100 | H = HeckeModuleHomspace(X, Y) |
|---|
| 101 | |
|---|
| 102 | elif cat._is_subclass(category.FreeModules): |
|---|
| 103 | |
|---|
| 104 | from sage.modules.free_module_homspace import FreeModuleHomspace |
|---|
| 105 | H = FreeModuleHomspace(X, Y, cat) |
|---|
| 106 | |
|---|
| 107 | elif cat._is_subclass(category.Rings): |
|---|
| 108 | |
|---|
| 109 | from sage.rings.homset import RingHomset |
|---|
| 110 | H = RingHomset(X, Y) |
|---|
| 111 | |
|---|
| 112 | elif cat._is_subclass(category.Schemes) or cat._is_subclass(category.Schemes_over_base): |
|---|
| 113 | |
|---|
| 114 | from sage.schemes.generic.homset import SchemeHomset |
|---|
| 115 | H = SchemeHomset(X, Y) |
|---|
| 116 | |
|---|
| 117 | else: # default |
|---|
| 118 | if isinstance(X, ParentWithBase): |
|---|
| 119 | H = HomsetWithBase(X, Y, cat) |
|---|
| 120 | else: |
|---|
| 121 | H = Homset(X, Y, cat) |
|---|
| 122 | |
|---|
| 123 | ##_cache[key] = weakref.ref(H) |
|---|
| 124 | _cache[(X, Y, cat)] = weakref.ref(H) |
|---|
| 125 | |
|---|
| 126 | return H |
|---|
| 127 | |
|---|
| 128 | def hom(X, Y, f): |
|---|
| 129 | """ |
|---|
| 130 | Return Hom(X,Y)(f), where f is data that defines an element of Hom(X,Y). |
|---|
| 131 | |
|---|
| 132 | EXAMPLES: |
|---|
| 133 | sage: R, x = PolynomialRing(QQ,'x').objgen() |
|---|
| 134 | sage: phi = hom(R, QQ, [2]) |
|---|
| 135 | sage: phi(x^2 + 3) |
|---|
| 136 | 7 |
|---|
| 137 | """ |
|---|
| 138 | return Hom(X,Y)(f) |
|---|
| 139 | |
|---|
| 140 | def End(X, cat=None): |
|---|
| 141 | r""" |
|---|
| 142 | Create the set of endomorphisms of X in the category cat. |
|---|
| 143 | |
|---|
| 144 | INPUT: |
|---|
| 145 | X -- anything |
|---|
| 146 | cat -- (optional) category in which to coerce X |
|---|
| 147 | |
|---|
| 148 | OUTPUT: |
|---|
| 149 | a set of endomorphisms in cat |
|---|
| 150 | |
|---|
| 151 | EXAMPLES: |
|---|
| 152 | sage: V = VectorSpace(QQ, 3) |
|---|
| 153 | sage: End(V) |
|---|
| 154 | Set of Morphisms from Vector space of dimension 3 over Rational |
|---|
| 155 | Field to Vector space of dimension 3 over Rational Field in |
|---|
| 156 | Category of vector spaces over Rational Field |
|---|
| 157 | |
|---|
| 158 | sage: G = SymmetricGroup(3) |
|---|
| 159 | sage: S = End(G); S |
|---|
| 160 | Set of Morphisms from SymmetricGroup(3) to SymmetricGroup(3) in Category of groups |
|---|
| 161 | sage: is_Endset(S) |
|---|
| 162 | True |
|---|
| 163 | sage: S.domain() |
|---|
| 164 | Symmetric group of order 3! as a permutation group |
|---|
| 165 | |
|---|
| 166 | Homsets are \emph{not} objects in their category. They are |
|---|
| 167 | currently sets. |
|---|
| 168 | sage: S.category() |
|---|
| 169 | Category of sets |
|---|
| 170 | sage: S.domain().category() |
|---|
| 171 | Category of groups |
|---|
| 172 | """ |
|---|
| 173 | return Hom(X,X, cat) |
|---|
| 174 | |
|---|
| 175 | def end(X, f): |
|---|
| 176 | """ |
|---|
| 177 | Return End(X)(f), where f is data that defines an element of End(X). |
|---|
| 178 | |
|---|
| 179 | EXAMPLES: |
|---|
| 180 | sage: R, x = PolynomialRing(QQ,'x').objgen() |
|---|
| 181 | sage: phi = end(R, [x + 1]) |
|---|
| 182 | sage: phi |
|---|
| 183 | Ring endomorphism of Univariate Polynomial Ring in x over Rational Field |
|---|
| 184 | Defn: x |--> x + 1 |
|---|
| 185 | sage: phi(x^2 + 5) |
|---|
| 186 | x^2 + 2*x + 6 |
|---|
| 187 | """ |
|---|
| 188 | return End(X)(f) |
|---|
| 189 | |
|---|
| 190 | class Homset(Set_generic): |
|---|
| 191 | """ |
|---|
| 192 | The class for collections of morphisms in a category. |
|---|
| 193 | |
|---|
| 194 | EXAMPLES: |
|---|
| 195 | sage: H = Hom(QQ^2, QQ^3) |
|---|
| 196 | sage: loads(H.dumps()) == H |
|---|
| 197 | True |
|---|
| 198 | sage: E = End(AffineSpace(2, names='x,y')) |
|---|
| 199 | sage: loads(E.dumps()) == E |
|---|
| 200 | True |
|---|
| 201 | """ |
|---|
| 202 | def __init__(self, X, Y, cat=None, check=True): |
|---|
| 203 | self.__domain = X |
|---|
| 204 | self.__codomain = Y |
|---|
| 205 | if cat is None: |
|---|
| 206 | cat = X.category() |
|---|
| 207 | self.__category = cat |
|---|
| 208 | if check: |
|---|
| 209 | if not isinstance(cat, category.Category): |
|---|
| 210 | raise TypeError, "cat (=%s) must be a category"%cat |
|---|
| 211 | #if not X in cat: |
|---|
| 212 | # raise TypeError, "X (=%s) must be in cat (=%s)"%(X, cat) |
|---|
| 213 | #if not Y in cat: |
|---|
| 214 | # raise TypeError, "Y (=%s) must be in cat (=%s)"%(Y, cat) |
|---|
| 215 | |
|---|
| 216 | def _repr_(self): |
|---|
| 217 | return "Set of Morphisms from %s to %s in %s"%( |
|---|
| 218 | self.__domain, self.__codomain, self.__category) |
|---|
| 219 | |
|---|
| 220 | def __call__(self, x, y=None): |
|---|
| 221 | """ |
|---|
| 222 | Construct a morphism in this homset from x if possible. |
|---|
| 223 | """ |
|---|
| 224 | if x in self: |
|---|
| 225 | return x |
|---|
| 226 | raise TypeError, "Unable to coerce x (=%s) to a morphism in %s"%(x,self) |
|---|
| 227 | |
|---|
| 228 | def __cmp__(self, other): |
|---|
| 229 | if not isinstance(other, Homset): |
|---|
| 230 | return cmp(type(self), type(other)) |
|---|
| 231 | if self.__domain == other.__domain and self.__codomain == other.__codomain \ |
|---|
| 232 | and self.__category == other.__category: |
|---|
| 233 | return 0 |
|---|
| 234 | return cmp(self.__domain, other.__domain) |
|---|
| 235 | |
|---|
| 236 | def __contains__(self, x): |
|---|
| 237 | try: |
|---|
| 238 | return x.parent() == self |
|---|
| 239 | except AttributeError: |
|---|
| 240 | pass |
|---|
| 241 | return False |
|---|
| 242 | |
|---|
| 243 | def natural_map(self): |
|---|
| 244 | return morphism.FormalCoercionMorphism(self) # good default in many cases |
|---|
| 245 | |
|---|
| 246 | def domain(self): |
|---|
| 247 | return self.__domain |
|---|
| 248 | |
|---|
| 249 | def codomain(self): |
|---|
| 250 | return self.__codomain |
|---|
| 251 | |
|---|
| 252 | def is_endomorphism_set(self): |
|---|
| 253 | """ |
|---|
| 254 | Return True if the domain and codomain of self are the |
|---|
| 255 | same object. |
|---|
| 256 | """ |
|---|
| 257 | return self.__domain is self.__codomain |
|---|
| 258 | |
|---|
| 259 | def reversed(self): |
|---|
| 260 | """ |
|---|
| 261 | Return the corresponding homset, but with the domain and codomain |
|---|
| 262 | reversed. |
|---|
| 263 | """ |
|---|
| 264 | return Homset(self.__codomain, self.__domain, self.__category) |
|---|
| 265 | |
|---|
| 266 | class HomsetWithBase(ParentWithBase, Homset): |
|---|
| 267 | def __init__(self, X, Y, cat=None, check=True): |
|---|
| 268 | Homset.__init__(self, X, Y, cat, check) |
|---|
| 269 | ParentWithBase.__init__(self, X.base_ring()) |
|---|
| 270 | |
|---|
| 271 | def is_Homset(x): |
|---|
| 272 | """ |
|---|
| 273 | Return True if x is a set of homomorphisms in a category. |
|---|
| 274 | """ |
|---|
| 275 | return isinstance(x, Homset) |
|---|
| 276 | |
|---|
| 277 | def is_Endset(x): |
|---|
| 278 | """ |
|---|
| 279 | Return True if x is a set of endomorphisms in a category. |
|---|
| 280 | """ |
|---|
| 281 | return isinstance(x, Homset) and x.is_endomorphism_set() |
|---|