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