Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/numexpr/necompiler.py: 15%
465 statements
« prev ^ index » next coverage.py v7.2.5, created at 2023-05-10 06:15 +0000
« prev ^ index » next coverage.py v7.2.5, created at 2023-05-10 06:15 +0000
1###################################################################
2# Numexpr - Fast numerical array expression evaluator for NumPy.
3#
4# License: MIT
5# Author: See AUTHORS.txt
6#
7# See LICENSE.txt and LICENSES/*.txt for details about copyright and
8# rights to use.
9####################################################################
11import __future__
12import sys
13import numpy
14import threading
16is_cpu_amd_intel = False # DEPRECATION WARNING: WILL BE REMOVED IN FUTURE RELEASE
17from numexpr import interpreter, expressions, use_vml
18from numexpr.utils import CacheDict
20# Declare a double type that does not exist in Python space
21double = numpy.double
22double = numpy.double
24int_ = numpy.int32
25long_ = numpy.int64
27typecode_to_kind = {'b': 'bool', 'i': 'int', 'l': 'long', 'f': 'float', 'd': 'double',
28 'c': 'complex', 'n': 'none', 's': 'str'}
29kind_to_typecode = {'bool': 'b', 'int': 'i', 'long': 'l', 'float': 'f', 'double': 'd',
30 'complex': 'c', 'bytes': 's', 'str': 's', 'none': 'n'}
31type_to_typecode = {bool: 'b', int_: 'i', long_: 'l', float: 'f',
32 double: 'd', complex: 'c', bytes: 's', str: 's'}
33type_to_kind = expressions.type_to_kind
34kind_to_type = expressions.kind_to_type
35default_type = kind_to_type[expressions.default_kind]
36scalar_constant_kinds = list(kind_to_typecode.keys())
38# VML functions that are implemented in numexpr
39vml_functions = [
40 "div", # interp_body.cpp
41 "inv", # interp_body.cpp
42 "pow", # interp_body.cpp
43 # Keep the rest of this list in sync with the ones listed in functions.hpp
44 "sqrt",
45 "sin",
46 "cos",
47 "tan",
48 "arcsin",
49 "arccos",
50 "arctan",
51 "sinh",
52 "cosh",
53 "tanh",
54 "arcsinh",
55 "arccosh",
56 "arctanh",
57 "log",
58 "log1p",
59 "log10",
60 "exp",
61 "expm1",
62 "absolute",
63 "conjugate",
64 "arctan2",
65 "fmod",
66 "ceil",
67 "floor"
68 ]
71class ASTNode(object):
72 """Abstract Syntax Tree node.
74 Members:
76 astType -- type of node (op, constant, variable, raw, or alias)
77 astKind -- the type of the result (bool, float, etc.)
78 value -- value associated with this node.
79 An opcode, numerical value, a variable name, etc.
80 children -- the children below this node
81 reg -- the register assigned to the result for this node.
82 """
83 cmpnames = ['astType', 'astKind', 'value', 'children']
85 def __init__(self, astType='generic', astKind='unknown', value=None, children=()):
86 object.__init__(self)
87 self.astType = astType
88 self.astKind = astKind
89 self.value = value
90 self.children = tuple(children)
91 self.reg = None
93 def __eq__(self, other):
94 if self.astType == 'alias':
95 self = self.value
96 if other.astType == 'alias':
97 other = other.value
98 if not isinstance(other, ASTNode):
99 return False
100 for name in self.cmpnames:
101 if getattr(self, name) != getattr(other, name):
102 return False
103 return True
105 def __lt__(self,other):
106 # RAM: this is a fix for issue #88 whereby sorting on constants
107 # that may be of astKind == 'complex' but type(self.value) == int or float
108 # Here we let NumPy sort as it will cast data properly for comparison
109 # when the Python built-ins will raise an error.
110 if self.astType == 'constant':
111 if self.astKind == other.astKind:
112 return numpy.array(self.value) < numpy.array(other.value)
113 return self.astKind < other.astKind
114 else:
115 raise TypeError('Sorting not implemented for astType: %s'%self.astType)
117 def __hash__(self):
118 if self.astType == 'alias':
119 self = self.value
120 return hash((self.astType, self.astKind, self.value, self.children))
122 def __str__(self):
123 return 'AST(%s, %s, %s, %s, %s)' % (self.astType, self.astKind,
124 self.value, self.children, self.reg)
126 def __repr__(self):
127 return '<AST object at %s>' % id(self)
129 def key(self):
130 return (self.astType, self.astKind, self.value, self.children)
132 def typecode(self):
133 return kind_to_typecode[self.astKind]
135 def postorderWalk(self):
136 for c in self.children:
137 for w in c.postorderWalk():
138 yield w
139 yield self
141 def allOf(self, *astTypes):
142 astTypes = set(astTypes)
143 for w in self.postorderWalk():
144 if w.astType in astTypes:
145 yield w
148def expressionToAST(ex):
149 """Take an expression tree made out of expressions.ExpressionNode,
150 and convert to an AST tree.
152 This is necessary as ExpressionNode overrides many methods to act
153 like a number.
154 """
155 return ASTNode(ex.astType, ex.astKind, ex.value,
156 [expressionToAST(c) for c in ex.children])
159def sigPerms(s):
160 """Generate all possible signatures derived by upcasting the given
161 signature.
162 """
163 codes = 'bilfdc'
164 if not s:
165 yield ''
166 elif s[0] in codes:
167 start = codes.index(s[0])
168 for x in codes[start:]:
169 for y in sigPerms(s[1:]):
170 yield x + y
171 elif s[0] == 's': # numbers shall not be cast to strings
172 for y in sigPerms(s[1:]):
173 yield 's' + y
174 else:
175 yield s
178def typeCompileAst(ast):
179 """Assign appropriate types to each node in the AST.
181 Will convert opcodes and functions to appropriate upcast version,
182 and add "cast" ops if needed.
183 """
184 children = list(ast.children)
185 if ast.astType == 'op':
186 retsig = ast.typecode()
187 basesig = ''.join(x.typecode() for x in list(ast.children))
188 # Find some operation that will work on an acceptable casting of args.
189 for sig in sigPerms(basesig):
190 value = (ast.value + '_' + retsig + sig).encode('ascii')
191 if value in interpreter.opcodes:
192 break
193 else:
194 for sig in sigPerms(basesig):
195 funcname = (ast.value + '_' + retsig + sig).encode('ascii')
196 if funcname in interpreter.funccodes:
197 value = ('func_%sn' % (retsig + sig)).encode('ascii')
198 children += [ASTNode('raw', 'none',
199 interpreter.funccodes[funcname])]
200 break
201 else:
202 raise NotImplementedError(
203 "couldn't find matching opcode for '%s'"
204 % (ast.value + '_' + retsig + basesig))
205 # First just cast constants, then cast variables if necessary:
206 for i, (have, want) in enumerate(zip(basesig, sig)):
207 if have != want:
208 kind = typecode_to_kind[want]
209 if children[i].astType == 'constant':
210 children[i] = ASTNode('constant', kind, children[i].value)
211 else:
212 opname = "cast"
213 children[i] = ASTNode('op', kind, opname, [children[i]])
214 else:
215 value = ast.value
216 children = ast.children
217 return ASTNode(ast.astType, ast.astKind, value,
218 [typeCompileAst(c) for c in children])
221class Register(object):
222 """Abstraction for a register in the VM.
224 Members:
225 node -- the AST node this corresponds to
226 temporary -- True if this isn't an input or output
227 immediate -- not a register, but an immediate value
228 n -- the physical register number.
229 None if no number assigned yet.
230 """
232 def __init__(self, astnode, temporary=False):
233 self.node = astnode
234 self.temporary = temporary
235 self.immediate = False
236 self.n = None
238 def __str__(self):
239 if self.temporary:
240 name = 'Temporary'
241 else:
242 name = 'Register'
243 return '%s(%s, %s, %s)' % (name, self.node.astType,
244 self.node.astKind, self.n,)
246 def __repr__(self):
247 return self.__str__()
250class Immediate(Register):
251 """Representation of an immediate (integer) operand, instead of
252 a register.
253 """
255 def __init__(self, astnode):
256 Register.__init__(self, astnode)
257 self.immediate = True
259 def __str__(self):
260 return 'Immediate(%d)' % (self.node.value,)
263def stringToExpression(s, types, context):
264 """Given a string, convert it to a tree of ExpressionNode's.
265 """
266 old_ctx = expressions._context.get_current_context()
267 try:
268 expressions._context.set_new_context(context)
269 # first compile to a code object to determine the names
270 if context.get('truediv', False):
271 flags = __future__.division.compiler_flag
272 else:
273 flags = 0
274 c = compile(s, '<expr>', 'eval', flags)
275 # make VariableNode's for the names
276 names = {}
277 for name in c.co_names:
278 if name == "None":
279 names[name] = None
280 elif name == "True":
281 names[name] = True
282 elif name == "False":
283 names[name] = False
284 else:
285 t = types.get(name, default_type)
286 names[name] = expressions.VariableNode(name, type_to_kind[t])
287 names.update(expressions.functions)
288 # now build the expression
289 ex = eval(c, names)
290 if expressions.isConstant(ex):
291 ex = expressions.ConstantNode(ex, expressions.getKind(ex))
292 elif not isinstance(ex, expressions.ExpressionNode):
293 raise TypeError("unsupported expression type: %s" % type(ex))
294 finally:
295 expressions._context.set_new_context(old_ctx)
296 return ex
299def isReduction(ast):
300 prefixes = (b'sum_', b'prod_', b'min_', b'max_')
301 return any(ast.value.startswith(p) for p in prefixes)
304def getInputOrder(ast, input_order=None):
305 """
306 Derive the input order of the variables in an expression.
307 """
308 variables = {}
309 for a in ast.allOf('variable'):
310 variables[a.value] = a
311 variable_names = set(variables.keys())
313 if input_order:
314 if variable_names != set(input_order):
315 raise ValueError(
316 "input names (%s) don't match those found in expression (%s)"
317 % (input_order, variable_names))
319 ordered_names = input_order
320 else:
321 ordered_names = list(variable_names)
322 ordered_names.sort()
323 ordered_variables = [variables[v] for v in ordered_names]
324 return ordered_variables
327def convertConstantToKind(x, kind):
328 # Exception for 'float' types that will return the NumPy float32 type
329 if kind == 'float':
330 return numpy.float32(x)
331 elif isinstance(x,str):
332 return x.encode('ascii')
333 return kind_to_type[kind](x)
336def getConstants(ast):
337 """
338 RAM: implemented magic method __lt__ for ASTNode to fix issues
339 #88 and #209. The following test code works now, as does the test suite.
341 import numexpr as ne
342 a = 1 + 3j; b = 5.0
343 ne.evaluate('a*2 + 15j - b')
344 """
345 constant_registers = set([node.reg for node in ast.allOf("constant")])
346 constants_order = sorted([r.node for r in constant_registers])
347 constants = [convertConstantToKind(a.value, a.astKind)
348 for a in constants_order]
349 return constants_order, constants
352def sortNodesByOrder(nodes, order):
353 order_map = {}
354 for i, (_, v, _) in enumerate(order):
355 order_map[v] = i
356 dec_nodes = [(order_map[n.value], n) for n in nodes]
357 dec_nodes.sort()
358 return [a[1] for a in dec_nodes]
361def assignLeafRegisters(inodes, registerMaker):
362 """
363 Assign new registers to each of the leaf nodes.
364 """
365 leafRegisters = {}
366 for node in inodes:
367 key = node.key()
368 if key in leafRegisters:
369 node.reg = leafRegisters[key]
370 else:
371 node.reg = leafRegisters[key] = registerMaker(node)
374def assignBranchRegisters(inodes, registerMaker):
375 """
376 Assign temporary registers to each of the branch nodes.
377 """
378 for node in inodes:
379 node.reg = registerMaker(node, temporary=True)
382def collapseDuplicateSubtrees(ast):
383 """
384 Common subexpression elimination.
385 """
386 seen = {}
387 aliases = []
388 for a in ast.allOf('op'):
389 if a in seen:
390 target = seen[a]
391 a.astType = 'alias'
392 a.value = target
393 a.children = ()
394 aliases.append(a)
395 else:
396 seen[a] = a
397 # Set values and registers so optimizeTemporariesAllocation
398 # doesn't get confused
399 for a in aliases:
400 while a.value.astType == 'alias':
401 a.value = a.value.value
402 return aliases
405def optimizeTemporariesAllocation(ast):
406 """
407 Attempt to minimize the number of temporaries needed, by reusing old ones.
408 """
409 nodes = [n for n in ast.postorderWalk() if n.reg.temporary]
410 users_of = dict((n.reg, set()) for n in nodes)
412 node_regs = dict((n, set(c.reg for c in n.children if c.reg.temporary))
413 for n in nodes)
414 if nodes and nodes[-1] is not ast:
415 nodes_to_check = nodes + [ast]
416 else:
417 nodes_to_check = nodes
418 for n in nodes_to_check:
419 for c in n.children:
420 if c.reg.temporary:
421 users_of[c.reg].add(n)
423 unused = dict([(tc, set()) for tc in scalar_constant_kinds])
424 for n in nodes:
425 for c in n.children:
426 reg = c.reg
427 if reg.temporary:
428 users = users_of[reg]
429 users.discard(n)
430 if not users:
431 unused[reg.node.astKind].add(reg)
432 if unused[n.astKind]:
433 reg = unused[n.astKind].pop()
434 users_of[reg] = users_of[n.reg]
435 n.reg = reg
438def setOrderedRegisterNumbers(order, start):
439 """
440 Given an order of nodes, assign register numbers.
441 """
442 for i, node in enumerate(order):
443 node.reg.n = start + i
444 return start + len(order)
447def setRegisterNumbersForTemporaries(ast, start):
448 """
449 Assign register numbers for temporary registers, keeping track of
450 aliases and handling immediate operands.
451 """
452 seen = 0
453 signature = ''
454 aliases = []
455 for node in ast.postorderWalk():
456 if node.astType == 'alias':
457 aliases.append(node)
458 node = node.value
459 if node.reg.immediate:
460 node.reg.n = node.value
461 continue
462 reg = node.reg
463 if reg.n is None:
464 reg.n = start + seen
465 seen += 1
466 signature += reg.node.typecode()
467 for node in aliases:
468 node.reg = node.value.reg
469 return start + seen, signature
472def convertASTtoThreeAddrForm(ast):
473 """
474 Convert an AST to a three address form.
476 Three address form is (op, reg1, reg2, reg3), where reg1 is the
477 destination of the result of the instruction.
479 I suppose this should be called three register form, but three
480 address form is found in compiler theory.
481 """
482 return [(node.value, node.reg) + tuple([c.reg for c in node.children])
483 for node in ast.allOf('op')]
486def compileThreeAddrForm(program):
487 """
488 Given a three address form of the program, compile it a string that
489 the VM understands.
490 """
492 def nToChr(reg):
493 if reg is None:
494 return b'\xff'
495 elif reg.n < 0:
496 raise ValueError("negative value for register number %s" % reg.n)
497 else:
498 return bytes([reg.n])
500 def quadrupleToString(opcode, store, a1=None, a2=None):
501 cop = chr(interpreter.opcodes[opcode]).encode('ascii')
502 cs = nToChr(store)
503 ca1 = nToChr(a1)
504 ca2 = nToChr(a2)
505 return cop + cs + ca1 + ca2
507 def toString(args):
508 while len(args) < 4:
509 args += (None,)
510 opcode, store, a1, a2 = args[:4]
511 s = quadrupleToString(opcode, store, a1, a2)
512 l = [s]
513 args = args[4:]
514 while args:
515 s = quadrupleToString(b'noop', *args[:3])
516 l.append(s)
517 args = args[3:]
518 return b''.join(l)
520 prog_str = b''.join([toString(t) for t in program])
521 return prog_str
524context_info = [
525 ('optimization', ('none', 'moderate', 'aggressive'), 'aggressive'),
526 ('truediv', (False, True, 'auto'), 'auto')
527]
530def getContext(kwargs, frame_depth=1):
531 d = kwargs.copy()
532 context = {}
533 for name, allowed, default in context_info:
534 value = d.pop(name, default)
535 if value in allowed:
536 context[name] = value
537 else:
538 raise ValueError("'%s' must be one of %s" % (name, allowed))
540 if d:
541 raise ValueError("Unknown keyword argument '%s'" % d.popitem()[0])
542 if context['truediv'] == 'auto':
543 caller_globals = sys._getframe(frame_depth + 1).f_globals
544 context['truediv'] = caller_globals.get('division', None) == __future__.division
546 return context
549def precompile(ex, signature=(), context={}):
550 """
551 Compile the expression to an intermediate form.
552 """
553 types = dict(signature)
554 input_order = [name for (name, type_) in signature]
556 if isinstance(ex, str):
557 ex = stringToExpression(ex, types, context)
559 # the AST is like the expression, but the node objects don't have
560 # any odd interpretations
562 ast = expressionToAST(ex)
564 if ex.astType != 'op':
565 ast = ASTNode('op', value='copy', astKind=ex.astKind, children=(ast,))
567 ast = typeCompileAst(ast)
569 aliases = collapseDuplicateSubtrees(ast)
571 assignLeafRegisters(ast.allOf('raw'), Immediate)
572 assignLeafRegisters(ast.allOf('variable', 'constant'), Register)
573 assignBranchRegisters(ast.allOf('op'), Register)
575 # assign registers for aliases
576 for a in aliases:
577 a.reg = a.value.reg
579 input_order = getInputOrder(ast, input_order)
580 constants_order, constants = getConstants(ast)
582 if isReduction(ast):
583 ast.reg.temporary = False
585 optimizeTemporariesAllocation(ast)
587 ast.reg.temporary = False
588 r_output = 0
589 ast.reg.n = 0
591 r_inputs = r_output + 1
592 r_constants = setOrderedRegisterNumbers(input_order, r_inputs)
593 r_temps = setOrderedRegisterNumbers(constants_order, r_constants)
594 r_end, tempsig = setRegisterNumbersForTemporaries(ast, r_temps)
596 threeAddrProgram = convertASTtoThreeAddrForm(ast)
597 input_names = tuple([a.value for a in input_order])
598 signature = ''.join(type_to_typecode[types.get(x, default_type)]
599 for x in input_names)
600 return threeAddrProgram, signature, tempsig, constants, input_names
603def NumExpr(ex, signature=(), **kwargs):
604 """
605 Compile an expression built using E.<variable> variables to a function.
607 ex can also be specified as a string "2*a+3*b".
609 The order of the input variables and their types can be specified using the
610 signature parameter, which is a list of (name, type) pairs.
612 Returns a `NumExpr` object containing the compiled function.
613 """
614 # NumExpr can be called either directly by the end-user, in which case
615 # kwargs need to be sanitized by getContext, or by evaluate,
616 # in which case kwargs are in already sanitized.
617 # In that case frame_depth is wrong (it should be 2) but it doesn't matter
618 # since it will not be used (because truediv='auto' has already been
619 # translated to either True or False).
621 context = getContext(kwargs, frame_depth=1)
622 threeAddrProgram, inputsig, tempsig, constants, input_names = precompile(ex, signature, context)
623 program = compileThreeAddrForm(threeAddrProgram)
624 return interpreter.NumExpr(inputsig.encode('ascii'),
625 tempsig.encode('ascii'),
626 program, constants, input_names)
629def disassemble(nex):
630 """
631 Given a NumExpr object, return a list which is the program disassembled.
632 """
633 rev_opcodes = {}
634 for op in interpreter.opcodes:
635 rev_opcodes[interpreter.opcodes[op]] = op
636 r_constants = 1 + len(nex.signature)
637 r_temps = r_constants + len(nex.constants)
639 def parseOp(op):
640 name, sig = [*op.rsplit(b'_', 1), ''][:2]
641 return name, sig
643 def getArg(pc, offset):
644 arg = nex.program[pc + (offset if offset < 4 else offset+1)]
645 _, sig = parseOp(rev_opcodes.get(nex.program[pc]))
646 try:
647 code = sig[offset - 1]
648 except IndexError:
649 return None
651 code = bytes([code])
653 if arg == 255:
654 return None
655 if code != b'n':
656 if arg == 0:
657 return b'r0'
658 elif arg < r_constants:
659 return ('r%d[%s]' % (arg, nex.input_names[arg - 1])).encode('ascii')
660 elif arg < r_temps:
661 return ('c%d[%s]' % (arg, nex.constants[arg - r_constants])).encode('ascii')
662 else:
663 return ('t%d' % (arg,)).encode('ascii')
664 else:
665 return arg
667 source = []
668 for pc in range(0, len(nex.program), 4):
669 op = rev_opcodes.get(nex.program[pc])
670 _, sig = parseOp(op)
671 parsed = [op]
672 for i in range(len(sig)):
673 parsed.append(getArg(pc, 1 + i))
674 while len(parsed) < 4:
675 parsed.append(None)
676 source.append(parsed)
677 return source
680def getType(a):
681 kind = a.dtype.kind
682 if kind == 'b':
683 return bool
684 if kind in 'iu':
685 if a.dtype.itemsize > 4:
686 return long_ # ``long`` is for integers of more than 32 bits
687 if kind == 'u' and a.dtype.itemsize == 4:
688 return long_ # use ``long`` here as an ``int`` is not enough
689 return int_
690 if kind == 'f':
691 if a.dtype.itemsize > 4:
692 return double # ``double`` is for floats of more than 32 bits
693 return float
694 if kind == 'c':
695 return complex
696 if kind == 'S':
697 return bytes
698 if kind == 'U':
699 raise ValueError('NumExpr 2 does not support Unicode as a dtype.')
700 raise ValueError("unknown type %s" % a.dtype.name)
703def getExprNames(text, context):
704 ex = stringToExpression(text, {}, context)
705 ast = expressionToAST(ex)
706 input_order = getInputOrder(ast, None)
707 #try to figure out if vml operations are used by expression
708 if not use_vml:
709 ex_uses_vml = False
710 else:
711 for node in ast.postorderWalk():
712 if node.astType == 'op' and node.value in vml_functions:
713 ex_uses_vml = True
714 break
715 else:
716 ex_uses_vml = False
718 return [a.value for a in input_order], ex_uses_vml
721def getArguments(names, local_dict=None, global_dict=None):
722 """
723 Get the arguments based on the names.
724 """
725 call_frame = sys._getframe(2)
727 clear_local_dict = False
728 if local_dict is None:
729 local_dict = call_frame.f_locals
730 clear_local_dict = True
731 try:
732 frame_globals = call_frame.f_globals
733 if global_dict is None:
734 global_dict = frame_globals
736 # If `call_frame` is the top frame of the interpreter we can't clear its
737 # `local_dict`, because it is actually the `global_dict`.
738 clear_local_dict = clear_local_dict and not frame_globals is local_dict
740 arguments = []
741 for name in names:
742 try:
743 a = local_dict[name]
744 except KeyError:
745 a = global_dict[name]
746 arguments.append(numpy.asarray(a))
747 finally:
748 # If we generated local_dict via an explicit reference to f_locals,
749 # clear the dict to prevent creating extra ref counts in the caller's scope
750 # See https://github.com/pydata/numexpr/issues/310
751 if clear_local_dict:
752 local_dict.clear()
754 return arguments
757# Dictionaries for caching variable names and compiled expressions
758_names_cache = CacheDict(256)
759_numexpr_cache = CacheDict(256)
760_numexpr_last = {}
762evaluate_lock = threading.Lock()
764def evaluate(ex, local_dict=None, global_dict=None,
765 out=None, order='K', casting='safe', **kwargs):
766 """
767 Evaluate a simple array expression element-wise, using the new iterator.
769 ex is a string forming an expression, like "2*a+3*b". The values for "a"
770 and "b" will by default be taken from the calling function's frame
771 (through use of sys._getframe()). Alternatively, they can be specifed
772 using the 'local_dict' or 'global_dict' arguments.
774 Parameters
775 ----------
777 local_dict : dictionary, optional
778 A dictionary that replaces the local operands in current frame.
780 global_dict : dictionary, optional
781 A dictionary that replaces the global operands in current frame.
783 out : NumPy array, optional
784 An existing array where the outcome is going to be stored. Care is
785 required so that this array has the same shape and type than the
786 actual outcome of the computation. Useful for avoiding unnecessary
787 new array allocations.
789 order : {'C', 'F', 'A', or 'K'}, optional
790 Controls the iteration order for operands. 'C' means C order, 'F'
791 means Fortran order, 'A' means 'F' order if all the arrays are
792 Fortran contiguous, 'C' order otherwise, and 'K' means as close to
793 the order the array elements appear in memory as possible. For
794 efficient computations, typically 'K'eep order (the default) is
795 desired.
797 casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional
798 Controls what kind of data casting may occur when making a copy or
799 buffering. Setting this to 'unsafe' is not recommended, as it can
800 adversely affect accumulations.
802 * 'no' means the data types should not be cast at all.
803 * 'equiv' means only byte-order changes are allowed.
804 * 'safe' means only casts which can preserve values are allowed.
805 * 'same_kind' means only safe casts or casts within a kind,
806 like float64 to float32, are allowed.
807 * 'unsafe' means any data conversions may be done.
808 """
809 global _numexpr_last
810 if not isinstance(ex, str):
811 raise ValueError("must specify expression as a string")
813 # Get the names for this expression
814 context = getContext(kwargs, frame_depth=1)
815 expr_key = (ex, tuple(sorted(context.items())))
816 if expr_key not in _names_cache:
817 _names_cache[expr_key] = getExprNames(ex, context)
818 names, ex_uses_vml = _names_cache[expr_key]
819 arguments = getArguments(names, local_dict, global_dict)
821 # Create a signature
822 signature = [(name, getType(arg)) for (name, arg) in
823 zip(names, arguments)]
825 # Look up numexpr if possible.
826 numexpr_key = expr_key + (tuple(signature),)
827 try:
828 compiled_ex = _numexpr_cache[numexpr_key]
829 except KeyError:
830 compiled_ex = _numexpr_cache[numexpr_key] = NumExpr(ex, signature, **context)
831 kwargs = {'out': out, 'order': order, 'casting': casting,
832 'ex_uses_vml': ex_uses_vml}
833 _numexpr_last = dict(ex=compiled_ex, argnames=names, kwargs=kwargs)
834 with evaluate_lock:
835 return compiled_ex(*arguments, **kwargs)
838def re_evaluate(local_dict=None):
839 """
840 Re-evaluate the previous executed array expression without any check.
842 This is meant for accelerating loops that are re-evaluating the same
843 expression repeatedly without changing anything else than the operands.
844 If unsure, use evaluate() which is safer.
846 Parameters
847 ----------
849 local_dict : dictionary, optional
850 A dictionary that replaces the local operands in current frame.
852 """
853 try:
854 compiled_ex = _numexpr_last['ex']
855 except KeyError:
856 raise RuntimeError("not a previous evaluate() execution found")
857 argnames = _numexpr_last['argnames']
858 args = getArguments(argnames, local_dict)
859 kwargs = _numexpr_last['kwargs']
860 with evaluate_lock:
861 return compiled_ex(*args, **kwargs)