Example 8: Working with Matplotlib

This example shows how to generate the picture on the home page using the Python plotting library Matplotlib. (Other plotting libraries such as Bokeh and Seaborn have similar functionality.) If Matplotlib is not already installed in your working environment, do so as follows:

pip3 install matplotlib

The code consists of two parts:

  • Step 1: Create the data to be animated.

  • Step 2: Create the animation.

The data will represent a system where arrivals start light, increase suddenly, then drop off. This is represented by the following list:

arrival_schedule = [(5,  {1: 0.3, 2: 0.7}),
                    (1,  {0: 0.3, 5: 0.7}),
                    (10, {1: 0.3, 2: 0.7}),
                    (5,  {0: 0.3, 1: 0.7}),
                    (20, {0: 1.0})]

Each row of this array specifies the number of periods during which the given arrivals pmf applies. In all, it is specified for 41 periods. Including the pmf at time 0, there are 42 pmfs to be plotted.

Here is the code for Step 1:

from qplex import StandardMultiserver

engine = StandardMultiserver()
engine.service_duration_pmf = {1: 0.6, 2: 0.3, 3: 0.1}
engine.number_of_servers = 2

arrival_schedule = [(5,  {1: 0.3, 2: 0.7}),
                    (1,  {0: 0.3, 5: 0.7}),
                    (10, {1: 0.3, 2: 0.7}),
                    (5,  {0: 0.3, 1: 0.7}),
                    (20, {0: 1.0})]

pmf_list = [engine.get_number_of_entities_in_system_pmf()]
for number_of_periods, number_of_arrivals_pmf in arrival_schedule:
    for i in range(number_of_periods):
        engine.number_of_arrivals_pmf = number_of_arrivals_pmf
        engine.step()
        pmf_list.append(engine.get_number_of_entities_in_system_pmf())

z_max = max([max(pmf.keys()) for pmf in pmf_list])

And here is some of the code for Step 2:

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation, PillowWriter

ani = FuncAnimation(plt.figure(), add_frame, len(pmf_list))
ani.save('pmfs_over_time.gif', writer=PillowWriter(fps=5))

The Matplotlib function FuncAnimation repeatedly calls a function add_frame, which must be provided by the script. It obtains the appropriate pmf from pmf_list, converts it to a list plot_data, and calls various Matplotlib functions to configure the frame. Here is the code:

def add_frame(frame):
    pmf = pmf_list[frame]

    plot_data = [0] * (z_max+1)
    for value in pmf.keys():
        plot_data[value] = pmf[value]

    plt.clf()
    plt.xlim(0, z_max+1)
    plt.ylim(0, 1.05)
    plt.xticks([])
    plt.stairs(plot_data, edges=range(len(plot_data)+1))

Here is complete code:

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation, PillowWriter

from qplex import StandardMultiserver

def add_frame(frame):
    pmf = pmf_list[frame]

    plot_data = [0] * (z_max+1)
    for value in pmf.keys():
        plot_data[value] = pmf[value]

    plt.clf()
    plt.xlim(0, z_max+1)
    plt.ylim(0, 1.05)
    plt.xticks([])
    plt.stairs(plot_data, edges=range(len(plot_data)+1))

engine = StandardMultiserver()
engine.service_duration_pmf = {1: 0.6, 2: 0.3, 3: 0.1}
engine.number_of_servers = 2

arrival_schedule = [(5,  {1: 0.3, 2: 0.7}),
                    (1,  {0: 0.3, 5: 0.7}),
                    (10, {1: 0.3, 2: 0.7}),
                    (5,  {0: 0.3, 1: 0.7}),
                    (20, {0: 1.0})]

pmf_list = [engine.get_number_of_entities_in_system_pmf()]
for number_of_periods, number_of_arrivals_pmf in arrival_schedule:
    for i in range(number_of_periods):
        engine.number_of_arrivals_pmf = number_of_arrivals_pmf
        engine.step()
        pmf_list.append(engine.get_number_of_entities_in_system_pmf())

z_max = max([max(pmf.keys()) for pmf in pmf_list])

ani = FuncAnimation(plt.figure(), add_frame, len(pmf_list))
ani.save('pmfs_over_time.gif', writer=PillowWriter(fps=5))

This code creates a file pmfs_over_time.gif in the current working directory. Open this file with an application that supports animated GIFs to view the animation.