Ticket #5093: trac5093-fast-callable-cleanup.patch
File trac5093-fast-callable-cleanup.patch, 43.1 KB (added by , 14 years ago) |
---|
-
sage/calculus/calculus.py
# HG changeset patch # User Carl Witty <cwitty@newtonlabs.com> # Date 1237396960 25200 # Node ID a97f9d176f07425f7dc6c9965a931152469b7bdf # Parent c8ebd6134a6ff8bde681161097f56c7892502ab8 Fix bugs (and make minor enhancements) brought up by the reviewers/testers diff -r c8ebd6134a6f -r a97f9d176f07 sage/calculus/calculus.py
a b 5624 5624 add(v_0, v_1) 5625 5625 sage: (-x)._fast_callable_(etb) 5626 5626 neg(v_0) 5627 """ 5628 fops = [op._fast_callable_(etb) for op in self._operands] 5629 return self._operator(*fops) 5627 5628 TESTS:: 5629 5630 sage: etb = ExpressionTreeBuilder(vars=['x'], domain=RDF) 5631 sage: (x^7)._fast_callable_(etb) 5632 ipow(v_0, 7) 5633 """ 5634 # This used to convert the operands first. Doing it this way 5635 # instead gives a chance to notice powers with an integer 5636 # exponent before the exponent gets (potentially) converted 5637 # to another type. 5638 return etb.call(self._operator, *self._operands) 5630 5639 5631 5640 def _convert(self, typ): 5632 5641 """ … … 6232 6241 sage: z._fast_callable_(etb) 6233 6242 Traceback (most recent call last): 6234 6243 ... 6235 ValueError: list.index(x): x not in list6244 ValueError: Variable 'z' not found 6236 6245 """ 6237 6246 return etb.var(self) 6238 6247 -
sage/ext/fast_callable.pyx
diff -r c8ebd6134a6f -r a97f9d176f07 sage/ext/fast_callable.pyx
a b 32 32 sage: wilk = prod((x-i) for i in [1 .. 20]); wilk 33 33 (x - 20)*(x - 19)*(x - 18)*(x - 17)*(x - 16)*(x - 15)*(x - 14)*(x - 13)*(x - 12)*(x - 11)*(x - 10)*(x - 9)*(x - 8)*(x - 7)*(x - 6)*(x - 5)*(x - 4)*(x - 3)*(x - 2)*(x - 1) 34 34 sage: timeit('wilk.subs(x=30)') # random, long time 35 625 loops, best of 3: 1.4 6ms per loop35 625 loops, best of 3: 1.43 ms per loop 36 36 sage: fc_wilk = fast_callable(wilk) 37 37 sage: timeit('fc_wilk(30)') # random, long time 38 625 loops, best of 3: 10.4us per loop38 625 loops, best of 3: 9.72 us per loop 39 39 40 40 You can specify a particular domain for the evaluation using 41 41 \code{domain=}: … … 59 59 how that compares to the generic fc_wilk example above: 60 60 61 61 sage: timeit('fc_wilk_zz(30)') # random, long time 62 625 loops, best of 3: 15. 6us per loop62 625 loops, best of 3: 15.4 us per loop 63 63 64 64 However, for other types, using domain=D will get a large speedup, 65 65 because we have special-purpose interpreters for those types. One … … 70 70 71 71 sage: fc_wilk_rdf = fast_callable(wilk, domain=RDF) 72 72 sage: timeit('fc_wilk_rdf(30.0)') # random, long time 73 625 loops, best of 3: 5.13 us per loop 73 625 loops, best of 3: 7 us per loop 74 75 The domain does not need to be a Sage type; for instance, domain=float 76 also works. (We actually use the same fast interpreter for domain=float 77 and domain=RDF; the only difference is that when domain=RDF is used, 78 the return value is an RDF element, and when domain=float is used, 79 the return value is a Python float.) 80 81 sage: fc_wilk_float = fast_callable(wilk, domain=float) 82 sage: timeit('fc_wilk_float(30.0)') # random, long time 83 625 loops, best of 3: 5.04 us per loop 74 84 75 85 We also have support for RR: 76 86 77 87 sage: fc_wilk_rr = fast_callable(wilk, domain=RR) 78 88 sage: timeit('fc_wilk_rr(30.0)') # random, long time 79 625 loops, best of 3: 1 2.9us per loop89 625 loops, best of 3: 13 us per loop 80 90 81 91 By default, \function{fast_callable} uses the same variable names in the 82 92 same order that the \method{__call__} method on its argument would use; … … 165 175 EXAMPLES: 166 176 sage: var('x') 167 177 x 168 sage: f = fast_callable(sqrt(x^7+1), domain= RDF)178 sage: f = fast_callable(sqrt(x^7+1), domain=float) 169 179 170 180 sage: f(1) 171 181 1.4142135623730951 172 182 sage: f.op_list() 173 [('load_arg', 0), (' load_const', 7.0), 'pow', ('load_const', 1.0), 'add', 'sqrt', 'return']183 [('load_arg', 0), ('ipow', 7), ('load_const', 1.0), 'add', 'sqrt', 'return'] 174 184 175 185 To interpret that last line, we load argument 0 ('x' in this case) onto 176 186 the stack, push the constant 7.0 onto the stack, call the pow function … … 185 195 sage: f = etb.call(sqrt, x^7 + 1) 186 196 sage: g = etb.call(sin, x) 187 197 sage: fast_callable(f+g).op_list() 188 [('load_arg', 0), (' load_const', 7), 'pow', ('load_const', 1), 'add', ('py_call', sqrt, 1), ('load_arg', 0), ('py_call', sin, 1), 'add', 'return']198 [('load_arg', 0), ('ipow', 7), ('load_const', 1), 'add', ('py_call', sqrt, 1), ('load_arg', 0), ('py_call', sin, 1), 'add', 'return'] 189 199 190 200 191 201 AUTHOR: … … 265 275 from sage.structure.element cimport Element 266 276 from sage.rings.all import RDF 267 277 from sage.libs.mpfr cimport mpfr_t, mpfr_ptr, mpfr_init2, mpfr_set, GMP_RNDN 278 from sage.rings.integer import Integer 279 from sage.rings.integer_ring import ZZ 268 280 269 281 include "stdsage.pxi" 270 282 … … 277 289 method and a ._fast_callable_() method (this includes SR, univariate 278 290 polynomials, and multivariate polynomials). 279 291 280 By default, x is evaluated the same way that a Python function would281 evaluate it -- addition maps to PyNumber_Add, etc. However, you282 can specify domain=D where D is some Sage parent; in this case,283 all arithmetic is done in that parent. If we have a special-purpose284 interpreter for that parent (like RDF), domain=RDF will trigger the285 use of that interpreter.292 By default, x is evaluated the same way that a Python function 293 would evaluate it -- addition maps to PyNumber_Add, etc. However, 294 you can specify domain=D where D is some Sage parent or Python 295 type; in this case, all arithmetic is done in that domain. If we 296 have a special-purpose interpreter for that parent (like RDF or float), 297 domain=... will trigger the use of that interpreter. 286 298 287 299 If vars is None, then we will attempt to determine the set of 288 300 variables from x; otherwise, we will use the given set. … … 296 308 sin(2) + 12 297 309 sage: f(2.0) 298 310 12.9092974268257 311 312 We have special fast interpreters for domain=float and domain=RDF. 313 (Actually it's the same interpreter; only the return type varies.) 314 Note that the float interpreter is not actually more accurate than 315 the RDF interpreter; elements of RDF just don't display all 316 their digits. 317 318 sage: f_float = fast_callable(expr, domain=float) 319 sage: f_float(2) 320 12.909297426825681 299 321 sage: f_rdf = fast_callable(expr, domain=RDF) 300 322 sage: f_rdf(2) 301 12.9092974268 25681323 12.9092974268 302 324 sage: f = fast_callable(expr, vars=('z','x','y')) 303 325 sage: f(1, 2, 3) 304 326 sin(2) + 12 … … 309 331 sage: fp.op_list() 310 332 [('load_arg', 0), ('load_const', -1.0), 'mul', ('load_const', -12.0), 'add', ('load_arg', 0), 'mul', ('load_const', 0.5), 'add', ('load_arg', 0), 'mul', ('load_const', -0.0105263157895), 'add', ('load_arg', 0), 'mul', ('load_const', -0.5), 'add', ('load_arg', 0), 'mul', ('load_arg', 0), 'mul', ('load_const', -4.0), 'add', 'return'] 311 333 sage: fp(3.14159) 312 -4594.16182364 01764334 -4594.16182364 313 335 sage: K.<x,y,z> = QQ[] 314 336 sage: p = K.random_element(degree=3, terms=5); p 315 337 -x*y^2 - x*z^2 - 6*x^2 - y^2 - 3*x*z 316 338 sage: fp = fast_callable(p, domain=RDF) 317 339 sage: fp.op_list() 318 [('load_const', 0.0), ('load_const', -3.0), ('load_arg', 0), (' load_const', 1.0), 'pow', ('load_arg', 2), ('load_const', 1.0), 'pow', 'mul', 'mul', 'add', ('load_const', -1.0), ('load_arg', 0), ('load_const', 1.0), 'pow', ('load_arg', 1), ('load_const', 2.0), 'pow', 'mul', 'mul', 'add', ('load_const', -6.0), ('load_arg', 0), ('load_const', 2.0), 'pow', 'mul', 'add', ('load_const', -1.0), ('load_arg', 1), ('load_const', 2.0), 'pow', 'mul', 'add', ('load_const', -1.0), ('load_arg', 0), ('load_const', 1.0), 'pow', ('load_arg', 2), ('load_const', 2.0), 'pow', 'mul', 'mul', 'add', 'return']340 [('load_const', 0.0), ('load_const', -3.0), ('load_arg', 0), ('ipow', 1), ('load_arg', 2), ('ipow', 1), 'mul', 'mul', 'add', ('load_const', -1.0), ('load_arg', 0), ('ipow', 1), ('load_arg', 1), ('ipow', 2), 'mul', 'mul', 'add', ('load_const', -6.0), ('load_arg', 0), ('ipow', 2), 'mul', 'add', ('load_const', -1.0), ('load_arg', 1), ('ipow', 2), 'mul', 'add', ('load_const', -1.0), ('load_arg', 0), ('ipow', 1), ('load_arg', 2), ('ipow', 2), 'mul', 'mul', 'add', 'return'] 319 341 sage: fp(e, pi, sqrt(2)) 320 -98.0015640336 29322342 -98.0015640336 321 343 sage: symbolic_result = p(e, pi, sqrt(2)); symbolic_result 322 344 -1*e*pi^2 - pi^2 - 6*e^2 - 3*sqrt(2)*e - 2*e 323 345 sage: n(symbolic_result) 324 346 -98.0015640336293 325 347 326 348 sage: from sage.ext.fast_callable import ExpressionTreeBuilder 327 sage: etb = ExpressionTreeBuilder(vars=('x','y'), domain= RDF)349 sage: etb = ExpressionTreeBuilder(vars=('x','y'), domain=float) 328 350 sage: x = etb.var('x') 329 351 sage: y = etb.var('y') 330 352 sage: expr = etb.call(sin, x^2 + y); expr 331 sin(add( pow(v_0, 2.0), v_1))332 sage: fc = fast_callable(expr, domain= RDF)353 sin(add(ipow(v_0, 2), v_1)) 354 sage: fc = fast_callable(expr, domain=float) 333 355 sage: fc(5, 7) 334 356 0.55142668124169059 335 357 """ … … 357 379 str = InstructionStream(sage.ext.interpreters.wrapper_rr.metadata, 358 380 len(vars), 359 381 domain) 360 elif domain == RDF :382 elif domain == RDF or domain is float: 361 383 import sage.ext.interpreters.wrapper_rdf 362 384 builder = sage.ext.interpreters.wrapper_rdf.Wrapper_rdf 363 385 str = InstructionStream(sage.ext.interpreters.wrapper_rdf.metadata, 364 len(vars)) 386 len(vars), 387 domain) 365 388 elif domain is None: 366 389 import sage.ext.interpreters.wrapper_py 367 390 builder = sage.ext.interpreters.wrapper_py.Wrapper_py … … 550 573 sage: etb.var('y') 551 574 Traceback (most recent call last): 552 575 ... 553 ValueError: list.index(x): x not in list576 ValueError: Variable 'y' not found 554 577 """ 555 ind = self._vars.index(self._clean_var(v)) 578 var_name = self._clean_var(v) 579 try: 580 ind = self._vars.index(var_name) 581 except ValueError: 582 raise ValueError, "Variable '%s' not found" % var_name 556 583 return ExpressionVariable(self, ind) 557 584 558 585 def _var_number(self, n): … … 581 608 The arguments will be converted to Expressions using 582 609 ExpressionTreeBuilder.__call__. 583 610 611 As a special case, notices if the function is operator.pow and 612 the second argument is integral, and constructs an ExpressionIPow 613 instead. 614 584 615 EXAMPLES: 585 616 sage: from sage.ext.fast_callable import ExpressionTreeBuilder 586 617 sage: etb = ExpressionTreeBuilder(vars=(x,)) … … 592 623 sin(1) 593 624 sage: etb.call(factorial, x+57) 594 625 {factorial}(add(v_0, 57)) 626 sage: etb.call(operator.pow, x, 543) 627 ipow(v_0, 543) 595 628 """ 596 return ExpressionCall(self, fn, map(self, args)) 629 if fn is operator.pow: 630 base, exponent = args 631 return self(base)**exponent 632 else: 633 return ExpressionCall(self, fn, map(self, args)) 597 634 598 635 def choice(self, cond, iftrue, iffalse): 599 636 r""" … … 791 828 r""" 792 829 Compute a power expression from two Expressions. 793 830 831 If the second Expression is a constant integer, then return 832 an ExpressionIPow instead of an ExpressionCall. 833 794 834 EXAMPLES: 795 835 sage: from sage.ext.fast_callable import ExpressionTreeBuilder 796 836 sage: etb = ExpressionTreeBuilder(vars=(x,)) … … 798 838 sage: x^x 799 839 pow(v_0, v_0) 800 840 sage: x^1 801 pow(v_0, 1)841 ipow(v_0, 1) 802 842 sage: x.__pow__(1) 803 pow(v_0, 1) 843 ipow(v_0, 1) 844 sage: x.__pow__(1.0) 845 pow(v_0, 1.00000000000000) 804 846 sage: x.__rpow__(1) 805 847 pow(1, v_0) 806 848 """ … … 811 853 # (Plus, we should consider how strict a semantics we want; 812 854 # probably this sort of optimization should be controlled by a 813 855 # flag.) 814 return _expression_binop_helper(s, o, op_pow) 856 857 cdef Expression es 858 if isinstance(o, (int, long, Integer)): 859 es = s 860 return ExpressionIPow(es._etb, s, o) 861 else: 862 # I really don't like this, but I can't think of a better way 863 from sage.calculus.calculus import is_SymbolicExpression 864 if is_SymbolicExpression(o) and o in ZZ: 865 es = s 866 return ExpressionIPow(es._etb, s, ZZ(o)) 867 else: 868 return _expression_binop_helper(s, o, op_pow) 815 869 816 870 def __neg__(self): 817 871 r""" … … 1088 1142 fn = function_name(self._function) 1089 1143 return '%s(%s)' % (fn, ', '.join(map(repr, self._arguments))) 1090 1144 1145 cdef class ExpressionIPow(Expression): 1146 r""" 1147 A power Expression with an integer exponent. 1148 1149 EXAMPLES: 1150 sage: from sage.ext.fast_callable import ExpressionTreeBuilder 1151 sage: etb = ExpressionTreeBuilder(vars=(x,)) 1152 sage: type(etb.var('x')^17) 1153 <type 'sage.ext.fast_callable.ExpressionIPow'> 1154 """ 1155 cdef object _base 1156 cdef object _exponent 1157 1158 def __init__(self, etb, base, exponent): 1159 r""" 1160 Initialize an ExpressionIPow. 1161 1162 EXAMPLES: 1163 sage: from sage.ext.fast_callable import ExpressionTreeBuilder, ExpressionIPow 1164 sage: etb = ExpressionTreeBuilder(vars=(x,)) 1165 sage: x = etb(x) 1166 sage: x^(-12) 1167 ipow(v_0, -12) 1168 sage: v = ExpressionIPow(etb, x, 55); v 1169 ipow(v_0, 55) 1170 sage: v._get_etb() is etb 1171 True 1172 sage: v.base() 1173 v_0 1174 sage: v.exponent() 1175 55 1176 """ 1177 Expression.__init__(self, etb) 1178 self._base = base 1179 self._exponent = exponent 1180 1181 def base(self): 1182 r""" 1183 Return the base from this ExpressionIPow. 1184 1185 EXAMPLES: 1186 sage: from sage.ext.fast_callable import ExpressionTreeBuilder 1187 sage: etb = ExpressionTreeBuilder(vars=(x,)) 1188 sage: (etb(33)^42).base() 1189 33 1190 """ 1191 return self._base 1192 1193 def exponent(self): 1194 r""" 1195 Return the exponent from this ExpressionIPow. 1196 1197 EXAMPLES: 1198 sage: from sage.ext.fast_callable import ExpressionTreeBuilder 1199 sage: etb = ExpressionTreeBuilder(vars=(x,)) 1200 sage: (etb(x)^(-1)).exponent() 1201 -1 1202 """ 1203 return self._exponent 1204 1205 def __repr__(self): 1206 r""" 1207 Give a string representing this ExpressionIPow. 1208 1209 EXAMPLES: 1210 sage: from sage.ext.fast_callable import ExpressionTreeBuilder 1211 sage: etb = ExpressionTreeBuilder(vars=(x,)) 1212 sage: x = etb.var(x) 1213 sage: x^3 1214 ipow(v_0, 3) 1215 sage: x^(-2) 1216 ipow(v_0, -2) 1217 sage: v = (x+1)^3 1218 sage: v 1219 ipow(add(v_0, 1), 3) 1220 sage: repr(v) 1221 'ipow(add(v_0, 1), 3)' 1222 sage: v.__repr__() 1223 'ipow(add(v_0, 1), 3)' 1224 """ 1225 return 'ipow(%s, %d)' % (repr(self._base), self._exponent) 1226 1091 1227 cdef class ExpressionChoice(Expression): 1092 1228 r""" 1093 1229 A conditional expression. … … 1239 1375 1240 1376 return ExpressionCall(self._etb, op, [self, other]) 1241 1377 1378 class IntegerPowerFunction(object): 1379 r""" 1380 This class represents the function x^n for an arbitrary integral 1381 power n. That is, IntegerPowerFunction(2) is the squaring function; 1382 IntegerPowerFunction(-1) is the reciprocal function. 1383 1384 EXAMPLES: 1385 sage: from sage.ext.fast_callable import IntegerPowerFunction 1386 sage: square = IntegerPowerFunction(2) 1387 sage: square 1388 (^2) 1389 sage: square(pi) 1390 pi^2 1391 sage: square(I) 1392 -1 1393 sage: square(RIF(-1, 1)).str(style='brackets') 1394 '[0.00000000000000000 .. 1.0000000000000000]' 1395 sage: IntegerPowerFunction(-1) 1396 (^(-1)) 1397 sage: IntegerPowerFunction(-1)(22/7) 1398 7/22 1399 sage: v = Integers(123456789)(54321) 1400 sage: v^9876543210 1401 79745229 1402 sage: IntegerPowerFunction(9876543210)(v) 1403 79745229 1404 """ 1405 1406 def __init__(self, n): 1407 r""" 1408 Initializes an IntegerPowerFunction. 1409 1410 EXAMPLES: 1411 sage: from sage.ext.fast_callable import IntegerPowerFunction 1412 sage: cube = IntegerPowerFunction(3) 1413 sage: cube 1414 (^3) 1415 sage: cube(AA(7)^(1/3)) 1416 7.000000000000000? 1417 sage: cube.exponent 1418 3 1419 """ 1420 self.exponent = n 1421 1422 def __repr__(self): 1423 r""" 1424 Return a string representing this IntegerPowerFunction. 1425 1426 EXAMPLES: 1427 sage: from sage.ext.fast_callable import IntegerPowerFunction 1428 sage: square = IntegerPowerFunction(2) 1429 sage: square 1430 (^2) 1431 sage: repr(square) 1432 '(^2)' 1433 sage: square.__repr__() 1434 '(^2)' 1435 sage: repr(IntegerPowerFunction(-57)) 1436 '(^(-57))' 1437 """ 1438 if self.exponent >= 0: 1439 return "(^%s)" % self.exponent 1440 else: 1441 return "(^(%s))" % self.exponent 1442 1443 def __call__(self, x): 1444 r""" 1445 Call this IntegerPowerFunction, to compute a power of its argument. 1446 1447 EXAMPLES: 1448 sage: from sage.ext.fast_callable import IntegerPowerFunction 1449 sage: square = IntegerPowerFunction(2) 1450 sage: square.__call__(5) 1451 25 1452 sage: square(5) 1453 25 1454 """ 1455 return x**self.exponent 1456 1242 1457 builtin_functions = None 1243 1458 cpdef get_builtin_functions(): 1244 1459 r""" … … 1324 1539 8.2831853071795864769252867665590057684 1325 1540 sage: fc = fast_callable(expr, domain=RDF) 1326 1541 sage: fc(0) 1327 3.1415926535 8979311542 3.14159265359 1328 1543 sage: fc(1) 1329 8.2831853071 7958621544 8.28318530718 1330 1545 sage: fc.op_list() 1331 1546 [('load_arg', 0), ('load_const', pi), 'add', ('load_arg', 0), ('load_const', 1), 'add', 'mul', 'return'] 1332 1547 sage: fc = fast_callable(etb.call(sin, x) + etb.call(sqrt, x), domain=RDF) 1333 1548 sage: fc(1) 1334 1.8414709848 0789651549 1.84147098481 1335 1550 sage: fc.op_list() 1336 1551 [('load_arg', 0), 'sin', ('load_arg', 0), 'sqrt', 'add', 'return'] 1337 1552 sage: fc = fast_callable(etb.call(sin, x) + etb.call(sqrt, x)) … … 1341 1556 [('load_arg', 0), ('py_call', sin, 1), ('load_arg', 0), ('py_call', sqrt, 1), 'add', 'return'] 1342 1557 sage: fc = fast_callable(etb.call(my_sin, x), domain=RDF) 1343 1558 sage: fc(3) 1344 0.1411200080 59867211559 0.14112000806 1345 1560 sage: fc = fast_callable(etb.call(my_sin, x), domain=RealField(100)) 1346 1561 sage: fc(3) 1347 1562 0.14112000805986722210074480281 … … 1349 1564 [('load_arg', 0), ('py_call', <function my_sin at 0x...>, 1), 'return'] 1350 1565 sage: fc = fast_callable(etb.call(my_sqrt, x), domain=RDF) 1351 1566 sage: fc(3) 1352 1.7320508075688772 1567 1.73205080757 1568 sage: parent(fc(3)) 1569 Real Double Field 1353 1570 sage: fc(-3) 1354 1571 Traceback (most recent call last): 1355 1572 ... … … 1399 1616 Traceback (most recent call last): 1400 1617 ... 1401 1618 TypeError: unable to convert x (=sin(3)) to an integer 1619 1620 sage: fc = fast_callable(etb(x)^100) 1621 sage: fc(pi) 1622 pi^100 1623 sage: fc = fast_callable(etb(x)^100, domain=ZZ) 1624 sage: fc(2) 1625 1267650600228229401496703205376 1626 sage: fc = fast_callable(etb(x)^100, domain=RIF) 1627 sage: fc(RIF(-2)) 1628 1.2676506002282295?e30 1629 sage: fc = fast_callable(etb(x)^100, domain=RDF) 1630 sage: fc.op_list() 1631 [('load_arg', 0), ('ipow', 100), 'return'] 1632 sage: fc(1.1) 1633 13780.6123398 1634 sage: fc = fast_callable(etb(x)^100, domain=RR) 1635 sage: fc.op_list() 1636 [('load_arg', 0), ('ipow', 100), 'return'] 1637 sage: fc(1.1) 1638 13780.6123398224 1639 sage: fc = fast_callable(etb(x)^(-100), domain=RDF) 1640 sage: fc.op_list() 1641 [('load_arg', 0), ('ipow', -100), 'return'] 1642 sage: fc(1.1) 1643 7.25657159015e-05 1644 sage: fc = fast_callable(etb(x)^(-100), domain=RR) 1645 sage: fc(1.1) 1646 0.0000725657159014814 1647 sage: expo = 2^32 1648 sage: base = (1.0).nextabove() 1649 sage: fc = fast_callable(etb(x)^expo, domain=RDF) 1650 sage: fc.op_list() 1651 [('load_arg', 0), ('py_call', (^4294967296), 1), 'return'] 1652 sage: fc(base) 1653 1.00000095367 1654 sage: RDF(base)^expo 1655 1.00000095367 1656 sage: fc = fast_callable(etb(x)^expo, domain=RR) 1657 sage: fc.op_list() 1658 [('load_arg', 0), ('py_call', (^4294967296), 1), 'return'] 1659 sage: fc(base) 1660 1.00000095367477 1661 sage: base^expo 1662 1.00000095367477 1402 1663 """ 1403 1664 cdef ExpressionConstant econst 1404 1665 cdef ExpressionVariable evar … … 1426 1687 stream.instr('py_call', fn, len(ecall._arguments)) 1427 1688 else: 1428 1689 raise ValueError, "Unhandled function %s in generate_code" % fn 1690 elif isinstance(expr, ExpressionIPow): 1691 base = expr.base() 1692 exponent = expr.exponent() 1693 metadata = stream.get_metadata() 1694 ipow_range = metadata.ipow_range 1695 if ipow_range is True: 1696 use_ipow = True 1697 elif isinstance(ipow_range, tuple): 1698 a,b = ipow_range 1699 use_ipow = (a <= exponent <= b) 1700 else: 1701 use_ipow = False 1702 generate_code(base, stream) 1703 if use_ipow: 1704 stream.instr('ipow', exponent) 1705 else: 1706 stream.instr('py_call', IntegerPowerFunction(exponent), 1) 1429 1707 else: 1430 1708 raise ValueError, "Unhandled expression kind %s in generate_code" % type(expr) 1431 1709 … … 1604 1882 self._bytecode.append(loc) 1605 1883 elif spec.parameters[i] == 'args': 1606 1884 self._bytecode.append(args[i]) 1885 elif spec.parameters[i] == 'code': 1886 self._bytecode.append(args[i]) 1607 1887 elif spec.parameters[i] == 'n_inputs': 1608 1888 self._bytecode.append(args[i]) 1609 1889 n_inputs = args[i] … … 1631 1911 Returns the interpreter metadata being used by the current 1632 1912 InstructionStream. 1633 1913 1634 (Probably only useful for writing doctests.) 1914 The code generator sometimes uses this to decide which code 1915 to generate. 1635 1916 1636 1917 EXAMPLES: 1637 1918 sage: from sage.ext.interpreters.wrapper_rdf import metadata … … 1684 1965 sage: instr_stream.current_op_list() 1685 1966 [('load_arg', 0), ('py_call', <built-in function sin>, 1), 'abs', 'return'] 1686 1967 sage: instr_stream.get_current() 1687 {'domain': None, 'code': [0, 0, 3, 0, 1, 1 1, 2], 'py_constants': [<built-in function sin>], 'args': 1, 'stack': 1, 'constants': []}1968 {'domain': None, 'code': [0, 0, 3, 0, 1, 12, 2], 'py_constants': [<built-in function sin>], 'args': 1, 'stack': 1, 'constants': []} 1688 1969 """ 1689 1970 d = {'args': self._n_args, 1690 1971 'constants': self._constants, … … 1697 1978 class InterpreterMetadata(object): 1698 1979 r""" 1699 1980 The interpreter metadata for a fast_callable interpreter. Currently 1700 only consists of a dictionary mapping instruction names to 1701 (CompilerInstrSpec, opcode) pairs, and a list mapping opcodes to 1702 (instruction name, CompilerInstrSpec) pairs. 1981 consists of a dictionary mapping instruction names to 1982 (CompilerInstrSpec, opcode) pairs, a list mapping opcodes to 1983 (instruction name, CompilerInstrSpec) pairs, and a range of exponents 1984 for which the ipow instruction can be used. This range can be 1985 False (if the ipow instruction should never be used), a pair of 1986 two integers (a,b), if ipow should be used for a<=n<=b, or True, 1987 if ipow should always be used. When ipow cannot be used, then 1988 we fall back on calling IntegerPowerFunction. 1703 1989 1704 1990 See the class docstring for CompilerInstrSpec for more information. 1705 1991 1706 1992 NOTE: You must not modify the metadata. 1707 1993 """ 1708 1994 1709 def __init__(self, by_opname, by_opcode ):1995 def __init__(self, by_opname, by_opcode, ipow_range): 1710 1996 r""" 1711 1997 Initialize an InterpreterMetadata object. 1712 1998 … … 1715 2001 1716 2002 Currently we do no error checking or processing, so we can 1717 2003 use this simple test: 1718 sage: metadata = InterpreterMetadata(by_opname='opname dict goes here', by_opcode='opcode list goes here' )2004 sage: metadata = InterpreterMetadata(by_opname='opname dict goes here', by_opcode='opcode list goes here', ipow_range=(2, 57)) 1719 2005 sage: metadata.by_opname 1720 2006 'opname dict goes here' 1721 2007 sage: metadata.by_opcode 1722 2008 'opcode list goes here' 2009 sage: metadata.ipow_range 2010 (2, 57) 1723 2011 """ 1724 2012 self.by_opname = by_opname 1725 2013 self.by_opcode = by_opcode 2014 self.ipow_range = ipow_range 1726 2015 1727 2016 class CompilerInstrSpec(object): 1728 2017 r""" … … 1817 2106 for p in instr.parameters: 1818 2107 p_loc = code[0] 1819 2108 code = code[1:] 1820 if p in ('args', ' n_inputs', 'n_outputs'):2109 if p in ('args', 'code', 'n_inputs', 'n_outputs'): 1821 2110 op.append(p_loc) 1822 2111 else: 1823 2112 op.append(args[p][p_loc]) … … 1869 2158 1870 2159 EXAMPLES: 1871 2160 sage: fast_callable(sin(x)/x, domain=RDF).get_orig_args() 1872 {'domain': None, 'code': [0, 0, 15, 0, 0, 7, 2], 'py_constants': [], 'args': 1, 'stack': 2, 'constants': []}2161 {'domain': Real Double Field, 'code': [0, 0, 16, 0, 0, 7, 2], 'py_constants': [], 'args': 1, 'stack': 2, 'constants': []} 1873 2162 """ 1874 2163 return self._orig_args 1875 2164 -
sage/ext/fast_eval.pyx
diff -r c8ebd6134a6f -r a97f9d176f07 sage/ext/fast_eval.pyx
a b 88 88 #***************************************************************************** 89 89 90 90 from sage.ext.fast_callable import fast_callable, Wrapper 91 from sage.rings.all import RDF92 91 93 92 include "stdsage.pxi" 94 93 … … 1348 1347 if old: 1349 1348 return f._fast_float_(*vars) 1350 1349 else: 1351 return fast_callable(f, vars=vars, domain= RDF)1350 return fast_callable(f, vars=vars, domain=float) 1352 1351 except AttributeError: 1353 1352 pass 1354 1353 -
sage/ext/gen_interpreters.py
diff -r c8ebd6134a6f -r a97f9d176f07 sage/ext/gen_interpreters.py
a b 104 104 from distutils.extension import Extension 105 105 106 106 ############################## 107 # This module is used during the Sage bu ld process, so it should not107 # This module is used during the Sage build process, so it should not 108 108 # use any other Sage modules. (In particular, it MUST NOT use any 109 109 # Cython modules -- they won't be built yet!) 110 110 # Also, we have some trivial dependency tracking, where we don't 111 111 # rebuild the interpreters if this file hasn't changed; if 112 # interpreter configu ation is split out into a separate file,112 # interpreter configuration is split out into a separate file, 113 113 # that will have to be changed. 114 114 ############################## 115 115 … … 1644 1644 chunk = chunks[chunk_code] 1645 1645 addr = None 1646 1646 ch_len = None 1647 if chunk.is_stack(): 1647 # shouldn't hardcode 'code' here 1648 if chunk.is_stack() or chunk.name == 'code': 1648 1649 pass 1649 1650 else: 1650 1651 m = re.match(r'\[(?:([0-9]+)|([a-zA-Z]))\]', s) … … 1959 1960 err_return -- a string indicating the value to be returned 1960 1961 in case of a Python exception 1961 1962 mc_code -- a memory chunk to use for the interpreted code 1963 extra_class_members -- Class members for the wrapper that 1964 don't correspond to memory chunks 1965 extra_members_initialize -- Code to initialize extra_class_members 1962 1966 1963 1967 EXAMPLES: 1964 1968 sage: from sage.ext.gen_interpreters import * 1965 1969 sage: interp = RDFInterpreter() 1966 1970 sage: interp.header 1967 ' '1971 '\n#include <gsl/gsl_math.h>\n' 1968 1972 sage: interp.pyx_header 1969 1973 '' 1970 1974 sage: interp.err_return 1971 1975 '-1094648009105371' 1972 1976 sage: interp.mc_code 1973 1977 {MC:code} 1978 sage: interp = RRInterpreter() 1979 sage: interp.extra_class_members 1980 '' 1981 sage: interp.extra_members_initialize 1982 '' 1974 1983 """ 1975 1984 self.header = '' 1976 1985 self.pyx_header = '' 1977 1986 self.err_return = 'NULL' 1978 1987 self.mc_code = MemoryChunkConstants('code', ty_int) 1988 self.extra_class_members = '' 1989 self.extra_members_initialize = '' 1979 1990 1980 1991 def _set_opcodes(self): 1981 1992 r""" … … 2017 2028 return_type -- the type returned by the C interpreter (None for int, 2018 2029 where 1 means success and 0 means error) 2019 2030 mc_retval -- None, or the MemoryChunk to use as a return value 2031 ipow_range -- the range of exponents supported by the ipow 2032 instruction (default is False, meaning never use ipow) 2033 adjust_retval -- None, or a string naming a function to call 2034 in the wrapper's __call__ to modify the return 2035 value of the interpreter 2020 2036 2021 2037 EXAMPLES: 2022 2038 sage: from sage.ext.gen_interpreters import * … … 2044 2060 else: 2045 2061 self.return_type = None 2046 2062 self.mc_retval = mc_retval 2063 self.ipow_range = False 2064 self.adjust_retval = None 2047 2065 2048 2066 class RDFInterpreter(StackInterpreter): 2049 2067 r""" 2050 2068 A subclass of StackInterpreter, specifying an interpreter over 2051 machine-floating-point values (C doubles). 2069 machine-floating-point values (C doubles). This is used for 2070 both domain=RDF and domain=float; currently the only difference 2071 between the two is the type of the value returned from the 2072 wrapper (they use the same wrapper and interpreter). 2052 2073 """ 2053 2074 2054 2075 def __init__(self): … … 2060 2081 sage: interp = RDFInterpreter() 2061 2082 sage: interp.name 2062 2083 'rdf' 2084 sage: interp.extra_class_members 2085 'cdef object _domain\n' 2086 sage: interp.extra_members_initialize 2087 "self._domain = args['domain']\n" 2088 sage: interp.adjust_retval 2089 'self._domain' 2063 2090 sage: interp.mc_py_constants 2064 2091 {MC:py_constants} 2065 2092 sage: interp.chunks … … 2087 2114 pg = params_gen(A=self.mc_args, C=self.mc_constants, D=self.mc_code, 2088 2115 S=self.mc_stack, P=self.mc_py_constants) 2089 2116 self.pg = pg 2117 self.header = """ 2118 #include <gsl/gsl_math.h> 2119 """ 2090 2120 instrs = [ 2091 2121 InstrSpec('load_arg', pg('A[D]', 'S'), 2092 2122 code='o0 = i0;'), … … 2123 2153 ('mul', '*'), ('div', '/')]: 2124 2154 instrs.append(instr_infix(name, pg('SS', 'S'), op)) 2125 2155 instrs.append(instr_funcall_2args('pow', pg('SS', 'S'), 'pow')) 2156 instrs.append(instr_funcall_2args('ipow', pg('SD', 'S'), 'gsl_pow_int')) 2126 2157 for (name, op) in [('neg', '-i0'), ('invert', '1/i0'), 2127 2158 ('abs', 'fabs(i0)')]: 2128 2159 instrs.append(instr_unary(name, pg('S', 'S'), op)) … … 2132 2163 instrs.append(instr_unary(name, pg('S', 'S'), "%s(i0)" % name)) 2133 2164 self.instr_descs = instrs 2134 2165 self._set_opcodes() 2166 # supported for exponents that fit in an int 2167 self.ipow_range = (int(-2**31), int(2**31-1)) 2168 self.extra_class_members = "cdef object _domain\n" 2169 self.extra_members_initialize = "self._domain = args['domain']\n" 2170 self.adjust_retval = 'self._domain' 2135 2171 2136 2172 class RRInterpreter(StackInterpreter): 2137 2173 r""" … … 2255 2291 ('mul', 'mpfr_mul'), ('div', 'mpfr_div'), 2256 2292 ('pow', 'mpfr_pow')]: 2257 2293 instrs.append(instr_funcall_2args_mpfr(name, pg('SS', 'S'), op)) 2294 instrs.append(instr_funcall_2args_mpfr('ipow', pg('SD', 'S'), 'mpfr_pow_si')) 2258 2295 for name in ['neg', 'abs', 2259 2296 'log', 'log2', 'log10', 2260 2297 'exp', 'exp2', 'exp10', … … 2277 2314 code='mpfr_ui_div(o0, 1, i0, GMP_RNDN);')) 2278 2315 self.instr_descs = instrs 2279 2316 self._set_opcodes() 2317 # Supported for exponents that fit in a long, so we could use 2318 # a much wider range on a 64-bit machine. On the other hand, 2319 # it's easier to write the code this way, and constant integer 2320 # exponents outside this range probably aren't very common anyway. 2321 self.ipow_range = (int(-2**31), int(2**31-1)) 2280 2322 2281 2323 class PythonInterpreter(StackInterpreter): 2282 2324 r""" … … 2300 2342 it's different than Py_XDECREF followed by assigning NULL.) 2301 2343 2302 2344 Note that as a tiny optimization, the interpreter always assumes 2303 (and assures) that empty parts of the stack contain NULL, so2345 (and ensures) that empty parts of the stack contain NULL, so 2304 2346 it doesn't bother to Py_XDECREF before it pushes onto the stack. 2305 2347 """ 2306 2348 … … 2368 2410 instrs.append(instr_funcall_2args(name, pg('SS', 'S'), op)) 2369 2411 instrs.append(InstrSpec('pow', pg('SS', 'S'), 2370 2412 code='o0 = PyNumber_Power(i0, i1, Py_None);')) 2413 instrs.append(InstrSpec('ipow', pg('SC[D]', 'S'), 2414 code='o0 = PyNumber_Power(i0, i1, Py_None);')) 2371 2415 for (name, op) in [('neg', 'PyNumber_Negative'), 2372 2416 ('invert', 'PyNumber_Invert'), 2373 2417 ('abs', 'PyNumber_Absolute')]: 2374 2418 instrs.append(instr_unary(name, pg('S', 'S'), '%s(i0)'%op)) 2375 2419 self.instr_descs = instrs 2376 2420 self._set_opcodes() 2421 # Always use ipow 2422 self.ipow_range = True 2377 2423 2378 2424 class ElementInterpreter(PythonInterpreter): 2379 2425 r""" … … 2542 2588 if input_len is not None: 2543 2589 w(" int n_i%d = %s;\n" % (i, string_of_addr(input_len))) 2544 2590 if not ch.is_stack(): 2545 if input_len is not None: 2591 # Shouldn't hardcode 'code' here 2592 if ch.name == 'code': 2593 w(" %s i%d = %s;\n" % (chst.c_local_type(), i, string_of_addr(ch))) 2594 elif input_len is not None: 2546 2595 w(" %s i%d = %s + ai%d;\n" % 2547 2596 (chst.c_ptr_type(), i, ch.name, i)) 2548 2597 else: … … 2742 2791 2743 2792 the_call = je(""" 2744 2793 {% if s.return_type %}return {% endif -%} 2794 {% if s.adjust_retval %}{{ s.adjust_retval }}({% endif %} 2745 2795 interp_{{ s.name }}({{ arg_ch.pass_argument() }} 2746 2796 {% for ch in s.chunks[1:] %} 2747 2797 , {{ ch.pass_argument() }} 2748 2798 {% endfor %} 2749 ) 2799 ){% if s.adjust_retval %}){% endif %} 2800 2750 2801 """, s=s, arg_ch=arg_ch) 2751 2802 2752 2803 w(je(""" … … 2783 2834 {% for ch in s.chunks %} 2784 2835 {% print ch.declare_class_members() %} 2785 2836 {% endfor %} 2837 {% print indent_lines(4, s.extra_class_members) %} 2786 2838 2787 2839 def __init__(self, args): 2788 2840 Wrapper.__init__(self, args, metadata) … … 2795 2847 {% for ch in s.chunks %} 2796 2848 {% print ch.init_class_members() %} 2797 2849 {% endfor %} 2850 {% print indent_lines(8, s.extra_members_initialize) %} 2798 2851 2799 2852 def __dealloc__(self): 2800 2853 cdef int i … … 2840 2893 ('{{ instr.name }}', 2841 2894 CompilerInstrSpec({{ instr.n_inputs }}, {{ instr.n_outputs }}, {{ instr.parameters }})), 2842 2895 {% endfor %} 2843 ]) 2896 ], 2897 ipow_range={{ s.ipow_range }}) 2844 2898 """, s=s, self=self, types=types, arg_ch=arg_ch, indent_lines=indent_lines, the_call=the_call, do_cleanup=do_cleanup)) 2845 2899 2846 2900 def get_interpreter(self): … … 2895 2949 simplest instructions: 2896 2950 sage: print rdf_interp 2897 2951 /* ... */ ... 2898 case 9: /* neg */2952 case 10: /* neg */ 2899 2953 { 2900 2954 double i0 = *--stack; 2901 2955 double o0; … … 2913 2967 type. 2914 2968 sage: print rr_interp 2915 2969 /* ... */ ... 2916 case 9: /* neg */2970 case 10: /* neg */ 2917 2971 { 2918 2972 mpfr_ptr i0 = *--stack; 2919 2973 mpfr_ptr o0 = *stack++; … … 2932 2986 Python-object element interpreter. 2933 2987 sage: print el_interp 2934 2988 /* ... */ ... 2935 case 9: /* neg */2989 case 10: /* neg */ 2936 2990 { 2937 2991 PyObject* i0 = *--stack; 2938 2992 *stack = NULL; … … 3153 3207 Finally we get to the __call__ method. We grab the arguments 3154 3208 passed by the caller, stuff them in our pre-allocated 3155 3209 argument array, and then call the C interpreter. 3210 3211 We optionally adjust the return value of the interpreter 3212 (currently only the RDF/float interpreter performs this step; 3213 this is the only place where domain=RDF differs than 3214 domain=float): 3215 3156 3216 sage: print rdf_wrapper 3157 3217 # ... 3158 3218 def __call__(self, *args): … … 3161 3221 cdef int i 3162 3222 for i from 0 <= i < len(args): 3163 3223 self._args[i] = args[i] 3164 return interp_rdf(c_args3224 return self._domain(interp_rdf(c_args 3165 3225 , self._constants 3166 3226 , self._py_constants 3167 3227 , self._stack 3168 3228 , self._code 3169 ) 3229 )) 3170 3230 ... 3171 3231 3172 3232 In Python-object based interpreters, the call to the C … … 3200 3260 documented at InterpreterMetadata; for now, we'll just show 3201 3261 what it looks like. 3202 3262 3203 Currently, there are t woparts to the metadata; the first maps3263 Currently, there are three parts to the metadata; the first maps 3204 3264 instruction names to instruction descriptions. The second one 3205 3265 maps opcodes to instruction descriptions. Note that we don't 3206 3266 use InstrSpec objects here; instead, we use CompilerInstrSpec 3207 3267 objects, which are much simpler and contain only the information 3208 we'll need at runtime. 3268 we'll need at runtime. The third part says what range the 3269 ipow instruction is defined over. 3209 3270 3210 3271 First the part that maps instruction names to 3211 3272 (CompilerInstrSpec, opcode) pairs. … … 3237 3298 ('add', 3238 3299 CompilerInstrSpec(2, 1, [])), 3239 3300 ... 3240 ]) 3301 ], ...) 3302 3303 And then the ipow range: 3304 sage: print rdf_wrapper 3305 # ... 3306 metadata = InterpreterMetadata(..., 3307 ipow_range=(-2147483648, 2147483647)) 3308 3241 3309 3242 3310 And that's it for the wrapper. 3243 3311 """ … … 3367 3435 modules = [ 3368 3436 Extension('sage.ext.interpreters.wrapper_rdf', 3369 3437 sources = ['sage/ext/interpreters/wrapper_rdf.pyx', 3370 'sage/ext/interpreters/interp_rdf.c']), 3438 'sage/ext/interpreters/interp_rdf.c'], 3439 libraries = ['gsl']), 3371 3440 3372 3441 Extension('sage.ext.interpreters.wrapper_rr', 3373 3442 sources = ['sage/ext/interpreters/wrapper_rr.pyx', -
sage/numerical/optimize.py
diff -r c8ebd6134a6f -r a97f9d176f07 sage/numerical/optimize.py
a b 235 235 if isinstance(func,SymbolicExpression): 236 236 var_list=func.variables() 237 237 var_names=map(str,var_list) 238 fast_f=fast_callable(func, vars=var_names, domain= RDF)238 fast_f=fast_callable(func, vars=var_names, domain=float) 239 239 f=lambda p: fast_f(*p) 240 240 gradient_list=func.gradient() 241 fast_gradient_functions=[fast_callable(gradient_list[i], vars=var_names, domain= RDF) for i in xrange(len(gradient_list))]241 fast_gradient_functions=[fast_callable(gradient_list[i], vars=var_names, domain=float) for i in xrange(len(gradient_list))] 242 242 gradient=lambda p: scipy.array([ a(*p) for a in fast_gradient_functions]) 243 243 else: 244 244 f=func … … 260 260 elif algorithm=="ncg": 261 261 if isinstance(func,SymbolicExpression): 262 262 hess=func.hessian() 263 hess_fast= [ [fast_callable(a, vars=var_names, domain= RDF) for a in row] for row in hess]263 hess_fast= [ [fast_callable(a, vars=var_names, domain=float) for a in row] for row in hess] 264 264 hessian=lambda p: [[a(*p) for a in row] for row in hess_fast] 265 265 hessian_p=lambda p,v: scipy.dot(scipy.array(hessian(p)),v) 266 266 min= optimize.fmin_ncg(f,map(float,x0),fprime=gradient,fhess=hessian,fhess_p=hessian_p,**args) -
sage/rings/polynomial/multi_polynomial.pyx
diff -r c8ebd6134a6f -r a97f9d176f07 sage/rings/polynomial/multi_polynomial.pyx
a b 260 260 sage: v = K.random_element(degree=3, terms=4); v 261 261 -6/5*x*y*z + 2*y*z^2 - x 262 262 sage: v._fast_callable_(etb) 263 add(add(add(0, mul(-6/5, mul(mul( pow(v_0, 1), pow(v_1, 1)), pow(v_2, 1)))), mul(2, mul(pow(v_1, 1), pow(v_2, 2)))), mul(-1,pow(v_0, 1)))263 add(add(add(0, mul(-6/5, mul(mul(ipow(v_0, 1), ipow(v_1, 1)), ipow(v_2, 1)))), mul(2, mul(ipow(v_1, 1), ipow(v_2, 2)))), mul(-1, ipow(v_0, 1))) 264 264 265 265 TESTS: 266 sage: v = K(0) 267 sage: vf = fast_callable(v) 268 sage: type(v(0r, 0r, 0r)) 269 <type 'sage.rings.rational.Rational'> 270 sage: type(vf(0r, 0r, 0r)) 271 <type 'sage.rings.rational.Rational'> 266 272 sage: K.<x,y,z> = QQ[] 267 273 sage: from sage.ext.fast_eval import fast_float 268 274 sage: fast_float(K(0)).op_list() … … 270 276 sage: fast_float(K(17)).op_list() 271 277 [('load_const', 0.0), ('load_const', 17.0), 'add', 'return'] 272 278 sage: fast_float(y).op_list() 273 [('load_const', 0.0), ('load_const', 1.0), ('load_arg', 1), (' load_const', 1.0), 'pow', 'mul', 'add', 'return']279 [('load_const', 0.0), ('load_const', 1.0), ('load_arg', 1), ('ipow', 1), 'mul', 'add', 'return'] 274 280 """ 275 281 my_vars = self.parent().variable_names() 276 282 x = [etb.var(v) for v in my_vars] 277 283 n = len(x) 278 284 279 expr = etb.constant( 0)285 expr = etb.constant(self.base_ring()(0)) 280 286 for (m, c) in self.dict().iteritems(): 281 287 monom = misc.mul([ x[i]**m[i] for i in range(n) if m[i] != 0], 282 288 etb.constant(c))