Numexpr tutorial¶

Numexpr is a powerful module for fast evaluations of numerical expressions that leads to significant time and memory savings, compare to numpy, when used for complex expressions.

In [1]:
import numexpr as ne

Local and global variables in Python¶

global variables¶

In [2]:
A = 2

def f():
    # global variable
    print("A ** 2 = ", A ** 2)

f()
A ** 2 =  4

local variables¶

In [3]:
A = 2

def f():
    A = 3
    
    # global variable
    print("A ** 2 = ", A ** 2)

f()
A ** 2 =  9

Local and global variables in Numexpr¶

default global variables¶

In [4]:
A = 2

def f():
    # global variable
    print("A ** 2 = ", ne.evaluate("A ** 2"))

f()
A ** 2 =  4

default local variables¶

In [5]:
A = 2

def f():
    A = 3
    
    # global variable
    print("A ** 2 = ", ne.evaluate("A ** 2"))

f()
A ** 2 =  9

Customized local and global variables¶

However, you can manually specify the local and global variables in ne.evaluate using optional parameters local_dict and global_dict.

First, declare dictionaries

In [6]:
variables1 = {"A" : 2, "B" : 3}
variables2 = {"A" : 3, "B" : 4}

then try this

In [7]:
ne.evaluate("A + B", local_dict=variables1)
Out[7]:
array(5, dtype=int64)
In [8]:
ne.evaluate("A + B", local_dict=variables1, global_dict=variables2)
Out[8]:
array(5, dtype=int64)
In [9]:
ne.evaluate("A + B", local_dict=variables2)
Out[9]:
array(7, dtype=int64)
In [10]:
ne.evaluate("A + B", local_dict=variables2, global_dict=variables1)
Out[10]:
array(7, dtype=int64)

One can combine the local dictionary and optional parameter local_dict. In the example below, A is taken from the local scope, whereas B is from the specified global variables variables1:

In [11]:
A = 1
ne.evaluate("A + B", global_dict=variables1)
Out[11]:
array(4, dtype=int64)

Here both A and B are from variables1:

In [12]:
A = 1
ne.evaluate("A + B", local_dict=variables1)
Out[12]:
array(5, dtype=int64)

More advanced example of customized local and global variables¶

In the following code, values of A and B are extracted from the global scope:

In [13]:
A = 1
B = 2

class Tes():
    def __init__(self):
        
        self.A = 3
        self.B = 5
        
    def eval(self):
        return ne.evaluate("A + B")

Tes().eval()
Out[13]:
array(3, dtype=int64)

To access self.A and self.A as A and B, use local_dict=vars(self)

In [14]:
A = 1
B = 2

class Tes():
    def __init__(self):
        
        self.A = 3
        self.B = 5
        
    def eval(self):
        return ne.evaluate("A + B", local_dict=vars(self))

Tes().eval()
Out[14]:
array(8, dtype=int64)

This pattern is heavily used in the codes at QuantumClassicalDynamics.

The previous code is equivalent to

In [15]:
A = 1
B = 2

class Tes():
    def __init__(self):
        
        self.A = 3
        self.B = 5
        
    def eval(self):
        A = self.A
        B = self.B
        return ne.evaluate("A + B")

Tes().eval()
Out[15]:
array(8, dtype=int64)
In [ ]: