This notebook contains material from CBE40455-2020; content is available on Github.

3.1 Python Generators

3.1.1 A simple simulation model

As a simple demonstration, let's consider the task of modeling the progess of a disease in a single person. The person will go through several stages of the disease

$$\text{Uninfected} \quad \longrightarrow \quad \text{Exposed} \quad \longrightarrow \quad \text{Infectious} \quad \longrightarrow \quad \text{Recovered}$$

3.1.1.1 Python generators

Python generators are the basic building block for discrete event simulation in Python. Python generators are similar to functions with three profound differences:

  1. They return values using the yield statement rather than a return statement. They don't disappear from memory until all statements have been executed.
  2. Because they don't disappear, they can start and be restarted with a next statement.
  3. Multiple instances of a generator may be working at the same time.
In [24]:
def a_function():
    return 3

a_function()
Out[24]:
3
In [25]:
def a_generator():
    yield 3
    
a_generator()
Out[25]:
<generator object a_generator at 0x7fd645f02450>

Using a generator is different from a function because they don't disappear from memory

In [27]:
a = a_generator()
next(a)
Out[27]:
3

Let's create a generator that can return multiple values.

In [34]:
def my_first_generator():
    yield 1
    yield 2
    yield 3
    yield 4

a = my_first_generator()
print(next(a))
1

Let's get the next value.

In [35]:
print(next(a))
2

Let's create a second instance of the same generator.

In [36]:
b = my_first_generator()
print(next(b))
1

Note how the instances each retain a unique state.

In [38]:
print(next(a))
print(next(b))
4
2

3.1.1.1.1 Exercise

Create a generator that produces a sequence of successive squares.

In [41]:
def succ_sq():
    k = 0
    while True:
        yield k**2
        k = k + 1
        
a = succ_sq()
print(next(a))
print(next(a))
print(next(a))
print(next(a))
0
1
4
9

3.1.1.1.2 Exercise

Create generator that creates the Fibonacci sequence

$$\begin{align*} F_0 & = 0 \\ F_1 & = 1 \\ F_{n} &= F_{n-1} + F_{n-2} \end{align*}$$
In [45]:
def fib():
    a = 0
    b = 1
    yield a
    yield b
    while True:
        a, b = b, a+b
        yield b
        
f = fib()
for k in range(0, 20):
    print(next(f))
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181

3.1.1.1.3 Exercise

Write a Python generator that simulates the response of a differential equation describing concentration in a stirred tank reactor.

$$\frac{dC}{dt} = -k C + q(t)$$

where k = 1.0, C(0) = 1.0 and q(t) = 0.5. Use Euler's approximation.

In [73]:
q = 0.5

def reactor(dt, k):
    C = 1.0
    t = 0.0
    while t <= 5.0:
        yield t, C
        t = t + dt
        C = C - k*C*dt + q*dt
        
a = reactor(0.2, 1.0)
for t, C in a:
    print(round(t, 2), round(C, 3))
0.0 1.0
0.2 0.9
0.4 0.82
0.6 0.756
0.8 0.705
1.0 0.664
1.2 0.631
1.4 0.605
1.6 0.584
1.8 0.567
2.0 0.554
2.2 0.543
2.4 0.534
2.6 0.527
2.8 0.522
3.0 0.518
3.2 0.514
3.4 0.511
3.6 0.509
3.8 0.507
4.0 0.506
4.2 0.505
4.4 0.504
4.6 0.503
4.8 0.502