Ticket #14249: trac_14249-coercion_without_an_element.patch
File trac_14249-coercion_without_an_element.patch, 19.6 KB (added by , 10 years ago) |
---|
-
sage/structure/coerce.pxd
# HG changeset patch # User Simon King <simon.king@uni-jena.de> # Date 1363171507 -3600 # Node ID 59cc9751e0c0725283d3ccf5362fdcd278689d48 # Parent 3a9ba2ec449be4de0dfc3179889864589de813e5 #14249: Do not call an_element() in coercion if actual elements are given. diff --git a/sage/structure/coerce.pxd b/sage/structure/coerce.pxd
a b 20 20 cpdef verify_coercion_maps(self, R, S, homs, bint fix=*) 21 21 cpdef verify_action(self, action, R, S, op, bint fix=*) 22 22 23 cpdef get_action(self, xp, yp, op)24 cpdef discover_action(self, R, S, op )23 cpdef get_action(self, R, S, op, r=*, s=*) 24 cpdef discover_action(self, R, S, op, r=*, s=*) 25 25 26 26 cdef bint _record_exceptions 27 27 cpdef _record_exception(self) -
sage/structure/coerce.pyx
diff --git a/sage/structure/coerce.pyx b/sage/structure/coerce.pyx
a b 794 794 yp = parent_c(y) 795 795 if xp is yp: 796 796 return op(x,y) 797 action = self.get_action(xp, yp, op )797 action = self.get_action(xp, yp, op, x, y) 798 798 if action is not None: 799 799 return (<Action>action)._call_(x, y) 800 800 … … 1234 1234 return None 1235 1235 1236 1236 1237 cpdef get_action(self, R, S, op ):1237 cpdef get_action(self, R, S, op, r=None, s=None): 1238 1238 """ 1239 1239 Get the action of R on S or S on R associated to the operation op. 1240 1240 1241 1242 1241 1243 EXAMPLES:: 1242 1244 1243 1245 sage: cm = sage.structure.element.get_coercion_model() 1244 1246 sage: cm.get_action(ZZ['x'], ZZ, operator.mul) 1245 1247 Right scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring … … 1267 1269 return self._action_maps.get(R, S, op) 1268 1270 except KeyError: 1269 1271 pass 1270 action = self.discover_action(R, S, op )1272 action = self.discover_action(R, S, op, r, s) 1271 1273 action = self.verify_action(action, R, S, op) 1272 1274 self._action_maps.set(R, S, op, action) 1273 1275 return action … … 1330 1332 1331 1333 return action 1332 1334 1333 cpdef discover_action(self, R, S, op ):1335 cpdef discover_action(self, R, S, op, r=None, s=None): 1334 1336 """ 1335 1337 INPUT 1336 1338 1337 1339 - ``R`` - the left Parent (or type) 1338 1339 1340 - ``S`` - the right Parent (or type) 1340 1341 - ``op`` - the operand, typically an element of the operator module. 1341 - ``op`` - the operand, typically an element of the operator module 1342 - ``r`` - (optional) element of R 1343 - ``s`` - (optional) element of S. 1342 1344 1343 1345 OUTPUT: 1344 1346 1345 1347 - An action A such that s op r is given by A(s,r). 1346 1348 1347 1349 The steps taken are illustrated below. 1348 1350 1349 1351 EXAMPLES:: 1350 1352 1351 1353 sage: P.<x> = ZZ['x'] 1352 1354 sage: P.get_action(ZZ) 1353 1355 Right scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring … … 1356 1358 sage: cm = sage.structure.element.get_coercion_model() 1357 1359 1358 1360 If R or S is a Parent, ask it for an action by/on R:: 1359 1361 1360 1362 sage: cm.discover_action(ZZ, P, operator.mul) 1361 1363 Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring 1362 1364 1363 1365 If R or S a type, recursively call get_action with the Sage versions of R and/or S:: 1364 1366 1365 1367 sage: cm.discover_action(P, int, operator.mul) 1366 1368 Right scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring 1367 1369 with precomposition on right by Native morphism: 1368 1370 From: Set of Python objects of type 'int' 1369 1371 To: Integer Ring 1370 1372 1371 1373 If op in an inplace operation, look for the non-inplace action:: 1372 1374 1373 1375 sage: cm.discover_action(P, ZZ, operator.imul) 1374 1376 Right scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring 1375 1377 1376 1378 If op is division, look for action on right by inverse:: 1377 1379 1378 1380 sage: cm.discover_action(P, ZZ, operator.div) 1379 1381 Right inverse action by Rational Field on Univariate Polynomial Ring in x over Integer Ring 1380 1382 with precomposition on right by Natural morphism: … … 1384 1386 #print "looking", R, <int><void *>R, op, S, <int><void *>S 1385 1387 1386 1388 if PY_TYPE_CHECK(R, Parent): 1387 action = (<Parent>R).get_action(S, op, True )1389 action = (<Parent>R).get_action(S, op, True, r, s) 1388 1390 if action is not None: 1389 1391 #print "found2", action 1390 1392 return action 1391 1393 1392 1394 if PY_TYPE_CHECK(S, Parent): 1393 action = (<Parent>S).get_action(R, op, False )1395 action = (<Parent>S).get_action(R, op, False, s, r) 1394 1396 if action is not None: 1395 1397 #print "found1", action 1396 1398 return action … … 1398 1400 if PY_TYPE(R) == <void *>type: 1399 1401 sageR = py_scalar_parent(R) 1400 1402 if sageR is not None: 1401 action = self.discover_action(sageR, S, op )1403 action = self.discover_action(sageR, S, op, s=s) 1402 1404 if action is not None: 1403 1405 if not PY_TYPE_CHECK(action, IntegerMulAction): 1404 1406 action = PrecomposedAction(action, sageR.coerce_map_from(R), None) … … 1407 1409 if PY_TYPE(S) == <void *>type: 1408 1410 sageS = py_scalar_parent(S) 1409 1411 if sageS is not None: 1410 action = self.discover_action(R, sageS, op )1412 action = self.discover_action(R, sageS, op, r=r) 1411 1413 if action is not None: 1412 1414 if not PY_TYPE_CHECK(action, IntegerMulAction): 1413 1415 action = PrecomposedAction(action, None, sageS.coerce_map_from(S)) … … 1415 1417 1416 1418 if op.__name__[0] == 'i': 1417 1419 try: 1418 a = self.discover_action(R, S, no_inplace_op(op) )1420 a = self.discover_action(R, S, no_inplace_op(op), r, s) 1419 1421 if a is not None: 1420 1422 is_inverse = isinstance(a, InverseAction) 1421 1423 if is_inverse: a = ~a 1422 1424 if a is not None and PY_TYPE_CHECK(a, RightModuleAction): 1423 1425 # We want a new instance so that we don't alter the (potentially cached) original 1424 a = RightModuleAction(S, R )1426 a = RightModuleAction(S, R, s, r) 1425 1427 a.is_inplace = 1 1426 1428 if is_inverse: a = ~a 1427 1429 return a -
sage/structure/coerce_actions.pyx
diff --git a/sage/structure/coerce_actions.pyx b/sage/structure/coerce_actions.pyx
a b 151 151 else: 152 152 return (<Element>a)._acted_upon_(b, True) 153 153 154 def detect_element_action(Parent X, Y, bint X_on_left ):154 def detect_element_action(Parent X, Y, bint X_on_left, X_el=None, Y_el=None): 155 155 r""" 156 156 Returns an action of X on Y or Y on X as defined by elements X, if any. 157 157 … … 171 171 172 172 TESTS: 173 173 174 This test checks that the issue in Trac #7718has been fixed::174 This test checks that the issue in :trac:`7718` has been fixed:: 175 175 176 176 sage: class MyParent(Parent): 177 ... def an_element(self):178 ... pass179 ... 177 ....: def an_element(self): 178 ....: pass 179 ....: 180 180 sage: A = MyParent() 181 181 sage: detect_element_action(A, ZZ, True) 182 182 Traceback (most recent call last): 183 183 ... 184 184 RuntimeError: an_element() for <class '__main__.MyParent'> returned None 185 185 """ 186 cdef Element x = an_element(X) 186 cdef Element x 187 if X_el is None or (parent_c(X_el) is not X): 188 x = an_element(X) 189 else: 190 x = X_el 187 191 if x is None: 188 raise RuntimeError, "an_element() for %s returned None" % X 189 y = an_element(Y) 192 raise RuntimeError("an_element() for %s returned None" % X) 193 if Y_el is None or (parent_c(Y_el) is not Y): 194 y = an_element(Y) 195 else: 196 y = Y_el 190 197 if y is None: 191 198 if isinstance(Y, Parent): 192 raise RuntimeError , "an_element() for %s returned None" % Y199 raise RuntimeError("an_element() for %s returned None" % Y) 193 200 else: 194 201 return # don't know how to make elements of this type... 195 202 if isinstance(x, ModuleElement) and isinstance(y, RingElement): 196 203 # Elements defining _lmul_ and _rmul_ 197 204 try: 198 return (RightModuleAction if X_on_left else LeftModuleAction)(Y, X )199 except CoercionException :205 return (RightModuleAction if X_on_left else LeftModuleAction)(Y, X, y, x) 206 except CoercionException, msg: 200 207 _record_exception() 201 208 try: 202 209 # Elements defining _act_on_ … … 214 221 215 222 216 223 cdef class ModuleAction(Action): 224 """ 225 Module action. 217 226 218 def __init__(self, G, S): 227 .. SEEALSO:: 228 229 This is an abstract class, one must actually instantiate a 230 :class:`LeftModuleAction` or a :class:`RightModuleAction`. 231 232 INPUT: 233 234 - ``G`` -- the actor, an instance of :class:`~sage.structure.parent.Parent`. 235 - ``S`` -- the object that is acted upon. 236 - ``g`` -- optional, an element of ``G``. 237 - ``a`` -- optional, an element of ``S``. 238 - ``check`` -- if True (default), then there will be no consistency tests 239 performed on sample elements. 240 241 NOTE: 242 243 By default, the sample elements of ``S`` and ``G`` are obtained from 244 :meth:`~sage.structure.parent.Parent.an_element`, which relies on the 245 implementation of an ``_an_element_()`` method. This is not always 246 awailable. But usually, the action is only needed when one already 247 *has* two elements. Hence, by :trac:`14249`, the coercion model will 248 pass these two elements the the :class:`ModuleAction` constructor. 249 250 The actual action is implemented by the ``_rmul_`` or ``_lmul_`` 251 function on its elements. We must, however, be very particular about 252 what we feed into these functions, because they operate under the 253 assumption that the inputs lie exactly in the base ring and may 254 segfault otherwise. Thus we handle all possible base extensions 255 manually here. 256 257 """ 258 def __init__(self, G, S, g=None, a=None, check=True): 219 259 """ 220 260 This creates an action of an element of a module by an element of its 221 261 base ring. The simplest example to keep in mind is R acting on the 222 262 polynomial ring R[x]. 223 263 224 The actual action is implemented by the _rmul_ or _lmul_ function on225 its elements. We must, however, be very particular about what we feed226 into these functions because they operate under the assumption that227 the inputs lie exactly in the base ring and may segfault otherwise.228 229 Thus we handle all possible base extensions manually here. This is230 an abstract class, one must actually instantiate a LeftModuleAction231 or a RightModuleAction232 233 264 EXAMPLES:: 234 265 235 266 sage: from sage.structure.coerce_actions import LeftModuleAction … … 293 324 the_set = S if self.extended_base is None else self.extended_base 294 325 assert the_ring is the_set.base(), "BUG in coercion model\n Apparently there are two versions of\n %s\n in the cache."%the_ring 295 326 296 g = G.an_element() 297 a = S.an_element() 327 if not check: 328 return 329 if g is None: 330 g = G.an_element() 331 if parent_c(g) is not G: 332 raise CoercionException("The parent of %s is not %s but %s"%(g,G,parent_c(g))) 333 if a is None: 334 a = S.an_element() 335 if parent_c(a) is not S: 336 raise CoercionException("The parent of %s is not %s but %s"%(a,S,parent_c(a))) 298 337 if not isinstance(g, RingElement) or not isinstance(a, ModuleElement): 299 raise CoercionException , "Not a ring element acting on a module element."338 raise CoercionException("Not a ring element acting on a module element.") 300 339 res = self.act(g, a) 301 302 if parent_c(res) is not S and parent_c(res) is not self.extended_base: 340 if parent_c(res) is not the_set: 303 341 # In particular we will raise an error if res is None 304 raise CoercionException , "Result is None or has wrong parent."342 raise CoercionException("Result is None or has wrong parent.") 305 343 306 344 307 345 def _repr_name_(self): … … 425 463 426 464 cdef class IntegerMulAction(Action): 427 465 428 def __init__(self, ZZ, M, is_left=True ):466 def __init__(self, ZZ, M, is_left=True, m=None): 429 467 r""" 430 468 This class implements the action `n \cdot a = a + a + \cdots + a` via 431 469 repeated doubling. 432 470 433 Both addition and negation must be defined on the set `A`. 471 Both addition and negation must be defined on the set `M`. 472 473 INPUT: 474 475 - An integer ring, ``ZZ`` 476 - A ``ZZ`` module ``M`` 477 - Optional: An element ``m`` of ``M`` 434 478 435 479 EXAMPLES:: 436 480 … … 447 491 if PY_TYPE_CHECK(ZZ, type): 448 492 from sage.structure.parent import Set_PythonType 449 493 ZZ = Set_PythonType(ZZ) 450 test = M.an_element() + (-M.an_element()) # make sure addition and negation is allowed 494 if m is None: 495 m = M.an_element() 496 test = m + (-m) # make sure addition and negation is allowed 451 497 Action.__init__(self, ZZ, M, is_left, operator.mul) 452 498 453 499 cpdef _call_(self, nn, a): -
sage/structure/parent.pxd
diff --git a/sage/structure/parent.pxd b/sage/structure/parent.pxd
a b 46 46 47 47 # returns the Action by/on self on/by S 48 48 # corresponding to op and self_on_left 49 cpdef get_action(self, other, op=*, bint self_on_left=*)50 cpdef _get_action_(self, other, op, bint self_on_left)49 cpdef get_action(self, S, op=*, bint self_on_left=*, self_el=*, S_el=*) 50 cpdef _get_action_(self, S, op, bint self_on_left) 51 51 52 52 # coerce x into self 53 53 cpdef coerce(self, x) … … 60 60 cpdef _generic_convert_map(self, S) 61 61 cdef discover_coerce_map_from(self, S) 62 62 cdef discover_convert_map_from(self, S) 63 cdef discover_action(self, G, op, bint self_on_left)63 cdef discover_action(self, S, op, bint self_on_left, self_el=*, S_el=*) 64 64 65 65 # List consisting of Morphisms (from anything to self) 66 66 # and Parents for which the __call__ method of self -
sage/structure/parent.pyx
diff --git a/sage/structure/parent.pyx b/sage/structure/parent.pyx
a b 56 56 ... 57 57 AssertionError 58 58 59 When implementing an element of a ring, one would typically provide the 60 element class with ``_rmul_`` and/or ``_lmul_`` methods for the action of a 61 base ring, and with ``_mul_`` for the ring multiplication. However, prior to 62 :trac:`14249`, it would have been necessary to additionally define a method 63 ``_an_element_()`` for the parent. But now, the following example works:: 64 65 sage: from sage.structure.element import RingElement 66 sage: class MyElement(RingElement): 67 ....: def __init__(self, x,y, parent = None): 68 ....: RingElement.__init__(self, parent) 69 ....: def _mul_(self, other): 70 ....: return self 71 ....: def _rmul_(self, other): 72 ....: return self 73 ....: def _lmul_(self, other): 74 ....: return self 75 sage: class MyParent(Parent): 76 ....: Element = MyElement 77 78 Now, we define :: 79 80 sage: P = MyParent(base=ZZ, category=Rings()) 81 sage: a = P(1,2) 82 sage: a*a is a 83 True 84 sage: a*2 is a 85 True 86 sage: 2*a is a 87 True 88 59 89 TESTS: 60 90 61 91 This came up in some subtle bug once:: 62 92 63 93 sage: gp(2) + gap(3) 64 94 5 95 65 96 """ 66 97 67 98 cimport element … … 2348 2379 """ 2349 2380 return None 2350 2381 2351 cpdef get_action(self, S, op=operator.mul, bint self_on_left=True ):2382 cpdef get_action(self, S, op=operator.mul, bint self_on_left=True, self_el=None, S_el=None): 2352 2383 """ 2353 2384 Returns an action of self on S or S on self. 2354 2385 … … 2370 2401 2371 2402 action = self._get_action_(S, op, self_on_left) 2372 2403 if action is None: 2373 action = self.discover_action(S, op, self_on_left )2404 action = self.discover_action(S, op, self_on_left, self_el, S_el) 2374 2405 2375 2406 if action is not None: 2376 2407 from sage.categories.action import Action … … 2383 2414 return action 2384 2415 2385 2416 2386 cdef discover_action(self, S, op, bint self_on_left ):2417 cdef discover_action(self, S, op, bint self_on_left, self_el=None, S_el=None): 2387 2418 # G acts on S, G -> G', R -> S => G' acts on R (?) 2388 2419 # NO! ZZ[x,y] acts on Matrices(ZZ[x]) but ZZ[y] does not. 2389 2420 # What may be true is that if the action's destination is S, then this can be allowed. 2421 # Note: a is either None or a sample elements of self. 2422 # If needed, it will be passed to Left/RightModuleAction. 2390 2423 from sage.categories.action import Action, PrecomposedAction 2391 2424 from sage.categories.homset import Hom 2392 2425 from coerce_actions import LeftModuleAction, RightModuleAction … … 2404 2437 R = action 2405 2438 _register_pair(self, R, "action") # to kill circular recursion 2406 2439 if self_on_left: 2407 action = LeftModuleAction(R, self ) # self is acted on from right2440 action = LeftModuleAction(R, self, a=S_el, g=self_el) # self is acted on from right 2408 2441 else: 2409 action = RightModuleAction(R, self ) # self is acted on from left2442 action = RightModuleAction(R, self, a=S_el, g=self_el) # self is acted on from left 2410 2443 ## The following two lines are disabled to prevent the following from working: 2411 2444 ## sage: x, y = var('x,y') 2412 2445 ## sage: parent(ZZ[x][y](1)*vector(QQ[y],[1,2])) … … 2444 2477 2445 2478 # detect actions defined by _rmul_, _lmul_, _act_on_, and _acted_upon_ methods 2446 2479 from coerce_actions import detect_element_action 2447 action = detect_element_action(self, S, self_on_left )2480 action = detect_element_action(self, S, self_on_left, self_el, S_el) 2448 2481 if action is not None: 2449 2482 return action 2450 2483 2451 2484 try: 2452 2485 # maybe there is a more clever way of detecting ZZ than importing here... 2453 2486 from sage.rings.integer_ring import ZZ 2454 2487 if S is ZZ and not self.has_coerce_map_from(ZZ): 2455 2488 from sage.structure.coerce_actions import IntegerMulAction 2456 action = IntegerMulAction(S, self, not self_on_left )2489 action = IntegerMulAction(S, self, not self_on_left, self_el) 2457 2490 return action 2458 2491 except (CoercionException, TypeError): 2459 2492 _record_exception() -
sage/structure/parent_old.pyx
diff --git a/sage/structure/parent_old.pyx b/sage/structure/parent_old.pyx
a b 241 241 242 242 cdef get_action_c_impl(self, S, op, bint self_on_left): 243 243 check_old_coerce(self) 244 return self.discover_action(S, op, self_on_left )244 return self.discover_action(S, op, self_on_left, None, None) 245 245 246 246 ################################################################################# 247 247 # Coercion support functionality