Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/ext/fast_callable.pyx
8817 views
1
r"""
2
Fast Expression Evaluation.
3
4
For many applications such as numerical integration, differential
5
equation approximation, plotting a 3d surface, optimization problems,
6
monte-carlo simulations, etc., one wishes to pass around and evaluate
7
a single algebraic expression many, many times at various floating
8
point values. Other applications may need to evaluate an expression
9
many times in interval arithmetic, or in a finite field. Doing this
10
via recursive calls over a python representation of the object (even
11
if Maxima or other outside packages are not involved) is extremely
12
inefficient.
13
14
This module provides a function, \function{fast_callable}, to
15
transform such expressions into a form where they can be evaluated
16
quickly::
17
18
sage: f = sin(x) + 3*x^2
19
sage: ff = fast_callable(f, vars=[x])
20
sage: ff(3.5)
21
36.3992167723104
22
sage: ff(RIF(3.5))
23
36.39921677231038?
24
25
By default, \function{fast_callable} only removes some interpretive
26
overhead from the evaluation, but all of the individual arithmetic
27
operations are done using standard \sage arithmetic. This is still a
28
huge win over sage.calculus, which evidently has a lot of overhead.
29
Compare the cost of evaluating Wilkinson's polynomial (in unexpanded
30
form) at x=30::
31
32
sage: wilk = prod((x-i) for i in [1 .. 20]); wilk
33
(x - 1)*(x - 2)*(x - 3)*(x - 4)*(x - 5)*(x - 6)*(x - 7)*(x - 8)*(x - 9)*(x - 10)*(x - 11)*(x - 12)*(x - 13)*(x - 14)*(x - 15)*(x - 16)*(x - 17)*(x - 18)*(x - 19)*(x - 20)
34
sage: timeit('wilk.subs(x=30)') # random, long time
35
625 loops, best of 3: 1.43 ms per loop
36
sage: fc_wilk = fast_callable(wilk, vars=[x])
37
sage: timeit('fc_wilk(30)') # random, long time
38
625 loops, best of 3: 9.72 us per loop
39
40
You can specify a particular domain for the evaluation using
41
\code{domain=}::
42
43
sage: fc_wilk_zz = fast_callable(wilk, vars=[x], domain=ZZ)
44
45
The meaning of domain=D is that each intermediate and final result
46
is converted to type D. For instance, the previous example of
47
``sin(x) + 3*x^2`` with domain=D would be equivalent to
48
``D(D(sin(D(x))) + D(D(3)*D(D(x)^2)))``. (This example also
49
demonstrates the one exception to the general rule: if an exponent is an
50
integral constant, then it is not wrapped with D().)
51
52
At first glance, this seems like a very bad idea if you want to
53
compute quickly. And it is a bad idea, for types where we don't
54
have a special interpreter. It's not too bad of a slowdown, though.
55
To mitigate the costs, we check whether the value already has
56
the correct parent before we call D.
57
58
We don't yet have a special interpreter with domain ZZ, so we can see
59
how that compares to the generic fc_wilk example above::
60
61
sage: timeit('fc_wilk_zz(30)') # random, long time
62
625 loops, best of 3: 15.4 us per loop
63
64
However, for other types, using domain=D will get a large speedup,
65
because we have special-purpose interpreters for those types. One
66
example is RDF. Since with domain=RDF we know that every single
67
operation will be floating-point, we can just execute the
68
floating-point operations directly and skip all the Python object
69
creations that you would get from actually using RDF objects::
70
71
sage: fc_wilk_rdf = fast_callable(wilk, vars=[x], domain=RDF)
72
sage: timeit('fc_wilk_rdf(30.0)') # random, long time
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, vars=[x], domain=float)
82
sage: timeit('fc_wilk_float(30.0)') # random, long time
83
625 loops, best of 3: 5.04 us per loop
84
85
We also have support for ``RR``::
86
87
sage: fc_wilk_rr = fast_callable(wilk, vars=[x], domain=RR)
88
sage: timeit('fc_wilk_rr(30.0)') # random, long time
89
625 loops, best of 3: 13 us per loop
90
91
And support for ``CDF``::
92
93
sage: fc_wilk_rr = fast_callable(wilk, vars=[x], domain=CDF)
94
sage: timeit('fc_wilk_rr(30.0)') # random, long time
95
625 loops, best of 3: 10.2 us per loop
96
97
Currently, \function{fast_callable} can accept two kinds of objects:
98
polynomials (univariate and multivariate) and symbolic expressions
99
(elements of the Symbolic Ring). (This list is likely to grow
100
significantly in the near future.) For polynomials, you can omit the
101
'vars' argument; the variables will default to the ring generators (in
102
the order used when creating the ring). ::
103
104
sage: K.<x,y,z> = QQ[]
105
sage: p = 10*y + 100*z + x
106
sage: fp = fast_callable(p)
107
sage: fp(1,2,3)
108
321
109
110
But you can also specify the variable names to override the default
111
ordering (you can include extra variable names here, too). ::
112
113
sage: fp = fast_callable(p, vars=('x','w','z','y'))
114
115
For symbolic expressions, you need to specify the variable names, so
116
that \function{fast_callable} knows what order to use. ::
117
118
sage: var('y,z,x')
119
(y, z, x)
120
sage: f = 10*y + 100*z + x
121
sage: ff = fast_callable(f, vars=(x,y,z))
122
sage: ff(1,2,3)
123
321
124
125
You can also specify extra variable names::
126
127
sage: ff = fast_callable(f, vars=('x','w','z','y'))
128
sage: ff(1,2,3,4)
129
341
130
131
This should be enough for normal use of \function{fast_callable}; let's
132
discuss some more advanced topics.
133
134
Sometimes it may be useful to create a fast version of an expression
135
without going through symbolic expressions or polynomials; perhaps
136
because you want to describe to \function{fast_callable} an expression
137
with common subexpressions.
138
139
Internally, \function{fast_callable} works in two stages: it constructs
140
an expression tree from its argument, and then it builds a
141
fast evaluator from that expression tree. You can bypass the first phase
142
by building your own expression tree and passing that directly to
143
\function{fast_callable}, using an \class{ExpressionTreeBuilder}. ::
144
145
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
146
sage: etb = ExpressionTreeBuilder(vars=('x','y','z'))
147
148
An \class{ExpressionTreeBuilder} has three interesting methods:
149
\method{constant}, \method{var}, and \method{call}.
150
All of these methods return \class{ExpressionTree} objects.
151
152
The \method{var} method takes a string, and returns an expression tree
153
for the corresponding variable. ::
154
155
sage: x = etb.var('x')
156
sage: y = etb.var('y')
157
sage: z = etb.var('y')
158
159
Expression trees support Python's numeric operators, so you can easily
160
build expression trees representing arithmetic expressions. ::
161
162
sage: v1 = (x+y)*(y+z) + (y//z)
163
164
The \method{constant} method takes a \sage value, and returns an
165
expression tree representing that value. ::
166
167
sage: v2 = etb.constant(3.14159) * x + etb.constant(1729) * y
168
169
The \method{call} method takes a \sage/Python function and zero or more
170
expression trees, and returns an expression tree representing
171
the function call. ::
172
173
sage: v3 = etb.call(sin, v1+v2)
174
sage: v3
175
sin(add(add(mul(add(v_0, v_1), add(v_1, v_1)), floordiv(v_1, v_1)), add(mul(3.14159000000000, v_0), mul(1729, v_1))))
176
177
Many \sage/Python built-in functions are specially handled; for instance,
178
when evaluating an expression involving \function{sin} over \code{RDF},
179
the C math library function \function{sin} is called. Arbitrary functions
180
are allowed, but will be much slower since they will call back to
181
Python code on every call; for example, the following will work. ::
182
183
sage: def my_sqrt(x): return pow(x, 0.5)
184
sage: e = etb.call(my_sqrt, v1); e
185
{my_sqrt}(add(mul(add(v_0, v_1), add(v_1, v_1)), floordiv(v_1, v_1)))
186
sage: fast_callable(e)(1, 2, 3)
187
3.60555127546399
188
189
To provide \function{fast_callable} for your own class (so that
190
\code{fast_callable(x)} works when \variable{x} is an instance of your
191
class), implement a method \code{_fast_callable_(self, etb)} for your class.
192
This method takes an \class{ExpressionTreeBuilder}, and returns an
193
expression tree built up using the methods described above.
194
195
EXAMPLES::
196
197
sage: var('x')
198
x
199
sage: f = fast_callable(sqrt(x^7+1), vars=[x], domain=float)
200
201
sage: f(1)
202
1.4142135623730951
203
sage: f.op_list()
204
[('load_arg', 0), ('ipow', 7), ('load_const', 1.0), 'add', 'sqrt', 'return']
205
206
To interpret that last line, we load argument 0 ('x' in this case) onto
207
the stack, push the constant 7.0 onto the stack, call the pow function
208
(which takes 2 arguments from the stack), push the constant 1.0, add the
209
top two arguments of the stack, and then call sqrt.
210
211
Here we take sin of the first argument and add it to f::
212
213
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
214
sage: etb = ExpressionTreeBuilder('x')
215
sage: x = etb.var('x')
216
sage: f = etb.call(sqrt, x^7 + 1)
217
sage: g = etb.call(sin, x)
218
sage: fast_callable(f+g).op_list()
219
[('load_arg', 0), ('ipow', 7), ('load_const', 1), 'add', ('py_call', <function sqrt at ...>, 1), ('load_arg', 0), ('py_call', sin, 1), 'add', 'return']
220
221
222
AUTHOR:
223
-- Carl Witty (2009-02): initial version (heavily inspired by
224
Robert Bradshaw's fast_eval.pyx)
225
"""
226
227
228
#*****************************************************************************
229
# Copyright (C) 2008 Robert Bradshaw <[email protected]>
230
# Copyright (C) 2009 Carl Witty <[email protected]>
231
#
232
# Distributed under the terms of the GNU General Public License (GPL)
233
#
234
# This code is distributed in the hope that it will be useful,
235
# but WITHOUT ANY WARRANTY; without even the implied warranty of
236
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
237
# General Public License for more details.
238
#
239
# The full text of the GPL is available at:
240
#
241
# http://www.gnu.org/licenses/
242
#*****************************************************************************
243
244
# The following bits of text were written for the module docstring.
245
# They are not true yet, but I hope they will be true someday, at
246
# which point I will move them into the docstring.
247
#------------------------------ WRONG (for now) docs follow
248
# The final interesting method of \class{ExpressionTreeBuilder} is
249
# \method{choice}. This produces conditional expressions, like the C
250
# \code{COND ? T : F} expression or the Python {T if COND else F}.
251
# This lets you define piecewise functions using \function{fast_callable}.
252
253
# sage: v4 = etb.choice(v3 >= etb.constant(0), v1, v2)
254
255
# The arguments are \code{(COND, T, F)} (the same order as in C), so the
256
# above means that if \variable{v3} evaluates to a nonnegative number,
257
# then \variable{v4} will evaluate to the result of \variable{v1};
258
# otherwise, \variable{v4} will evaluate to the result of \variable{v2}.
259
260
# Let's see an example where we see that \function{fast_callable} does not
261
# evaluate common subexpressions more than once. We'll make a
262
# \function{fast_callable} expression that gives the result
263
# of 16 iterations of the Mandelbrot function.
264
265
# sage: etb = ExpressionTreeBuilder('c')
266
# sage: z = etb.constant(0)
267
# sage: c = etb.var('c')
268
# sage: for i in range(16):
269
# ... z = z*z + c
270
# sage: mand = fast_callable(z, domain=CDF) # not tested
271
272
# Now \variable{ff} does 32 complex arithmetic operations on each call
273
# (16 additions and 16 multiplications). However, if \code{z*z} produced
274
# code that evaluated \variable{z} twice, then this would do many
275
# thousands of arithmetic operations instead.
276
277
# Note that the handling for common subexpressions only checks whether
278
# expression trees are the same Python object; for instance, the following
279
# code will evaluate \code{x+1} twice:
280
281
# sage: etb = ExpressionTreeBuilder('x')
282
# sage: x = etb.var('x')
283
# sage: (x+1)*(x+1)
284
# *(+(v_0, 1), +(v_0, 1))
285
286
# but this code will only evaluate \code{x+1} once:
287
288
# sage: v = x+1; v*v
289
# *(+(v_0, 1), +(v_0, 1))
290
#------------------------------ done with WRONG (for now) docs
291
292
293
import operator
294
from copy import copy
295
from sage.rings.real_mpfr cimport RealField_class, RealNumber
296
from sage.structure.element cimport Element
297
from sage.rings.all import RDF, CDF
298
from sage.libs.mpfr cimport mpfr_t, mpfr_ptr, mpfr_init2, mpfr_set, GMP_RNDN
299
from sage.rings.integer import Integer
300
from sage.rings.integer_ring import ZZ
301
302
include "stdsage.pxi"
303
304
def fast_callable(x, domain=None, vars=None,
305
_autocompute_vars_for_backward_compatibility_with_deprecated_fast_float_functionality=False,
306
expect_one_var=False):
307
r"""
308
Given an expression x, compiles it into a form that can be quickly
309
evaluated, given values for the variables in x.
310
311
Currently, x can be an expression object, an element of SR, or a
312
(univariate or multivariate) polynomial; this list will probably
313
be extended soon.
314
315
By default, x is evaluated the same way that a Python function
316
would evaluate it -- addition maps to PyNumber_Add, etc. However,
317
you can specify domain=D where D is some Sage parent or Python
318
type; in this case, all arithmetic is done in that domain. If we
319
have a special-purpose interpreter for that parent (like RDF or float),
320
domain=... will trigger the use of that interpreter.
321
322
If vars is None and x is a polynomial, then we will use the
323
generators of parent(x) as the variables; otherwise, vars must be
324
specified (unless x is a symbolic expression with only one variable,
325
and expect_one_var is True, in which case we will use that variable).
326
327
EXAMPLES::
328
329
sage: var('x')
330
x
331
sage: expr = sin(x) + 3*x^2
332
sage: f = fast_callable(expr, vars=[x])
333
sage: f(2)
334
sin(2) + 12
335
sage: f(2.0)
336
12.9092974268257
337
338
We have special fast interpreters for domain=float and domain=RDF.
339
(Actually it's the same interpreter; only the return type varies.)
340
Note that the float interpreter is not actually more accurate than
341
the RDF interpreter; elements of RDF just don't display all
342
their digits. We have special fast interpreter for domain=CDF.
343
344
sage: f_float = fast_callable(expr, vars=[x], domain=float)
345
sage: f_float(2)
346
12.909297426825681
347
sage: f_rdf = fast_callable(expr, vars=[x], domain=RDF)
348
sage: f_rdf(2)
349
12.9092974268
350
sage: f_cdf = fast_callable(expr, vars=[x], domain=CDF)
351
sage: f_cdf(2)
352
12.9092974268
353
sage: f_cdf(2+I)
354
10.4031192506 + 11.510943741*I
355
sage: f = fast_callable(expr, vars=('z','x','y'))
356
sage: f(1, 2, 3)
357
sin(2) + 12
358
sage: K.<x> = QQ[]
359
sage: p = K.random_element(6); p
360
-x^6 - 12*x^5 + 1/2*x^4 - 1/95*x^3 - 1/2*x^2 - 4
361
sage: fp = fast_callable(p, domain=RDF)
362
sage: fp.op_list()
363
[('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']
364
sage: fp(3.14159)
365
-4594.16182364
366
sage: K.<x,y,z> = QQ[]
367
sage: p = K.random_element(degree=3, terms=5); p
368
-x*y^2 - x*z^2 - 6*x^2 - y^2 - 3*x*z
369
sage: fp = fast_callable(p, domain=RDF)
370
sage: fp.op_list()
371
[('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']
372
sage: fp(e, pi, sqrt(2))
373
-98.0015640336
374
sage: symbolic_result = p(e, pi, sqrt(2)); symbolic_result
375
-pi^2*e - pi^2 - 3*sqrt(2)*e - 6*e^2 - 2*e
376
sage: n(symbolic_result)
377
-98.0015640336293
378
379
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
380
sage: etb = ExpressionTreeBuilder(vars=('x','y'), domain=float)
381
sage: x = etb.var('x')
382
sage: y = etb.var('y')
383
sage: expr = etb.call(sin, x^2 + y); expr
384
sin(add(ipow(v_0, 2), v_1))
385
sage: fc = fast_callable(expr, domain=float)
386
sage: fc(5, 7)
387
0.5514266812416906
388
"""
389
cdef Expression et
390
if isinstance(x, Expression):
391
et = x
392
vars = et._etb._vars
393
else:
394
if vars is None or len(vars) == 0:
395
from sage.symbolic.ring import SR
396
from sage.symbolic.callable import is_CallableSymbolicExpressionRing
397
from sage.symbolic.expression import is_Expression
398
399
# XXX This is pretty gross... there should be a "callable_variables"
400
# method that does all this.
401
vars = x.variables()
402
if x.parent() is SR and x.number_of_arguments() > len(vars):
403
vars = list(vars) + ['EXTRA_VAR%d' % n for n in range(len(vars), x.number_of_arguments())]
404
405
# Failing to specify the variables is deprecated for any
406
# symbolic expression, except for PrimitiveFunction and
407
# CallableSymbolicExpression.
408
if is_Expression(x) and not is_CallableSymbolicExpressionRing(x.parent()):
409
if expect_one_var and len(vars) <= 1:
410
if len(vars) == 0:
411
vars = ['EXTRA_VAR0']
412
else:
413
if _autocompute_vars_for_backward_compatibility_with_deprecated_fast_float_functionality:
414
from sage.misc.superseded import deprecation
415
deprecation(5413, "Substitution using function-call syntax and unnamed arguments is deprecated and will be removed from a future release of Sage; you can use named arguments instead, like EXPR(x=..., y=...)")
416
else:
417
raise ValueError, "List of variables must be specified for symbolic expressions"
418
from sage.rings.polynomial.polynomial_ring import is_PolynomialRing
419
from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing
420
if is_PolynomialRing(x.parent()) or is_MPolynomialRing(x.parent()):
421
vars = x.parent().variable_names()
422
etb = ExpressionTreeBuilder(vars=vars, domain=domain)
423
et = x._fast_callable_(etb)
424
if isinstance(domain, RealField_class):
425
import sage.ext.interpreters.wrapper_rr
426
builder = sage.ext.interpreters.wrapper_rr.Wrapper_rr
427
428
str = InstructionStream(sage.ext.interpreters.wrapper_rr.metadata,
429
len(vars),
430
domain)
431
elif domain == RDF or domain is float:
432
import sage.ext.interpreters.wrapper_rdf
433
builder = sage.ext.interpreters.wrapper_rdf.Wrapper_rdf
434
str = InstructionStream(sage.ext.interpreters.wrapper_rdf.metadata,
435
len(vars),
436
domain)
437
elif domain == CDF:
438
import sage.ext.interpreters.wrapper_cdf
439
builder = sage.ext.interpreters.wrapper_cdf.Wrapper_cdf
440
str = InstructionStream(sage.ext.interpreters.wrapper_cdf.metadata,
441
len(vars),
442
domain)
443
elif domain is None:
444
import sage.ext.interpreters.wrapper_py
445
builder = sage.ext.interpreters.wrapper_py.Wrapper_py
446
str = InstructionStream(sage.ext.interpreters.wrapper_py.metadata,
447
len(vars))
448
else:
449
import sage.ext.interpreters.wrapper_el
450
builder = sage.ext.interpreters.wrapper_el.Wrapper_el
451
str = InstructionStream(sage.ext.interpreters.wrapper_el.metadata,
452
len(vars),
453
domain)
454
generate_code(et, str)
455
str.instr('return')
456
return builder(str.get_current())
457
458
def function_name(fn):
459
r"""
460
Given a function, returns a string giving a name for the function.
461
462
For functions we recognize, we use our standard opcode name for the
463
function (so operator.add becomes 'add', and sage.all.sin becomes 'sin').
464
465
For functions we don't recognize, we try to come up with a name,
466
but the name will be wrapped in braces; this is a signal that
467
we'll definitely use a slow Python call to call this function.
468
(We may use a slow Python call even for functions we do recognize,
469
if we're targeting an interpreter without an opcode for the function.)
470
471
Only used when printing Expressions.
472
473
EXAMPLES::
474
475
sage: from sage.ext.fast_callable import function_name
476
sage: function_name(operator.pow)
477
'pow'
478
sage: function_name(cos)
479
'cos'
480
sage: function_name(factorial)
481
'{factorial}'
482
"""
483
builtins = get_builtin_functions()
484
if fn in builtins:
485
return builtins[fn]
486
try:
487
return "{%s}" % fn.__name__
488
except AttributeError:
489
return "{%r}" % fn
490
491
cdef class ExpressionTreeBuilder:
492
r"""
493
A class with helper methods for building Expressions.
494
495
An instance of this class is passed to _fast_callable_ methods;
496
you can also instantiate it yourself to create your own expressions
497
for fast_callable, bypassing _fast_callable_.
498
499
EXAMPLES::
500
501
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
502
sage: etb = ExpressionTreeBuilder('x')
503
sage: x = etb.var('x')
504
sage: (x+3)*5
505
mul(add(v_0, 3), 5)
506
"""
507
508
cdef readonly object _domain
509
cdef readonly object _vars
510
511
def __init__(self, vars, domain=None):
512
r"""
513
Initialize an instance of ExpressionTreeBuilder. Takes
514
a list or tuple of variable names to use, and also an optional
515
domain. If a domain is given, then creating an ExpressionConstant
516
node with the __call__, make, or constant methods will convert
517
the value into the given domain.
518
519
Note that this is the only effect of the domain parameter. It
520
is quite possible to use different domains for
521
ExpressionTreeBuilder and for fast_callable; in that case,
522
constants will be converted twice (once when building the
523
Expression, and once when generating code).
524
525
EXAMPLES::
526
527
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
528
sage: etb = ExpressionTreeBuilder('x')
529
sage: etb(3^50)
530
717897987691852588770249
531
sage: etb = ExpressionTreeBuilder('x', domain=RR)
532
sage: etb(3^50)
533
7.17897987691853e23
534
"""
535
536
if isinstance(vars, tuple):
537
vars = list(vars)
538
elif not isinstance(vars, list):
539
vars = [vars]
540
541
vars = map(self._clean_var, vars)
542
543
self._domain = domain
544
self._vars = vars
545
546
def __call__(self, x):
547
r"""
548
Try to convert the given value to an Expression. If it is already
549
an Expression, just return it. If it has a _fast_callable_
550
method, then call the method with self as an argument. Otherwise,
551
use self.constant() to turn it into a constant.
552
553
EXAMPLES::
554
555
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
556
sage: etb = ExpressionTreeBuilder('x')
557
sage: v = etb(3); v, type(v)
558
(3, <type 'sage.ext.fast_callable.ExpressionConstant'>)
559
sage: v = etb(polygen(QQ)); v, type(v)
560
(v_0, <type 'sage.ext.fast_callable.ExpressionVariable'>)
561
sage: v is etb(v)
562
True
563
"""
564
if isinstance(x, Expression):
565
return x
566
567
try:
568
fc = x._fast_callable_
569
except AttributeError:
570
return self.constant(x)
571
572
return fc(self)
573
574
def _clean_var(self, v):
575
r"""
576
Give a variable name, given a variable.
577
578
EXAMPLES::
579
580
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
581
sage: etb = ExpressionTreeBuilder('x')
582
sage: var('x')
583
x
584
sage: etb._clean_var(x)
585
'x'
586
sage: x = polygen(RR); x
587
x
588
sage: etb._clean_var(x)
589
'x'
590
"""
591
# There should be a better way to do this. (Maybe there is.)
592
if not PY_TYPE_CHECK(v, str):
593
v = str(v)
594
if '*' in v:
595
v = v[v.index('*')+1:]
596
return v
597
598
def constant(self, c):
599
r"""
600
Turn the argument into an ExpressionConstant, converting it to
601
our domain if we have one.
602
603
EXAMPLES::
604
605
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
606
sage: etb = ExpressionTreeBuilder('x')
607
sage: etb.constant(pi)
608
pi
609
sage: etb = ExpressionTreeBuilder('x', domain=RealField(200))
610
sage: etb.constant(pi)
611
3.1415926535897932384626433832795028841971693993751058209749
612
"""
613
if self._domain is not None:
614
c = self._domain(c)
615
return ExpressionConstant(self, c)
616
617
def var(self, v):
618
r"""
619
Turn the argument into an ExpressionVariable. Looks it up in
620
the list of variables. (Variables are matched by name.)
621
622
EXAMPLES::
623
624
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
625
sage: var('a,b,some_really_long_name')
626
(a, b, some_really_long_name)
627
sage: x = polygen(QQ)
628
sage: etb = ExpressionTreeBuilder(vars=('a','b',some_really_long_name, x))
629
sage: etb.var(some_really_long_name)
630
v_2
631
sage: etb.var('some_really_long_name')
632
v_2
633
sage: etb.var(x)
634
v_3
635
sage: etb.var('y')
636
Traceback (most recent call last):
637
...
638
ValueError: Variable 'y' not found
639
"""
640
var_name = self._clean_var(v)
641
try:
642
ind = self._vars.index(var_name)
643
except ValueError:
644
raise ValueError, "Variable '%s' not found" % var_name
645
return ExpressionVariable(self, ind)
646
647
def _var_number(self, n):
648
r"""
649
Given an integer, return the variable with that index.
650
651
EXAMPLES::
652
653
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
654
sage: etb = ExpressionTreeBuilder(vars=('a','b','c','d'))
655
sage: etb._var_number(0)
656
v_0
657
sage: etb._var_number(3)
658
v_3
659
sage: etb._var_number(4)
660
Traceback (most recent call last):
661
...
662
ValueError: Variable number 4 out of range
663
"""
664
if 0 <= n < len(self._vars):
665
return ExpressionVariable(self, n)
666
raise ValueError, "Variable number %d out of range" % n
667
668
def call(self, fn, *args):
669
r"""
670
Construct a call node, given a function and a list of arguments.
671
The arguments will be converted to Expressions using
672
ExpressionTreeBuilder.__call__.
673
674
As a special case, notices if the function is operator.pow and
675
the second argument is integral, and constructs an ExpressionIPow
676
instead.
677
678
EXAMPLES::
679
680
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
681
sage: etb = ExpressionTreeBuilder(vars=(x,))
682
sage: etb.call(cos, x)
683
cos(v_0)
684
sage: etb.call(sin, 1)
685
sin(1)
686
sage: etb.call(sin, etb(1))
687
sin(1)
688
sage: etb.call(factorial, x+57)
689
{factorial}(add(v_0, 57))
690
sage: etb.call(operator.pow, x, 543)
691
ipow(v_0, 543)
692
"""
693
if fn is operator.pow:
694
base, exponent = args
695
return self(base)**exponent
696
else:
697
return ExpressionCall(self, fn, map(self, args))
698
699
def choice(self, cond, iftrue, iffalse):
700
r"""
701
Construct a choice node (a conditional expression), given the
702
condition, and the values for the true and false cases.
703
704
(It's possible to create choice nodes, but they don't work yet.)
705
706
EXAMPLES::
707
708
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
709
sage: etb = ExpressionTreeBuilder(vars=(x,))
710
sage: etb.choice(etb.call(operator.eq, x, 0), 0, 1/x)
711
(0 if {eq}(v_0, 0) else div(1, v_0))
712
"""
713
return ExpressionChoice(self,
714
cond,
715
self(iftrue),
716
self(iffalse))
717
718
# Cache these values, to make expression building a tiny bit faster
719
# (by skipping the hash-table lookup in the operator module).
720
cdef op_add = operator.add
721
cdef op_sub = operator.sub
722
cdef op_mul = operator.mul
723
cdef op_div = operator.div
724
cdef op_floordiv = operator.floordiv
725
cdef op_pow = operator.pow
726
cdef op_neg = operator.neg
727
cdef op_abs = operator.abs
728
cdef op_inv = operator.inv
729
730
cdef class Expression:
731
r"""
732
Represents an expression for fast_callable.
733
734
Supports the standard Python arithmetic operators; if arithmetic
735
is attempted between an Expression and a non-Expression, the
736
non-Expression is converted to an expression (using the
737
__call__ method of the Expression's ExpressionTreeBuilder).
738
739
EXAMPLES::
740
741
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
742
sage: etb = ExpressionTreeBuilder(vars=(x,))
743
sage: x = etb.var(x)
744
sage: etb(x)
745
v_0
746
sage: etb(3)
747
3
748
sage: etb.call(sin, x)
749
sin(v_0)
750
sage: (x+1)/(x-1)
751
div(add(v_0, 1), sub(v_0, 1))
752
sage: x//5
753
floordiv(v_0, 5)
754
sage: -abs(~x)
755
neg(abs(inv(v_0)))
756
"""
757
758
cdef ExpressionTreeBuilder _etb
759
760
def __init__(self, etb):
761
r"""
762
Initialize an Expression. Sets the _etb member.
763
764
EXAMPLES::
765
766
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
767
sage: etb = ExpressionTreeBuilder(vars=(x,))
768
sage: v = etb(3); v # indirect doctest
769
3
770
sage: v._get_etb() is etb
771
True
772
"""
773
self._etb = etb
774
775
def _get_etb(self):
776
r"""
777
Returns the ExpressionTreeBuilder used to build a given expression.
778
779
EXAMPLES::
780
781
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
782
sage: etb = ExpressionTreeBuilder(vars=(x,))
783
sage: v = etb(3); v
784
3
785
sage: v._get_etb() is etb
786
True
787
"""
788
return self._etb
789
790
def __add__(s, o):
791
r"""
792
Compute a sum of two Expressions.
793
794
EXAMPLES::
795
796
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
797
sage: etb = ExpressionTreeBuilder(vars=(x,))
798
sage: x = etb(x)
799
sage: x+x
800
add(v_0, v_0)
801
sage: x+1
802
add(v_0, 1)
803
sage: 1+x
804
add(1, v_0)
805
sage: x.__add__(1)
806
add(v_0, 1)
807
sage: x.__radd__(1)
808
add(1, v_0)
809
"""
810
return _expression_binop_helper(s, o, op_add)
811
812
def __sub__(s, o):
813
r"""
814
Compute a difference of two Expressions.
815
816
EXAMPLES::
817
818
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
819
sage: etb = ExpressionTreeBuilder(vars=(x,))
820
sage: x = etb(x)
821
sage: x-x
822
sub(v_0, v_0)
823
sage: x-1
824
sub(v_0, 1)
825
sage: 1-x
826
sub(1, v_0)
827
sage: x.__sub__(1)
828
sub(v_0, 1)
829
sage: x.__rsub__(1)
830
sub(1, v_0)
831
"""
832
return _expression_binop_helper(s, o, op_sub)
833
834
def __mul__(s, o):
835
r"""
836
Compute a product of two Expressions.
837
838
EXAMPLES::
839
840
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
841
sage: etb = ExpressionTreeBuilder(vars=(x,))
842
sage: x = etb(x)
843
sage: x*x
844
mul(v_0, v_0)
845
sage: x*1
846
mul(v_0, 1)
847
sage: 1*x
848
mul(1, v_0)
849
sage: x.__mul__(1)
850
mul(v_0, 1)
851
sage: x.__rmul__(1)
852
mul(1, v_0)
853
"""
854
return _expression_binop_helper(s, o, op_mul)
855
856
def __div__(s, o):
857
r"""
858
Compute a quotient of two Expressions.
859
860
EXAMPLES::
861
862
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
863
sage: etb = ExpressionTreeBuilder(vars=(x,))
864
sage: x = etb(x)
865
sage: x/x
866
div(v_0, v_0)
867
sage: x/1
868
div(v_0, 1)
869
sage: 1/x
870
div(1, v_0)
871
sage: x.__div__(1)
872
div(v_0, 1)
873
sage: x.__rdiv__(1)
874
div(1, v_0)
875
"""
876
return _expression_binop_helper(s, o, op_div)
877
878
def __floordiv__(s, o):
879
r"""
880
Compute the floordiv (the floor of the quotient) of two Expressions.
881
882
EXAMPLES::
883
884
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
885
sage: etb = ExpressionTreeBuilder(vars=(x,))
886
sage: x = etb(x)
887
sage: x//x
888
floordiv(v_0, v_0)
889
sage: x//1
890
floordiv(v_0, 1)
891
sage: 1//x
892
floordiv(1, v_0)
893
sage: x.__floordiv__(1)
894
floordiv(v_0, 1)
895
sage: x.__rfloordiv__(1)
896
floordiv(1, v_0)
897
"""
898
return _expression_binop_helper(s, o, op_floordiv)
899
900
def __pow__(s, o, dummy):
901
r"""
902
Compute a power expression from two Expressions.
903
904
If the second Expression is a constant integer, then return
905
an ExpressionIPow instead of an ExpressionCall.
906
907
EXAMPLES::
908
909
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
910
sage: etb = ExpressionTreeBuilder(vars=(x,))
911
sage: x = etb(x)
912
sage: x^x
913
pow(v_0, v_0)
914
sage: x^1
915
ipow(v_0, 1)
916
sage: x.__pow__(1)
917
ipow(v_0, 1)
918
sage: x.__pow__(1.0)
919
pow(v_0, 1.00000000000000)
920
sage: x.__rpow__(1)
921
pow(1, v_0)
922
"""
923
# XXX There is a performance regression from the original
924
# fast_float here; it would replace small integer powers with
925
# multiplication. We can't do this safely until we support
926
# common subexpression elimination (or at least the dup instruction).
927
# (Plus, we should consider how strict a semantics we want;
928
# probably this sort of optimization should be controlled by a
929
# flag.)
930
931
cdef Expression es
932
if isinstance(o, (int, long, Integer)):
933
es = s
934
return ExpressionIPow(es._etb, s, o)
935
else:
936
# I really don't like this, but I can't think of a better way
937
from sage.symbolic.expression import is_Expression
938
if is_Expression(o) and o in ZZ:
939
es = s
940
return ExpressionIPow(es._etb, s, ZZ(o))
941
else:
942
return _expression_binop_helper(s, o, op_pow)
943
944
def __neg__(self):
945
r"""
946
Compute the negation of an Expression.
947
948
EXAMPLES::
949
950
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
951
sage: etb = ExpressionTreeBuilder(vars=(x,))
952
sage: x = etb(x)
953
sage: -x
954
neg(v_0)
955
sage: x.__neg__()
956
neg(v_0)
957
"""
958
return ExpressionCall(self._etb, op_neg, [self])
959
960
def __abs__(self):
961
r"""
962
Compute the absolute value of an Expression.
963
964
EXAMPLES::
965
966
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
967
sage: etb = ExpressionTreeBuilder(vars=(x,))
968
sage: x = etb(x)
969
sage: abs(x)
970
abs(v_0)
971
sage: x.abs()
972
abs(v_0)
973
sage: x.__abs__()
974
abs(v_0)
975
"""
976
return ExpressionCall(self._etb, op_abs, [self])
977
978
def abs(self):
979
r"""
980
Compute the absolute value of an Expression.
981
982
EXAMPLES::
983
984
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
985
sage: etb = ExpressionTreeBuilder(vars=(x,))
986
sage: x = etb(x)
987
sage: abs(x)
988
abs(v_0)
989
sage: x.abs()
990
abs(v_0)
991
sage: x.__abs__()
992
abs(v_0)
993
"""
994
return ExpressionCall(self._etb, op_abs, [self])
995
996
def __invert__(self):
997
r"""
998
Compute the inverse of an Expression.
999
1000
EXAMPLES::
1001
1002
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1003
sage: etb = ExpressionTreeBuilder(vars=(x,))
1004
sage: x = etb(x)
1005
sage: ~x
1006
inv(v_0)
1007
sage: x.__invert__()
1008
inv(v_0)
1009
"""
1010
return ExpressionCall(self._etb, op_inv, [self])
1011
1012
1013
cdef class ExpressionConstant(Expression):
1014
r"""
1015
An Expression that represents an arbitrary constant.
1016
1017
EXAMPLES:
1018
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1019
sage: etb = ExpressionTreeBuilder(vars=(x,))
1020
sage: type(etb(3))
1021
<type 'sage.ext.fast_callable.ExpressionConstant'>
1022
"""
1023
1024
cdef object _value
1025
1026
def __init__(self, etb, c):
1027
r"""
1028
Initialize an ExpressionConstant.
1029
1030
EXAMPLES::
1031
1032
sage: from sage.ext.fast_callable import ExpressionTreeBuilder, ExpressionConstant
1033
sage: etb = ExpressionTreeBuilder(vars=(x,))
1034
sage: etb(3)
1035
3
1036
sage: v = ExpressionConstant(etb, 3); v
1037
3
1038
sage: v._get_etb() is etb
1039
True
1040
sage: v.value()
1041
3
1042
sage: v.value() == 3
1043
True
1044
"""
1045
Expression.__init__(self, etb)
1046
self._value = c
1047
1048
def value(self):
1049
r"""
1050
Return the constant value of an ExpressionConstant.
1051
1052
EXAMPLES::
1053
1054
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1055
sage: etb = ExpressionTreeBuilder(vars=(x,))
1056
sage: etb(3).value()
1057
3
1058
"""
1059
return self._value
1060
1061
def __repr__(self):
1062
r"""
1063
Give a string representing this ExpressionConstant.
1064
(We use the repr of its value.)
1065
1066
EXAMPLES::
1067
1068
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1069
sage: etb = ExpressionTreeBuilder(vars=(x,))
1070
sage: v = etb.constant(pi)
1071
sage: v
1072
pi
1073
sage: repr(v)
1074
'pi'
1075
sage: v.__repr__()
1076
'pi'
1077
"""
1078
return repr(self._value)
1079
1080
cdef class ExpressionVariable(Expression):
1081
r"""
1082
An Expression that represents a variable.
1083
1084
EXAMPLES:
1085
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1086
sage: etb = ExpressionTreeBuilder(vars=(x,))
1087
sage: type(etb.var(x))
1088
<type 'sage.ext.fast_callable.ExpressionVariable'>
1089
"""
1090
cdef int _variable_index
1091
1092
def __init__(self, etb, int n):
1093
r"""
1094
Initialize an ExpressionVariable.
1095
1096
EXAMPLES::
1097
1098
sage: from sage.ext.fast_callable import ExpressionTreeBuilder, ExpressionVariable
1099
sage: etb = ExpressionTreeBuilder(vars=(x,))
1100
sage: etb(x)
1101
v_0
1102
sage: v = ExpressionVariable(etb, 0); v
1103
v_0
1104
sage: v._get_etb() is etb
1105
True
1106
sage: v.variable_index()
1107
0
1108
"""
1109
Expression.__init__(self, etb)
1110
self._variable_index = n
1111
1112
def variable_index(self):
1113
r"""
1114
Return the variable index of an ExpressionVariable.
1115
1116
EXAMPLES::
1117
1118
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1119
sage: etb = ExpressionTreeBuilder(vars=(x,))
1120
sage: etb(x).variable_index()
1121
0
1122
"""
1123
return self._variable_index
1124
1125
def __repr__(self):
1126
r"""
1127
Give a string representing this ExpressionVariable.
1128
1129
EXAMPLES::
1130
1131
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1132
sage: etb = ExpressionTreeBuilder(vars=(x,))
1133
sage: v = etb._var_number(0)
1134
sage: v
1135
v_0
1136
sage: repr(v)
1137
'v_0'
1138
sage: v.__repr__()
1139
'v_0'
1140
"""
1141
# Should we look up the variable name in self._etb, instead?
1142
# I think not.. I like the emphasis that we're totally removed
1143
# from the original expression when we have an Expression.
1144
return "v_%d" % self._variable_index
1145
1146
cdef class ExpressionCall(Expression):
1147
r"""
1148
An Expression that represents a function call.
1149
1150
EXAMPLES:
1151
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1152
sage: etb = ExpressionTreeBuilder(vars=(x,))
1153
sage: type(etb.call(sin, x))
1154
<type 'sage.ext.fast_callable.ExpressionCall'>
1155
"""
1156
cdef object _function
1157
cdef object _arguments
1158
1159
def __init__(self, etb, fn, args):
1160
r"""
1161
Initialize an ExpressionCall.
1162
1163
EXAMPLES::
1164
1165
sage: from sage.ext.fast_callable import ExpressionTreeBuilder, ExpressionCall
1166
sage: etb = ExpressionTreeBuilder(vars=(x,))
1167
sage: x = etb(x)
1168
sage: etb.call(factorial, x)
1169
{factorial}(v_0)
1170
sage: v = ExpressionCall(etb, factorial, [x]); v
1171
{factorial}(v_0)
1172
sage: v._get_etb() is etb
1173
True
1174
sage: v.function()
1175
factorial
1176
sage: v.arguments()
1177
[v_0]
1178
"""
1179
Expression.__init__(self, etb)
1180
self._function = fn
1181
self._arguments = args
1182
1183
def function(self):
1184
r"""
1185
Return the function from this ExpressionCall.
1186
1187
EXAMPLES::
1188
1189
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1190
sage: etb = ExpressionTreeBuilder(vars=(x,))
1191
sage: etb.call(sin, x).function()
1192
sin
1193
"""
1194
return self._function
1195
1196
def arguments(self):
1197
r"""
1198
Return the arguments from this ExpressionCall.
1199
1200
EXAMPLES::
1201
1202
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1203
sage: etb = ExpressionTreeBuilder(vars=(x,))
1204
sage: etb.call(sin, x).arguments()
1205
[v_0]
1206
"""
1207
return copy(self._arguments)
1208
1209
def __repr__(self):
1210
r"""
1211
Give a string representing this ExpressionCall.
1212
1213
EXAMPLES::
1214
1215
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1216
sage: etb = ExpressionTreeBuilder(vars=(x,))
1217
sage: x = etb.var(x)
1218
sage: etb.call(operator.add, x, 1)
1219
add(v_0, 1)
1220
sage: etb.call(factorial, x)
1221
{factorial}(v_0)
1222
sage: v = etb.call(sin, x)
1223
sage: v
1224
sin(v_0)
1225
sage: repr(v)
1226
'sin(v_0)'
1227
sage: v.__repr__()
1228
'sin(v_0)'
1229
"""
1230
fn = function_name(self._function)
1231
return '%s(%s)' % (fn, ', '.join(map(repr, self._arguments)))
1232
1233
cdef class ExpressionIPow(Expression):
1234
r"""
1235
A power Expression with an integer exponent.
1236
1237
EXAMPLES:
1238
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1239
sage: etb = ExpressionTreeBuilder(vars=(x,))
1240
sage: type(etb.var('x')^17)
1241
<type 'sage.ext.fast_callable.ExpressionIPow'>
1242
"""
1243
cdef object _base
1244
cdef object _exponent
1245
1246
def __init__(self, etb, base, exponent):
1247
r"""
1248
Initialize an ExpressionIPow.
1249
1250
EXAMPLES::
1251
1252
sage: from sage.ext.fast_callable import ExpressionTreeBuilder, ExpressionIPow
1253
sage: etb = ExpressionTreeBuilder(vars=(x,))
1254
sage: x = etb(x)
1255
sage: x^(-12)
1256
ipow(v_0, -12)
1257
sage: v = ExpressionIPow(etb, x, 55); v
1258
ipow(v_0, 55)
1259
sage: v._get_etb() is etb
1260
True
1261
sage: v.base()
1262
v_0
1263
sage: v.exponent()
1264
55
1265
"""
1266
Expression.__init__(self, etb)
1267
self._base = base
1268
self._exponent = exponent
1269
1270
def base(self):
1271
r"""
1272
Return the base from this ExpressionIPow.
1273
1274
EXAMPLES::
1275
1276
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1277
sage: etb = ExpressionTreeBuilder(vars=(x,))
1278
sage: (etb(33)^42).base()
1279
33
1280
"""
1281
return self._base
1282
1283
def exponent(self):
1284
r"""
1285
Return the exponent from this ExpressionIPow.
1286
1287
EXAMPLES::
1288
1289
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1290
sage: etb = ExpressionTreeBuilder(vars=(x,))
1291
sage: (etb(x)^(-1)).exponent()
1292
-1
1293
"""
1294
return self._exponent
1295
1296
def __repr__(self):
1297
r"""
1298
Give a string representing this ExpressionIPow.
1299
1300
EXAMPLES::
1301
1302
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1303
sage: etb = ExpressionTreeBuilder(vars=(x,))
1304
sage: x = etb.var(x)
1305
sage: x^3
1306
ipow(v_0, 3)
1307
sage: x^(-2)
1308
ipow(v_0, -2)
1309
sage: v = (x+1)^3
1310
sage: v
1311
ipow(add(v_0, 1), 3)
1312
sage: repr(v)
1313
'ipow(add(v_0, 1), 3)'
1314
sage: v.__repr__()
1315
'ipow(add(v_0, 1), 3)'
1316
"""
1317
return 'ipow(%s, %d)' % (repr(self._base), self._exponent)
1318
1319
cdef class ExpressionChoice(Expression):
1320
r"""
1321
A conditional expression.
1322
1323
(It's possible to create choice nodes, but they don't work yet.)
1324
1325
EXAMPLES:
1326
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1327
sage: etb = ExpressionTreeBuilder(vars=(x,))
1328
sage: etb.choice(etb.call(operator.eq, x, 0), 0, 1/x)
1329
(0 if {eq}(v_0, 0) else div(1, v_0))
1330
"""
1331
1332
cdef object _cond
1333
cdef object _iftrue
1334
cdef object _iffalse
1335
1336
def __init__(self, etb, cond, iftrue, iffalse):
1337
r"""
1338
Initialize an ExpressionChoice.
1339
1340
EXAMPLES::
1341
1342
sage: from sage.ext.fast_callable import ExpressionTreeBuilder, ExpressionChoice
1343
sage: etb = ExpressionTreeBuilder(vars=(x,))
1344
sage: x = etb(x)
1345
sage: etb.choice(x, ~x, 0)
1346
(inv(v_0) if v_0 else 0)
1347
sage: v = ExpressionChoice(etb, x, ~x, etb(0)); v
1348
(inv(v_0) if v_0 else 0)
1349
sage: v._get_etb() is etb
1350
True
1351
sage: v.condition()
1352
v_0
1353
sage: v.if_true()
1354
inv(v_0)
1355
sage: v.if_false()
1356
0
1357
"""
1358
Expression.__init__(self, etb)
1359
self._cond = cond
1360
self._iftrue = iftrue
1361
self._iffalse = iffalse
1362
1363
def condition(self):
1364
r"""
1365
Return the condition of an ExpressionChoice.
1366
1367
EXAMPLES::
1368
1369
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1370
sage: etb = ExpressionTreeBuilder(vars=(x,))
1371
sage: x = etb(x)
1372
sage: etb.choice(x, ~x, 0).condition()
1373
v_0
1374
"""
1375
return self._cond
1376
1377
def if_true(self):
1378
r"""
1379
Return the true branch of an ExpressionChoice.
1380
1381
EXAMPLES::
1382
1383
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1384
sage: etb = ExpressionTreeBuilder(vars=(x,))
1385
sage: x = etb(x)
1386
sage: etb.choice(x, ~x, 0).if_true()
1387
inv(v_0)
1388
"""
1389
return self._iftrue
1390
1391
def if_false(self):
1392
r"""
1393
Return the false branch of an ExpressionChoice.
1394
1395
EXAMPLES::
1396
1397
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1398
sage: etb = ExpressionTreeBuilder(vars=(x,))
1399
sage: x = etb(x)
1400
sage: etb.choice(x, ~x, 0).if_false()
1401
0
1402
"""
1403
return self._iffalse
1404
1405
def __repr__(self):
1406
r"""
1407
Give a string representation for this ExpressionChoice.
1408
(Based on the syntax for Python conditional expressions.)
1409
1410
EXAMPLES::
1411
1412
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
1413
sage: etb = ExpressionTreeBuilder(vars=(x,))
1414
sage: x = etb(x)
1415
sage: v = etb.choice(x, ~x, 0)
1416
sage: v
1417
(inv(v_0) if v_0 else 0)
1418
sage: repr(v)
1419
'(inv(v_0) if v_0 else 0)'
1420
sage: v.__repr__()
1421
'(inv(v_0) if v_0 else 0)'
1422
"""
1423
return '(%s if %s else %s)' % (repr(self._iftrue),
1424
repr(self._cond),
1425
repr(self._iffalse))
1426
1427
cpdef _expression_binop_helper(s, o, op):
1428
r"""
1429
Makes an Expression for (s op o). Either s or o (or both) must already
1430
be an expression.
1431
1432
EXAMPLES:
1433
sage: from sage.ext.fast_callable import _expression_binop_helper, ExpressionTreeBuilder
1434
sage: var('x,y')
1435
(x, y)
1436
sage: etb = ExpressionTreeBuilder(vars=(x,y))
1437
sage: x = etb(x)
1438
1439
Now x is an Expression, but y is not. Still, all the following
1440
cases work.
1441
sage: _expression_binop_helper(x, x, operator.add)
1442
add(v_0, v_0)
1443
sage: _expression_binop_helper(x, y, operator.add)
1444
add(v_0, v_1)
1445
sage: _expression_binop_helper(y, x, operator.add)
1446
add(v_1, v_0)
1447
"""
1448
# The Cython way of handling operator overloading on cdef classes
1449
# (which is inherited from Python) is quite annoying. Inside the
1450
# code for a binary operator, you know that either the first or
1451
# second argument (or both) is a member of your class, but you
1452
# don't know which.
1453
1454
# If there is an arithmetic operator between an Expression and
1455
# a non-Expression, I want to convert the non-Expression into
1456
# an Expression. But to do that, I need the ExpressionTreeBuilder
1457
# from the Expression.
1458
1459
cdef Expression self
1460
cdef Expression other
1461
1462
if not isinstance(o, Expression):
1463
self = s
1464
other = self._etb(o)
1465
elif not isinstance(s, Expression):
1466
other = o
1467
self = other._etb(s)
1468
else:
1469
self = s
1470
other = o
1471
assert self._etb is other._etb
1472
1473
return ExpressionCall(self._etb, op, [self, other])
1474
1475
class IntegerPowerFunction(object):
1476
r"""
1477
This class represents the function x^n for an arbitrary integral
1478
power n. That is, IntegerPowerFunction(2) is the squaring function;
1479
IntegerPowerFunction(-1) is the reciprocal function.
1480
1481
EXAMPLES:
1482
sage: from sage.ext.fast_callable import IntegerPowerFunction
1483
sage: square = IntegerPowerFunction(2)
1484
sage: square
1485
(^2)
1486
sage: square(pi)
1487
pi^2
1488
sage: square(I)
1489
-1
1490
sage: square(RIF(-1, 1)).str(style='brackets')
1491
'[0.00000000000000000 .. 1.0000000000000000]'
1492
sage: IntegerPowerFunction(-1)
1493
(^(-1))
1494
sage: IntegerPowerFunction(-1)(22/7)
1495
7/22
1496
sage: v = Integers(123456789)(54321)
1497
sage: v^9876543210
1498
79745229
1499
sage: IntegerPowerFunction(9876543210)(v)
1500
79745229
1501
"""
1502
1503
def __init__(self, n):
1504
r"""
1505
Initializes an IntegerPowerFunction.
1506
1507
EXAMPLES::
1508
1509
sage: from sage.ext.fast_callable import IntegerPowerFunction
1510
sage: cube = IntegerPowerFunction(3)
1511
sage: cube
1512
(^3)
1513
sage: cube(AA(7)^(1/3))
1514
7.000000000000000?
1515
sage: cube.exponent
1516
3
1517
"""
1518
self.exponent = n
1519
1520
def __repr__(self):
1521
r"""
1522
Return a string representing this IntegerPowerFunction.
1523
1524
EXAMPLES::
1525
1526
sage: from sage.ext.fast_callable import IntegerPowerFunction
1527
sage: square = IntegerPowerFunction(2)
1528
sage: square
1529
(^2)
1530
sage: repr(square)
1531
'(^2)'
1532
sage: square.__repr__()
1533
'(^2)'
1534
sage: repr(IntegerPowerFunction(-57))
1535
'(^(-57))'
1536
"""
1537
if self.exponent >= 0:
1538
return "(^%s)" % self.exponent
1539
else:
1540
return "(^(%s))" % self.exponent
1541
1542
def __call__(self, x):
1543
r"""
1544
Call this IntegerPowerFunction, to compute a power of its argument.
1545
1546
EXAMPLES::
1547
1548
sage: from sage.ext.fast_callable import IntegerPowerFunction
1549
sage: square = IntegerPowerFunction(2)
1550
sage: square.__call__(5)
1551
25
1552
sage: square(5)
1553
25
1554
"""
1555
return x**self.exponent
1556
1557
cdef dict builtin_functions = None
1558
cpdef dict get_builtin_functions():
1559
r"""
1560
To handle ExpressionCall, we need to map from Sage and
1561
Python functions to opcode names.
1562
1563
This returns a dictionary which is that map.
1564
1565
We delay building builtin_functions to break a circular import
1566
between sage.calculus and this file.
1567
1568
EXAMPLES:
1569
sage: from sage.ext.fast_callable import get_builtin_functions
1570
sage: builtins = get_builtin_functions()
1571
sage: sorted(list(builtins.values()))
1572
['abs', 'abs', 'acos', 'acosh', 'add', 'asin', 'asinh', 'atan', 'atanh', 'ceil', 'cos', 'cosh', 'cot', 'csc', 'div', 'exp', 'floor', 'floordiv', 'inv', 'log', 'log', 'mul', 'neg', 'pow', 'sec', 'sin', 'sinh', 'sqrt', 'sub', 'tan', 'tanh']
1573
sage: builtins[sin]
1574
'sin'
1575
sage: builtins[ln]
1576
'log'
1577
"""
1578
# We delay building builtin_functions to break a circular import
1579
# between sage.functions and this file.
1580
global builtin_functions
1581
if builtin_functions is not None:
1582
return builtin_functions
1583
builtin_functions = {
1584
operator.add: 'add',
1585
operator.sub: 'sub',
1586
operator.mul: 'mul',
1587
operator.div: 'div',
1588
operator.floordiv: 'floordiv',
1589
operator.abs: 'abs',
1590
operator.neg: 'neg',
1591
operator.inv: 'inv',
1592
operator.pow: 'pow',
1593
}
1594
# not handled: atan2, log2, log10
1595
import sage.functions.all as func_all
1596
for fn in ('sqrt', 'ceil', 'floor',
1597
'sin', 'cos', 'tan', 'sec', 'csc', 'cot',
1598
'asin', 'acos', 'atan', 'sinh', 'cosh', 'tanh',
1599
'asinh', 'acosh', 'atanh', 'exp', 'log'):
1600
builtin_functions[getattr(func_all, fn)] = fn
1601
builtin_functions[func_all.abs_symbolic] = 'abs'
1602
builtin_functions[func_all.ln] = 'log'
1603
return builtin_functions
1604
1605
cdef class InstructionStream # forward declaration
1606
1607
cpdef generate_code(Expression expr, InstructionStream stream):
1608
r"""
1609
Generate code from an Expression tree; write the result into an
1610
InstructionStream.
1611
1612
In fast_callable, first we create an Expression, either directly
1613
with an ExpressionTreeBuilder or with _fast_callable_ methods.
1614
Then we optimize the Expression in tree form. (Unfortunately,
1615
this step is currently missing -- we do no optimizations.)
1616
1617
Then we linearize the Expression into a sequence of instructions,
1618
by walking the Expression and sending the corresponding stack
1619
instructions to an InstructionStream.
1620
1621
EXAMPLES:
1622
sage: from sage.ext.fast_callable import ExpressionTreeBuilder, generate_code, InstructionStream
1623
sage: etb = ExpressionTreeBuilder('x')
1624
sage: x = etb.var('x')
1625
sage: expr = ((x+pi)*(x+1))
1626
sage: from sage.ext.interpreters.wrapper_py import metadata, Wrapper_py
1627
sage: instr_stream = InstructionStream(metadata, 1)
1628
sage: generate_code(expr, instr_stream)
1629
sage: instr_stream.instr('return')
1630
sage: v = Wrapper_py(instr_stream.get_current())
1631
sage: type(v)
1632
<type 'sage.ext.interpreters.wrapper_py.Wrapper_py'>
1633
sage: v(7)
1634
8*pi + 56
1635
1636
TESTS:
1637
sage: def my_sin(x): return sin(x)
1638
sage: def my_norm(x, y): return x*x + y*y
1639
sage: def my_sqrt(x):
1640
... if x < 0: raise ValueError, "sqrt of negative number"
1641
... return sqrt(x, extend=False)
1642
sage: fc = fast_callable(expr, domain=RealField(130))
1643
sage: fc(0)
1644
3.1415926535897932384626433832795028842
1645
sage: fc(1)
1646
8.2831853071795864769252867665590057684
1647
sage: fc = fast_callable(expr, domain=RDF)
1648
sage: fc(0)
1649
3.14159265359
1650
sage: fc(1)
1651
8.28318530718
1652
sage: fc.op_list()
1653
[('load_arg', 0), ('load_const', pi), 'add', ('load_arg', 0), ('load_const', 1), 'add', 'mul', 'return']
1654
sage: fc = fast_callable(etb.call(sin, x) + etb.call(sqrt, x), domain=RDF)
1655
sage: fc(1)
1656
1.84147098481
1657
sage: fc.op_list()
1658
[('load_arg', 0), 'sin', ('load_arg', 0), 'sqrt', 'add', 'return']
1659
sage: fc = fast_callable(etb.call(sin, x) + etb.call(sqrt, x))
1660
sage: fc(1)
1661
sin(1) + 1
1662
sage: fc.op_list()
1663
[('load_arg', 0), ('py_call', sin, 1), ('load_arg', 0), ('py_call', <function sqrt at ...>, 1), 'add', 'return']
1664
sage: fc = fast_callable(etb.call(my_sin, x), domain=RDF)
1665
sage: fc(3)
1666
0.14112000806
1667
sage: fc = fast_callable(etb.call(my_sin, x), domain=RealField(100))
1668
sage: fc(3)
1669
0.14112000805986722210074480281
1670
sage: fc.op_list()
1671
[('load_arg', 0), ('py_call', <function my_sin at 0x...>, 1), 'return']
1672
sage: fc = fast_callable(etb.call(my_sqrt, x), domain=RDF)
1673
sage: fc(3)
1674
1.73205080757
1675
sage: parent(fc(3))
1676
Real Double Field
1677
sage: fc(-3)
1678
Traceback (most recent call last):
1679
...
1680
ValueError: sqrt of negative number
1681
sage: fc = fast_callable(etb.call(my_sqrt, x), domain=RR)
1682
sage: fc(3)
1683
1.73205080756888
1684
sage: fc(-3)
1685
Traceback (most recent call last):
1686
...
1687
ValueError: sqrt of negative number
1688
sage: etb2 = ExpressionTreeBuilder(('y','z'))
1689
sage: y = etb2.var('y')
1690
sage: z = etb2.var('z')
1691
sage: fc = fast_callable(etb2.call(sqrt, etb2.call(my_norm, y, z)), domain=RDF)
1692
sage: fc(3, 4)
1693
5.0
1694
sage: fc.op_list()
1695
[('load_arg', 0), ('load_arg', 1), ('py_call', <function my_norm at 0x...>, 2), 'sqrt', 'return']
1696
sage: fc.python_calls()
1697
[<function my_norm at 0x...>]
1698
sage: fc = fast_callable(etb2.call(sqrt, etb2.call(my_norm, y, z)), domain=RR)
1699
sage: fc(3, 4)
1700
5.00000000000000
1701
sage: fc = fast_callable(etb2.call(my_norm, y, z), domain=ZZ)
1702
sage: fc(3, 4)
1703
25
1704
sage: fc.op_list()
1705
[('load_arg', 0), ('load_arg', 1), ('py_call', <function my_norm at 0x...>, 2), 'return']
1706
sage: fc = fast_callable(expr)
1707
sage: fc(3.0r)
1708
4.0*pi + 12.0
1709
sage: fc = fast_callable(x+3, domain=ZZ)
1710
sage: fc(4)
1711
7
1712
sage: fc = fast_callable(x/3, domain=ZZ)
1713
sage: fc(4)
1714
Traceback (most recent call last):
1715
...
1716
TypeError: no conversion of this rational to integer
1717
sage: fc(6)
1718
2
1719
sage: fc = fast_callable(etb.call(sin, x), domain=ZZ)
1720
sage: fc(0)
1721
0
1722
sage: fc(3)
1723
Traceback (most recent call last):
1724
...
1725
TypeError: unable to convert x (=sin(3)) to an integer
1726
1727
sage: fc = fast_callable(etb(x)^100)
1728
sage: fc(pi)
1729
pi^100
1730
sage: fc = fast_callable(etb(x)^100, domain=ZZ)
1731
sage: fc(2)
1732
1267650600228229401496703205376
1733
sage: fc = fast_callable(etb(x)^100, domain=RIF)
1734
sage: fc(RIF(-2))
1735
1.2676506002282295?e30
1736
sage: fc = fast_callable(etb(x)^100, domain=RDF)
1737
sage: fc.op_list()
1738
[('load_arg', 0), ('ipow', 100), 'return']
1739
sage: fc(1.1)
1740
13780.6123398
1741
sage: fc = fast_callable(etb(x)^100, domain=RR)
1742
sage: fc.op_list()
1743
[('load_arg', 0), ('ipow', 100), 'return']
1744
sage: fc(1.1)
1745
13780.6123398224
1746
sage: fc = fast_callable(etb(x)^(-100), domain=RDF)
1747
sage: fc.op_list()
1748
[('load_arg', 0), ('ipow', -100), 'return']
1749
sage: fc(1.1)
1750
7.25657159015e-05
1751
sage: fc = fast_callable(etb(x)^(-100), domain=RR)
1752
sage: fc(1.1)
1753
0.0000725657159014814
1754
sage: expo = 2^32
1755
sage: base = (1.0).nextabove()
1756
sage: fc = fast_callable(etb(x)^expo, domain=RDF)
1757
sage: fc.op_list()
1758
[('load_arg', 0), ('py_call', (^4294967296), 1), 'return']
1759
sage: fc(base)
1760
1.00000095367
1761
sage: RDF(base)^expo
1762
1.00000095367
1763
sage: fc = fast_callable(etb(x)^expo, domain=RR)
1764
sage: fc.op_list()
1765
[('load_arg', 0), ('py_call', (^4294967296), 1), 'return']
1766
sage: fc(base)
1767
1.00000095367477
1768
sage: base^expo
1769
1.00000095367477
1770
1771
Make sure we don't overflow the stack with highly nested expressions (#11766):
1772
1773
sage: R.<x> = CC[]
1774
sage: f = R(range(100000))
1775
sage: ff = fast_callable(f)
1776
sage: f(0.5)
1777
2.00000000000000
1778
sage: ff(0.5)
1779
2.00000000000000
1780
sage: f(0.9), ff(0.9)
1781
(90.0000000000000, 90.0000000000000)
1782
"""
1783
cdef ExpressionConstant econst
1784
cdef ExpressionVariable evar
1785
cdef ExpressionCall ecall
1786
cdef ExpressionChoice echoice
1787
1788
# Maintain our own stack to avoid crashing on deeply-nested expressions.
1789
cdef list todo = [expr]
1790
do_call = Expression(None)
1791
while len(todo):
1792
expr = todo.pop()
1793
if isinstance(expr, ExpressionConstant):
1794
econst = expr
1795
stream.load_const(econst._value)
1796
elif isinstance(expr, ExpressionVariable):
1797
evar = expr
1798
stream.load_arg(evar._variable_index)
1799
elif isinstance(expr, ExpressionCall):
1800
ecall = expr
1801
todo.append(expr)
1802
todo.append(do_call)
1803
for arg in reversed(ecall._arguments):
1804
todo.append(arg)
1805
continue
1806
elif expr is do_call:
1807
# arguments already evaluated, make the call
1808
ecall = todo.pop()
1809
fn = ecall._function
1810
opname = get_builtin_functions().get(fn)
1811
if opname is not None:
1812
if stream.has_instr(opname):
1813
stream.instr0(opname, ())
1814
continue
1815
if stream.has_instr('py_call'):
1816
stream.instr('py_call', fn, len(ecall._arguments))
1817
else:
1818
raise ValueError, "Unhandled function %s in generate_code" % fn
1819
elif isinstance(expr, ExpressionIPow):
1820
base = expr.base()
1821
exponent = expr.exponent()
1822
metadata = stream.get_metadata()
1823
ipow_range = metadata.ipow_range
1824
if ipow_range is True:
1825
use_ipow = True
1826
elif isinstance(ipow_range, tuple):
1827
a,b = ipow_range
1828
use_ipow = (a <= exponent <= b)
1829
else:
1830
use_ipow = False
1831
generate_code(base, stream)
1832
if use_ipow:
1833
stream.instr('ipow', exponent)
1834
else:
1835
stream.instr('py_call', IntegerPowerFunction(exponent), 1)
1836
else:
1837
raise ValueError, "Unhandled expression kind %s in generate_code" % type(expr)
1838
1839
cdef class InterpreterMetadata # forward declaration
1840
1841
cdef class InstructionStream:
1842
r"""
1843
An InstructionStream takes a sequence of instructions (passed in by
1844
a series of method calls) and computes the data structures needed
1845
by the interpreter. This is the stage where we switch from operating
1846
on Expression trees to a linear representation. If we had a peephole
1847
optimizer (we don't) it would go here.
1848
1849
Currently, this class is not very general; it only works for
1850
interpreters with a fixed set of memory chunks (with fixed names).
1851
Basically, it only works for stack-based expression interpreters.
1852
It should be generalized, so that the interpreter metadata includes
1853
a description of the memory chunks involved and the instruction stream
1854
can handle any interpreter.
1855
1856
Once you're done adding instructions, you call get_current() to retrieve
1857
the information needed by the interpreter (as a Python dictionary).
1858
"""
1859
1860
cdef InterpreterMetadata _metadata
1861
cdef list _instrs
1862
cdef list _bytecode
1863
cdef list _constants
1864
cdef object _constant_locs
1865
cdef object _py_constants
1866
cdef object _py_constant_locs
1867
cdef int _stack_cur_size
1868
cdef int _stack_max_size
1869
cdef int _n_args
1870
cdef object _domain
1871
1872
def __init__(self, metadata, n_args, domain=None):
1873
r"""
1874
Initialize an InstructionStream.
1875
1876
INPUTS:
1877
metadata - The metadata_by_opname from a wrapper module
1878
n_args - The number of arguments accessible by the generated code
1879
(this is just passed to the wrapper class)
1880
domain - The domain of interpretation (this is just passed to the
1881
wrapper class)
1882
1883
EXAMPLES::
1884
1885
sage: from sage.ext.interpreters.wrapper_rdf import metadata
1886
sage: from sage.ext.fast_callable import InstructionStream
1887
sage: instr_stream = InstructionStream(metadata, 1)
1888
sage: instr_stream.get_current()
1889
{'domain': None, 'code': [], 'py_constants': [], 'args': 1, 'stack': 0, 'constants': []}
1890
sage: md = instr_stream.get_metadata()
1891
sage: type(md)
1892
<type 'sage.ext.fast_callable.InterpreterMetadata'>
1893
sage: md.by_opname['py_call']
1894
(CompilerInstrSpec(0, 1, ['py_constants', 'n_inputs']), 3)
1895
sage: md.by_opcode[3]
1896
('py_call', CompilerInstrSpec(0, 1, ['py_constants', 'n_inputs']))
1897
"""
1898
self._metadata = metadata
1899
self._instrs = []
1900
self._bytecode = []
1901
self._constants = []
1902
self._constant_locs = {}
1903
self._py_constants = []
1904
self._py_constant_locs = {}
1905
self._stack_cur_size = 0
1906
self._stack_max_size = 0
1907
self._domain = domain
1908
self._n_args = n_args
1909
1910
def load_const(self, c):
1911
r"""
1912
Add a 'load_const' instruction to this InstructionStream.
1913
1914
EXAMPLES::
1915
1916
sage: from sage.ext.interpreters.wrapper_rdf import metadata
1917
sage: from sage.ext.fast_callable import InstructionStream, op_list
1918
sage: instr_stream = InstructionStream(metadata, 1)
1919
sage: instr_stream.load_const(5)
1920
sage: instr_stream.current_op_list()
1921
[('load_const', 5)]
1922
sage: instr_stream.load_const(7)
1923
sage: instr_stream.load_const(5)
1924
sage: instr_stream.current_op_list()
1925
[('load_const', 5), ('load_const', 7), ('load_const', 5)]
1926
1927
Note that constants are shared: even though we load 5 twice, it
1928
only appears once in the constant table.
1929
sage: instr_stream.get_current()['constants']
1930
[5, 7]
1931
"""
1932
self.instr('load_const', c)
1933
1934
def load_arg(self, n):
1935
r"""
1936
Add a 'load_arg' instruction to this InstructionStream.
1937
1938
EXAMPLES::
1939
1940
sage: from sage.ext.interpreters.wrapper_rdf import metadata
1941
sage: from sage.ext.fast_callable import InstructionStream
1942
sage: instr_stream = InstructionStream(metadata, 12)
1943
sage: instr_stream.load_arg(5)
1944
sage: instr_stream.current_op_list()
1945
[('load_arg', 5)]
1946
sage: instr_stream.load_arg(3)
1947
sage: instr_stream.current_op_list()
1948
[('load_arg', 5), ('load_arg', 3)]
1949
"""
1950
self.instr('load_arg', n)
1951
1952
cpdef bint has_instr(self, opname):
1953
r"""
1954
Check whether this InstructionStream knows how to generate code
1955
for a given instruction.
1956
1957
EXAMPLES::
1958
1959
sage: from sage.ext.interpreters.wrapper_rdf import metadata
1960
sage: from sage.ext.fast_callable import InstructionStream
1961
sage: instr_stream = InstructionStream(metadata, 1)
1962
sage: instr_stream.has_instr('return')
1963
True
1964
sage: instr_stream.has_instr('factorial')
1965
False
1966
sage: instr_stream.has_instr('abs')
1967
True
1968
"""
1969
return (opname in self._metadata.by_opname)
1970
1971
def instr(self, opname, *args):
1972
r"""
1973
Generate code in this InstructionStream for the given instruction
1974
and arguments.
1975
1976
The opname is used to look up a CompilerInstrSpec; the
1977
CompilerInstrSpec describes how to interpret the arguments.
1978
(This is documented in the class docstring for CompilerInstrSpec.)
1979
1980
EXAMPLES::
1981
1982
sage: from sage.ext.interpreters.wrapper_rdf import metadata
1983
sage: from sage.ext.fast_callable import InstructionStream
1984
sage: instr_stream = InstructionStream(metadata, 1)
1985
sage: instr_stream.instr('load_arg', 0)
1986
sage: instr_stream.instr('sin')
1987
sage: instr_stream.instr('py_call', math.sin, 1)
1988
sage: instr_stream.instr('abs')
1989
sage: instr_stream.instr('factorial')
1990
Traceback (most recent call last):
1991
...
1992
KeyError: 'factorial'
1993
sage: instr_stream.instr('return')
1994
sage: instr_stream.current_op_list()
1995
[('load_arg', 0), 'sin', ('py_call', <built-in function sin>, 1), 'abs', 'return']
1996
"""
1997
self.instr0(opname, args)
1998
1999
cdef instr0(self, opname, tuple args):
2000
"""
2001
Cdef version of instr. (Can't cpdef because of star args.)
2002
"""
2003
cdef int i
2004
2005
spec, opcode = self._metadata.by_opname[opname]
2006
assert len(spec.parameters) == len(args)
2007
2008
cdef int n_inputs = spec.n_inputs
2009
cdef int n_outputs = spec.n_outputs
2010
2011
self._bytecode.append(opcode)
2012
for i in range(len(args)):
2013
if spec.parameters[i] == 'constants':
2014
# XXX bad for strict-mode floating-point constants
2015
# (doesn't handle signed 0, NaN)
2016
arg = args[i]
2017
if arg in self._constant_locs:
2018
self._bytecode.append(self._constant_locs[arg])
2019
else:
2020
loc = len(self._constants)
2021
self._constants.append(arg)
2022
self._constant_locs[arg] = loc
2023
self._bytecode.append(loc)
2024
elif spec.parameters[i] == 'args':
2025
self._bytecode.append(args[i])
2026
elif spec.parameters[i] == 'code':
2027
self._bytecode.append(args[i])
2028
elif spec.parameters[i] == 'n_inputs':
2029
self._bytecode.append(args[i])
2030
n_inputs = args[i]
2031
elif spec.parameters[i] == 'n_outputs':
2032
self._bytecode.append(args[i])
2033
n_outputs = args[i]
2034
elif spec.parameters[i] == 'py_constants':
2035
arg = args[i]
2036
if arg in self._py_constant_locs:
2037
self._bytecode.append(self._py_constant_locs[arg])
2038
else:
2039
loc = len(self._py_constants)
2040
self._py_constants.append(arg)
2041
self._py_constant_locs[arg] = loc
2042
self._bytecode.append(loc)
2043
else:
2044
raise ValueError
2045
2046
self._stack_cur_size -= n_inputs
2047
self._stack_cur_size += n_outputs
2048
self._stack_max_size = max(self._stack_max_size, self._stack_cur_size)
2049
2050
def get_metadata(self):
2051
r"""
2052
Returns the interpreter metadata being used by the current
2053
InstructionStream.
2054
2055
The code generator sometimes uses this to decide which code
2056
to generate.
2057
2058
EXAMPLES::
2059
2060
sage: from sage.ext.interpreters.wrapper_rdf import metadata
2061
sage: from sage.ext.fast_callable import InstructionStream
2062
sage: instr_stream = InstructionStream(metadata, 1)
2063
sage: md = instr_stream.get_metadata()
2064
sage: type(md)
2065
<type 'sage.ext.fast_callable.InterpreterMetadata'>
2066
"""
2067
return self._metadata
2068
2069
def current_op_list(self):
2070
r"""
2071
Returns the list of instructions that have been added to this
2072
InstructionStream so far.
2073
2074
It's OK to call this, then add more instructions.
2075
2076
EXAMPLES::
2077
2078
sage: from sage.ext.interpreters.wrapper_rdf import metadata
2079
sage: from sage.ext.fast_callable import InstructionStream
2080
sage: instr_stream = InstructionStream(metadata, 1)
2081
sage: instr_stream.instr('load_arg', 0)
2082
sage: instr_stream.instr('py_call', math.sin, 1)
2083
sage: instr_stream.instr('abs')
2084
sage: instr_stream.instr('return')
2085
sage: instr_stream.current_op_list()
2086
[('load_arg', 0), ('py_call', <built-in function sin>, 1), 'abs', 'return']
2087
"""
2088
return op_list(self.get_current(), self._metadata)
2089
2090
def get_current(self):
2091
r"""
2092
Return the current state of the InstructionStream, as a dictionary
2093
suitable for passing to a wrapper class.
2094
2095
NOTE: The dictionary includes internal data structures of the
2096
InstructionStream; you must not modify it.
2097
2098
EXAMPLES::
2099
2100
sage: from sage.ext.interpreters.wrapper_rdf import metadata
2101
sage: from sage.ext.fast_callable import InstructionStream
2102
sage: instr_stream = InstructionStream(metadata, 1)
2103
sage: instr_stream.get_current()
2104
{'domain': None, 'code': [], 'py_constants': [], 'args': 1, 'stack': 0, 'constants': []}
2105
sage: instr_stream.instr('load_arg', 0)
2106
sage: instr_stream.instr('py_call', math.sin, 1)
2107
sage: instr_stream.instr('abs')
2108
sage: instr_stream.instr('return')
2109
sage: instr_stream.current_op_list()
2110
[('load_arg', 0), ('py_call', <built-in function sin>, 1), 'abs', 'return']
2111
sage: instr_stream.get_current()
2112
{'domain': None, 'code': [0, 0, 3, 0, 1, 12, 2], 'py_constants': [<built-in function sin>], 'args': 1, 'stack': 1, 'constants': []}
2113
"""
2114
d = {'args': self._n_args,
2115
'constants': self._constants,
2116
'py_constants': self._py_constants,
2117
'stack': self._stack_max_size,
2118
'code': self._bytecode,
2119
'domain': self._domain}
2120
return d
2121
2122
cdef class InterpreterMetadata(object):
2123
r"""
2124
The interpreter metadata for a fast_callable interpreter. Currently
2125
consists of a dictionary mapping instruction names to
2126
(CompilerInstrSpec, opcode) pairs, a list mapping opcodes to
2127
(instruction name, CompilerInstrSpec) pairs, and a range of exponents
2128
for which the ipow instruction can be used. This range can be
2129
False (if the ipow instruction should never be used), a pair of
2130
two integers (a,b), if ipow should be used for a<=n<=b, or True,
2131
if ipow should always be used. When ipow cannot be used, then
2132
we fall back on calling IntegerPowerFunction.
2133
2134
See the class docstring for CompilerInstrSpec for more information.
2135
2136
NOTE: You must not modify the metadata.
2137
"""
2138
cdef public dict by_opname
2139
cdef public list by_opcode
2140
cdef public ipow_range
2141
2142
def __init__(self, by_opname, by_opcode, ipow_range):
2143
r"""
2144
Initialize an InterpreterMetadata object.
2145
2146
EXAMPLES::
2147
2148
sage: from sage.ext.fast_callable import InterpreterMetadata
2149
sage: metadata = InterpreterMetadata(by_opname={'opname dict goes here': True}, by_opcode=['opcode list goes here'], ipow_range=(2, 57))
2150
sage: metadata.by_opname
2151
{'opname dict goes here': True}
2152
sage: metadata.by_opcode
2153
['opcode list goes here']
2154
sage: metadata.ipow_range
2155
(2, 57)
2156
"""
2157
self.by_opname = by_opname
2158
self.by_opcode = by_opcode
2159
self.ipow_range = ipow_range
2160
2161
class CompilerInstrSpec(object):
2162
r"""
2163
Describes a single instruction to the fast_callable code generator.
2164
2165
An instruction has a number of stack inputs, a number of stack
2166
outputs, and a parameter list describing extra arguments that
2167
must be passed to the InstructionStream.instr method (that end up
2168
as extra words in the code).
2169
2170
The parameter list is a list of strings. Each string is one of
2171
the following:
2172
2173
- 'args' - The instruction argument refers to an input argument
2174
of the wrapper class; it is just appended to the code.
2175
- 'constants', 'py_constants' - The instruction argument is a value;
2176
the value is added to the corresponding list (if it's
2177
not already there) and the index is appended to the
2178
code.
2179
- 'n_inputs', 'n_outputs' - The instruction actually takes a variable
2180
number of inputs or outputs (the n_inputs and n_outputs
2181
attributes of this instruction are ignored).
2182
The instruction argument specifies the number of inputs
2183
or outputs (respectively); it is just appended to the code.
2184
"""
2185
2186
def __init__(self, n_inputs, n_outputs, parameters):
2187
r"""
2188
Initialize a CompilerInstrSpec.
2189
2190
EXAMPLES::
2191
2192
sage: from sage.ext.fast_callable import CompilerInstrSpec
2193
sage: CompilerInstrSpec(0, 1, ['py_constants', 'n_inputs'])
2194
CompilerInstrSpec(0, 1, ['py_constants', 'n_inputs'])
2195
"""
2196
self.n_inputs = n_inputs
2197
self.n_outputs = n_outputs
2198
self.parameters = parameters
2199
2200
def __repr__(self):
2201
r"""
2202
Give a string representation for this CompilerInstrSpec.
2203
2204
EXAMPLES::
2205
2206
sage: from sage.ext.fast_callable import CompilerInstrSpec
2207
sage: v = CompilerInstrSpec(0, 1, ['py_constants', 'n_inputs'])
2208
sage: v
2209
CompilerInstrSpec(0, 1, ['py_constants', 'n_inputs'])
2210
sage: repr(v)
2211
"CompilerInstrSpec(0, 1, ['py_constants', 'n_inputs'])"
2212
sage: v.__repr__()
2213
"CompilerInstrSpec(0, 1, ['py_constants', 'n_inputs'])"
2214
"""
2215
return "CompilerInstrSpec(%d, %d, %s)" % (self.n_inputs, self.n_outputs, self.parameters)
2216
2217
def op_list(args, metadata):
2218
r"""
2219
Given a dictionary with the result of calling get_current on an
2220
InstructionStream, and the corresponding interpreter metadata,
2221
return a list of the instructions, in a simple somewhat
2222
human-readable format.
2223
2224
For debugging only. (That is, it's probably not a good idea to
2225
try to programmatically manipulate the result of this function;
2226
the expected use is just to print the returned list to the
2227
screen.)
2228
2229
There's probably no reason to call this directly; if you
2230
have a wrapper object, call op_list on it; if you have an
2231
InstructionStream object, call current_op_list on it.
2232
2233
EXAMPLES:
2234
sage: from sage.ext.interpreters.wrapper_rdf import metadata
2235
sage: from sage.ext.fast_callable import InstructionStream, op_list
2236
sage: instr_stream = InstructionStream(metadata, 1)
2237
sage: instr_stream.instr('load_arg', 0)
2238
sage: instr_stream.instr('abs')
2239
sage: instr_stream.instr('return')
2240
sage: instr_stream.current_op_list()
2241
[('load_arg', 0), 'abs', 'return']
2242
sage: op_list(instr_stream.get_current(), metadata)
2243
[('load_arg', 0), 'abs', 'return']
2244
"""
2245
ops = []
2246
code = args['code']
2247
while len(code):
2248
opcode = code[0]
2249
code = code[1:]
2250
(opname, instr) = metadata.by_opcode[opcode]
2251
if len(instr.parameters):
2252
op = [opname]
2253
for p in instr.parameters:
2254
p_loc = code[0]
2255
code = code[1:]
2256
if p in ('args', 'code', 'n_inputs', 'n_outputs'):
2257
op.append(p_loc)
2258
else:
2259
op.append(args[p][p_loc])
2260
ops.append(tuple(op))
2261
else:
2262
ops.append(opname)
2263
return ops
2264
2265
2266
cdef class Wrapper:
2267
r"""
2268
The parent class for all fast_callable wrappers. Implements shared
2269
behavior (currently only debugging).
2270
"""
2271
2272
def __init__(self, args, metadata):
2273
r"""
2274
Initialize a Wrapper object.
2275
2276
EXAMPLES::
2277
2278
sage: from sage.ext.fast_callable import ExpressionTreeBuilder, generate_code, InstructionStream
2279
sage: etb = ExpressionTreeBuilder('x')
2280
sage: x = etb.var('x')
2281
sage: expr = ((x+pi)*(x+1))
2282
sage: from sage.ext.interpreters.wrapper_py import metadata, Wrapper_py
2283
sage: instr_stream = InstructionStream(metadata, 1)
2284
sage: generate_code(expr, instr_stream)
2285
sage: instr_stream.instr('return')
2286
sage: v = Wrapper_py(instr_stream.get_current())
2287
sage: v.get_orig_args()
2288
{'domain': None, 'code': [0, 0, 1, 0, 4, 0, 0, 1, 1, 4, 6, 2], 'py_constants': [], 'args': 1, 'stack': 3, 'constants': [pi, 1]}
2289
sage: v.op_list()
2290
[('load_arg', 0), ('load_const', pi), 'add', ('load_arg', 0), ('load_const', 1), 'add', 'mul', 'return']
2291
"""
2292
2293
# We only keep the original arguments for debugging (op_list(), etc.);
2294
# is it worth the memory cost? (Note that we may be holding on to
2295
# large objects that could otherwise be garbage collected, for
2296
# instance.)
2297
self._orig_args = args
2298
self._metadata = metadata
2299
2300
def get_orig_args(self):
2301
r"""
2302
Get the original arguments used when initializing this
2303
wrapper.
2304
2305
(Probably only useful when writing doctests.)
2306
2307
EXAMPLES::
2308
2309
sage: fast_callable(sin(x)/x, vars=[x], domain=RDF).get_orig_args()
2310
{'domain': Real Double Field, 'code': [0, 0, 16, 0, 0, 8, 2], 'py_constants': [], 'args': 1, 'stack': 2, 'constants': []}
2311
"""
2312
return self._orig_args
2313
2314
def op_list(self):
2315
r"""
2316
Return the list of instructions in this wrapper.
2317
2318
EXAMPLES::
2319
2320
sage: fast_callable(cos(x)*x, vars=[x], domain=RDF).op_list()
2321
[('load_arg', 0), ('load_arg', 0), 'cos', 'mul', 'return']
2322
"""
2323
return op_list(self._orig_args, self._metadata)
2324
2325
def python_calls(self):
2326
r"""
2327
List the Python functions that are called in this wrapper.
2328
2329
(Python function calls are slow, so ideally this list would
2330
be empty. If it is not empty, then perhaps there is an
2331
optimization opportunity where a Sage developer could speed
2332
this up by adding a new instruction to the interpreter.)
2333
2334
EXAMPLES::
2335
2336
sage: fast_callable(abs(sin(x)), vars=[x], domain=RDF).python_calls()
2337
[]
2338
sage: fast_callable(abs(sin(factorial(x))), vars=[x]).python_calls()
2339
[factorial, sin]
2340
"""
2341
ops = self.op_list()
2342
py_calls = []
2343
for op in ops:
2344
if isinstance(op, tuple) and op[0] == 'py_call':
2345
py_calls.append(op[1])
2346
return py_calls
2347
2348
2349