Ticket #9648: trac_9648_modulemorphism_codomain_extension.2.patch
File trac_9648_modulemorphism_codomain_extension.2.patch, 11.8 KB (added by , 11 years ago) 


sage/categories/modules_with_basis.py
# HG changeset patch # User Christian Stump <christian.stump@univie.ac.at> # Date 1280514062 14400 # Node ID 6fd64517acfbfbe4f7cae0644b8352411e59ddab # Parent fa178313bbfc50ce14602e809c375b99e3341ed1 #9648: New feature: ModulesWithBasis allows module_morphism's to a wider class of codomains diff git a/sage/categories/modules_with_basis.py b/sage/categories/modules_with_basis.py
a b r""" 2 2 Modules With Basis 3 3 4 4 AUTHORS: 5  Nicolas Thiery (2008): initial revision5  Nicolas M. Thiery (20082010): initial revision 6 6  Jason Bandlow & Florent Hivert (2010): Triangular Morphisms 7 7  Christian Stump (2010): #9648: module_morphism's to a wider class of codomains 8 8 """ 9 9 #***************************************************************************** 10 10 # Copyright (C) 2008 Teresa GomezDiaz (CNRS) <Teresa.GomezDiaz@univmlv.fr> … … from sage.misc.lazy_attribute import laz 18 18 from sage.misc.cachefunc import cached_method 19 19 from sage.misc.misc import attrcall, deprecated_function_alias 20 20 from sage.misc.sage_itertools import max_cmp, min_cmp 21 from sage.categories.all import Modules, Fields, HomCategory, Homset21 from sage.categories.all import Sets, CommutativeAdditiveSemigroups, Modules, HomCategory, Homset 22 22 from sage.categories.cartesian_product import CartesianProductsCategory 23 23 from sage.categories.tensor import tensor, TensorProductsCategory 24 24 from sage.categories.dual import DualObjectsCategory … … class ModulesWithBasis(Category_over_bas 188 188 189 189 INPUT: 190 190 191  ``self``  a parent `X` in ``ModulesWithBasis(R)``, with basis `x`191  ``self``  a parent `X` in ``ModulesWithBasis(R)``, with basis `x` 192 192 indexed by `I` 193  ``codomain``  the codomain `Y` of `f`: defaults to ``f.codomain()``193  ``codomain``  the codomain `Y` of `f`: defaults to ``f.codomain()`` 194 194 if the later is defined 195  ``zero``  the zero of the codomain; defaults to196 ``codomain.zero()`` or 0 if codomain is not specified197  ``position``  a non negative integer; defaults to 0198  ``on_basis``  a function `f` which accepts elements of `I`199 as positionth argument and returns 200  ``diagonal``  a function `d` from `I` to `R`201  ``triangular``  "upper" or "lower" or None195  ``zero``  the zero of the codomain; defaults to ``codomain.zero()`` 196 Can be used (with care) to define affine maps 197  ``position``  a non negative integer; defaults to 0 198  ``on_basis``  a function `f` which accepts elements of `I` 199 as positionth argument and returns elements of `Y` 200  ``diagonal``  a function `d` from `I` to `R` 201  ``triangular``  "upper" or "lower" or None 202 202  "upper": if the `leading_support()` of the image of `F(i)` is `i`, or 203 203  "lower": if the `trailing_support()` of the image of `F(i)` is `i`. 204  ``category``  a category. By default, this is204  ``category``  a category. By default, this is 205 205 ``ModulesWithBasis(R)`` if `Y` is in this category, and 206 206 otherwise this lets `\hom(X,Y)` decide 207 207 … … class ModulesWithBasis(Category_over_bas 223 223 Generic morphism: 224 224 From: X 225 225 To: Y 226 sage: x = X.basis() 226 sage: phi.category_for() 227 Category of modules with basis over Rational Field 228 sage: x = X.basis(); y = Y.basis() 227 229 sage: phi(x[1] + x[3]) 228 230 B[1] + 2*B[2] + B[3] + 2*B[4] 229 231 232 With the ``zero`` argument, one can define affine morphisms: 233 234 sage: phi = X.module_morphism(lambda i: Y.monomial(i) + 2*Y.monomial(i+1), codomain = Y, zero = 10*y[1]) 235 sage: phi(x[1] + x[3]) 236 11*B[1] + 2*B[2] + B[3] + 2*B[4] 237 sage: phi.category_for() 238 Category of sets 239 240 One can construct morphisms with the base ring as codomain:: 241 242 sage: X = CombinatorialFreeModule(ZZ,[1,1]) 243 sage: phi = X.module_morphism( on_basis=lambda i: i, codomain=ZZ ) 244 sage: phi( 2 * X.monomial(1) + 3 * X.monomial(1) ) 245 1 246 sage: phi.category_for() 247 Category of commutative additive semigroups 248 sage: phi.category_for() # todo: not implemented (ZZ is currently not in Modules(ZZ)) 249 Category of modules over Integer Ring 250 251 Or more generaly any ring admitting a coercion map from the base ring: 252 253 sage: phi = X.module_morphism(on_basis= lambda i: i, codomain=RR ) 254 sage: phi( 2 * X.monomial(1) + 3 * X.monomial(1) ) 255 1.00000000000000 256 sage: phi.category_for() 257 Category of commutative additive semigroups 258 sage: phi.category_for() # todo: not implemented (RR is currently not in Modules(ZZ)) 259 Category of modules over Integer Ring 260 261 sage: phi = X.module_morphism(on_basis= lambda i: i, codomain=Zmod(4) ) 262 sage: phi( 2 * X.monomial(1) + 3 * X.monomial(1) ) 263 3 264 265 sage: phi = Y.module_morphism(on_basis= lambda i: i, codomain=Zmod(4) ) 266 Traceback (most recent call last): 267 ... 268 AssertionError: codomain should be a module over base_ring 269 270 On can also define module morphism betweem free modules 271 over different base rings; here we implement the natural 272 map from `X = \RR^2` to `Y = \CC`:: 273 274 sage: X = CombinatorialFreeModule(RR,['x','y']) 275 sage: Y = CombinatorialFreeModule(CC,['z']) 276 sage: x = X.monomial('x') 277 sage: y = X.monomial('y') 278 sage: z = Y.monomial('z') 279 sage: def on_basis( a ): 280 ... if a == 'x': 281 ... return CC(1) * z 282 ... elif a == 'y': 283 ... return CC(I) * z 284 sage: phi = X.module_morphism( on_basis=on_basis, codomain=Y ) 285 sage: v = 3 * x + 2 * y; v 286 3.00000000000000*B['x'] + 2.00000000000000*B['y'] 287 sage: phi(v) 288 (3.00000000000000+2.00000000000000*I)*B['z'] 289 sage: phi.category_for() 290 Category of commutative additive semigroups 291 sage: phi.category_for() # todo: not implemented (CC is currently not in Modules(RR)!) 292 Category of vector spaces over Real Field with 53 bits of precision 293 294 sage: Y = CombinatorialFreeModule(CC['q'],['z']) 295 sage: z = Y.monomial('z') 296 sage: phi = X.module_morphism( on_basis=on_basis, codomain=Y ) 297 sage: phi(v) 298 (3.00000000000000+2.00000000000000*I)*B['z'] 299 300 Of course, there should be a coercion between the 301 respective base rings of the domain and the codomain for 302 this to be meaningful:: 303 304 sage: Y = CombinatorialFreeModule(QQ,['z']) 305 sage: phi = X.module_morphism( on_basis=on_basis, codomain=Y ) 306 Traceback (most recent call last): 307 ... 308 AssertionError: codomain should be a module over base_ring 309 310 sage: Y = CombinatorialFreeModule(RR['q'],['z']) 311 sage: phi = Y.module_morphism( on_basis=on_basis, codomain=X ) 312 Traceback (most recent call last): 313 ... 314 AssertionError: codomain should be a module over base_ring 315 316 230 317 With the ``diagonal`` argument, this returns the module morphism `g` such that: 231 318 232 319 `g(x_i) = d(i) y_i` … … class ModuleMorphismByLinearity(Morphism 1022 1109  ``codomain``  a parent in Modules(...); defaults to f.codomain() if the later is defined 1023 1110  ``position``  a non negative integer; defaults to 0 1024 1111  ``on_basis``  a function which accepts indices of the basis of domain as positionth argument (optional) 1025  ``zero``  the zero of the codomain; defaults to codomain.zero() or 0 if codomain is not specified1112  ``zero``  the zero of the codomain; defaults to ``codomain.zero()`` 1026 1113 1027 1114 ``on_basis`` may alternatively be provided in derived classes by implementing or setting ``_on_basis``. 1028 1115 … … class ModuleMorphismByLinearity(Morphism 1050 1137 by making sure to create ``phi`` through its parent. 1051 1138 1052 1139 """ 1140 # Might want to assert that domain is a module with basis 1141 base_ring = domain.base_ring() 1142 1053 1143 if codomain is None and hasattr(on_basis, 'codomain'): 1054 1144 codomain = on_basis.codomain() 1145 assert codomain is not None 1146 assert hasattr( codomain, 'base_ring' ) 1147 # codomain should be a module over base_ring 1148 # The natural test would be ``codomains in Modules(base_ring)`` 1149 # But this is not properly implemented yet: 1150 # sage: CC in Modules(QQ) 1151 # False 1152 # sage: QQ in Modules(QQ) 1153 # False 1154 # sage: CC[x] in Modules(QQ) 1155 # False 1156 # The test below is a bit more restrictive 1157 assert codomain.base_ring().has_coerce_map_from(base_ring) or \ 1158 codomain.has_coerce_map_from(base_ring), \ 1159 "codomain should be a module over base_ring" 1160 1055 1161 if zero is None: 1056 if codomain is not None: 1057 zero = codomain.zero() 1162 zero = codomain.zero() 1163 self._zero = zero 1164 1165 self._is_module_with_basis_over_same_base_ring = \ 1166 codomain in ModulesWithBasis( base_ring ) and zero == codomain.zero() 1167 1168 if category is None: 1169 if self._is_module_with_basis_over_same_base_ring: 1170 category = ModulesWithBasis(base_ring) 1171 elif zero == codomain.zero(): 1172 if codomain in Modules(base_ring): 1173 category = Modules(base_ring) 1174 else: 1175 # QQ is not in Modules(QQ)! 1176 category = CommutativeAdditiveSemigroups() 1058 1177 else: 1059 zero = 0 1060 assert domain.base_ring() == codomain.base_ring() 1061 if category is None and codomain is not None and codomain.category().is_subcategory(ModulesWithBasis(domain.base_ring())): 1062 category = ModulesWithBasis(domain.base_ring()) 1063 assert codomain is not None 1178 category = Sets() 1179 1064 1180 Morphism.__init__(self, Hom(domain, codomain, category = category)) 1065 self._zero = zero 1181 1182 1066 1183 self._position = position 1067 1184 if on_basis is not None: 1068 1185 self._on_basis = on_basis … … class ModuleMorphismByLinearity(Morphism 1129 1246 after = args[self._position+1:len(args)] 1130 1247 x = args[self._position] 1131 1248 assert(x.parent() is self.domain()) 1132 return sum([self._on_basis(*(before+(index,)+after))._lmul_(coeff) for (index, coeff) in args[self._position]], self._zero) 1249 1250 if self._is_module_with_basis_over_same_base_ring: 1251 return self.codomain().sum(self._on_basis(*(before+(index,)+after))._lmul_(coeff) for (index, coeff) in args[self._position]) 1252 else: 1253 return sum(( coeff * self._on_basis(*(before+(index,)+after)) for (index, coeff) in args[self._position]), self._zero) 1133 1254 1134 1255 # As per the specs of Map, we should in fact implement _call_. 1135 1256 # However we currently need to abuse Map.__call__ (which strict