throbber
Case 2:15-cv-00225-JRG-RSP Document 60-5 Filed 09/28/15 Page 1 of 34 PageID #: 1046
`
`Exhibit ZTE-CC-SIMULATION
`
`Matloff, Norm,
`Introduction to Discrete-Event Simulation and
`the SimPy Language
`
`
`
`
`
`
`
`
`
`
`
`
`
`
`
`

`
`Case 2:15-cv-00225-JRG-RSP Document 60-5 Filed 09/28/15 Page 2 of 34 PageID #: 1047
`
`Introduction to Discrete-Event Simulation and the SimPy Language
`
`Norm Matloff
`
`February 13, 2008
`c2006-2008, N.S. Matloff
`
`Contents
`
`1 What Is Discrete-Event Simulation (DES)?
`
`2 World Views in DES Programming
`2.1 The Activity-Oriented Paradigm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
`
`2.2 The Event-Oriented Paradigm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
`
`2.3 The Process-Oriented Paradigm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
`
`3
`
`Introduction to the SimPy Simulation Language
`3.1 SimPy Overview .
`. .
`. .
`.
`. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
`
`3.2
`
`Introduction to SimPy Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
`
`3
`
`3
`3
`
`4
`
`6
`
`7
`8
`
`9
`
`3.2.1 MachRep1.py: Our First SimPy Program . . . . . . . . . . . . . . . . . . . . . . . 10
`
`3.2.2 MachRep2.py: Introducing the Resource Class . . . . . . . . . . . . . . . . . . . . 14
`
`3.2.3 MachRep3.py: Introducing Passivate/Reactivate Operations
`
`. . . . . . . . . . . . . 16
`
`3.2.4 MMk.py: “Do It Yourself” Queue Management . . . . . . . . . . . . . . . . . . . . 18
`
`3.2.5
`
`SMP.py: Simultaneous Possession of Resources . . . . . . . . . . . . . . . . . . . . 20
`
`3.2.6 Cell.py: Dynamic Creation of Threads . . . . . . . . . . . . . . . . . . . . . . . . . 22
`
`3.3 Note These Restrictions on PEMs
`
`. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
`
`3.4 SimPy Data Collection and Display . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
`
`3.4.1
`
`Introduction to Monitors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
`
`3.4.2 Time Averages .
`
`.
`
`.
`
`. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
`
`3.4.3 The Function Monitor.timeAverage()
`
`. . . . . . . . . . . . . . . . . . . . . . . . . 27
`
`3.4.4 But I Recommend That You Not Use This Function . . . . . . . . . . . . . . . . . . 27
`
`1
`
`

`
`Case 2:15-cv-00225-JRG-RSP Document 60-5 Filed 09/28/15 Page 3 of 34 PageID #: 1048
`
`3.4.5 Little’s Rule .
`
`.
`
`3.5 Other SimPy Features .
`
`.
`
`.
`
`.
`
`.
`
`.
`
`.
`
`. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
`
`. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
`
`A How to Obtain and Install SimPy
`
`29
`
`30
`B Debugging and Verifying SimPy Programs
`B.1 Debugging Tools
`.
`.
`.
`.
`.
`.
`. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
`
`B.2 Know How Control Transfers in SimPy Programs . . . . . . . . . . . . . . . . . . . . . . . 30
`
`B.3 Always Know What (Simulated) Time It Is
`
`. . . . . . . . . . . . . . . . . . . . . . . . . . 31
`
`B.4 Starting Over
`
`.
`
`B.5 Repeatability .
`
`.
`
`.
`
`.
`
`.
`
`. .
`
`. .
`
`. .
`
`. .
`
`.
`
`.
`
`. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
`
`. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
`
`B.6 Peeking at the SimPy’s Internal Event List . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
`
`B.7 SimPy’s Invaluable Tracing Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
`
`C Online Documentation for SimPy
`
`33
`
`2
`
`

`
`Case 2:15-cv-00225-JRG-RSP Document 60-5 Filed 09/28/15 Page 4 of 34 PageID #: 1049
`
`1 What Is Discrete-Event Simulation (DES)?
`
`Consider simulation of some system which evolves through time. There is a huge variety of such applica-
`tions. One can simulate a weather system, for instance. A key point, though, is that in that setting, the events
`being simulated would be continuous, meaning for example that if we were to graph temperature against
`time, the curve would be continuous, no breaks.
`
`By contrast, suppose we simulate the operation of a warehouse. Purchase orders come in and are filled,
`reducing inventory, but inventory is replenished from time to time. Here a typical variable would be the
`inventory itself, i.e. the number of items currently in stock for a given product. If we were to graph that
`number against time, we would get what mathematicians call a step function, i.e. a set of flat line seg-
`ments with breaks between them. The events here—decreases and increases in the inventory—are discrete
`variables, not continuous ones.
`
`DES involves simulating such systems.
`
`2 World Views in DES Programming
`
`Simulation programming can often be difficult—difficult to write the code, and difficult to debug. The
`reason for this is that it really is a form of parallel programming, with many different activities in progress
`simultaneously, and parallel programming can be challenging.
`For this reason, many people have tried to develop separate simulation languages, or at least simulation
`paradigms (i.e. programming styles) which enable to programmer to achieve clarity in simulation code.
`Special simulation languages have been invented in the past, notably SIMULA, which was invented in the
`1960s and has significance today in that it was the language which invented the concept of object-oriented
`programmg that is so popular today. However, the trend today is to simply develop simulation libraries
`which can be called from ordinary languages such as C++, instead of inventing entire new languages.1 So,
`the central focus today is on the programming paradigms, not on language. In this section we will present
`an overview of the three major discrete-event simulation paradigms.
`Several world views have been developed for DES programming, as seen in the next few sections.
`
`2.1 The Activity-Oriented Paradigm
`
`Let us think of simulating a queuing system. Jobs arrive at random times, and the job server takes a ran-
`dom time for each service. The time between arrivals of jobs, and the time needed to serve a job, will be
`continuous random variables, possibly having exponential or other continuous distributions.
`
`For concreteness, think of an example in which the server is an ATM cash machine and the jobs are cus-
`tomers waiting in line.
`Under the activity-oriented paradigm, we would break time into tiny increments. If for instance the mean
`interarrival time were, say 20 seconds, we might break time into increments of size 0.001. At each time
`point, our code would look around at all the activities, e.g. currently-active job servicing, and check for the
`possible occurrence of events, e.g. completion of service. Our goal is to find the long-run average job wait
`
`1These libraries are often called “languages” anyway, and I will do so too.
`
`3
`
`

`
`Case 2:15-cv-00225-JRG-RSP Document 60-5 Filed 09/28/15 Page 5 of 34 PageID #: 1050
`
`time.
`
`Let SimTime represent current simulated time. Our simulation code in the queue example above would look
`something like this:
`
`1
`
`2
`
`3
`
`4
`
`5
`
`6
`
`7
`
`8
`
`9
`
`10
`
`11
`
`12
`
`13
`
`14
`
`15
`
`16
`
`17
`
`18
`
`19
`
`20
`
`21
`
`22
`
`23
`
`24
`
`25
`
`26
`
`27
`
`28
`
`29
`
`QueueLength = 0
`NJobsServed = 0
`SumResidenceTimes = 0
`ServerBusy = false
`generate NextArrivalTime // random # generation
`NIncrements = MaxSimTime / 0.001
`for SimTime = 1*0.001 to NIncrements*0.001 do
`if SimTime = NextArrivalTime then
`add new jobobject to queue
`QueueLength++
`generate NextArrivalTime // random # generation
`if not ServerBusy then
`ServerBusy = true
`jobobject.ArrivalTime = SimTime
`generate ServiceFinishedtime
`currentjob = jobobject
`delete head of queue and assign to currentjob
`QueueLength--
`
`else
`if SimTime = ServiceFinishedtime then
`NJobsServed++
`SumResidenceTimes += SimTime - currentjob.ArrivalTime
`if QueueLength > 0 then
`generate ServiceFinishedtime
`delete currentjob from queue
`QueueLength--
`else
`ServerBusy = false
`print out SumResidenceTimes / NJobsServed
`
`// random # generation
`
`2.2 The Event-Oriented Paradigm
`
`Clearly, an activity-oriented simulation program is going to be very slow to execute. Most time increments
`will produce no state change to the system at all, i.e. no new arrivals to the queue and no completions of
`service by the server. Thus the activity checks will be wasted processor time. This is a big issue, because
`in general simulation code often needs a very long time to run. (Electronic chip manufacturers use DES for
`chip simulation. A simulation can take days to run.)
`
`Inspection of the above pseudocode, though, shows a way to dramatically increase simulation speed. Instead
`of having time “creep along” so slowly, why not take a “shortcut” to the next event? What we could do is
`something like the following:
`
`Instead of having the simulated time advance via the code
`
`1
`
`for SimTime = 1*0.001 to NIncrements*0.001 do
`
`we could advance simulated time directly to the time of the next event:
`
`4
`
`

`
`Case 2:15-cv-00225-JRG-RSP Document 60-5 Filed 09/28/15 Page 6 of 34 PageID #: 1051
`
`1
`
`2
`
`3
`
`4
`
`5
`
`1
`
`2
`
`3
`
`4
`
`5
`
`6
`
`7
`
`8
`
`9
`
`10
`
`11
`
`12
`
`13
`
`14
`
`15
`
`16
`
`17
`
`18
`
`19
`
`20
`
`21
`
`22
`
`23
`
`24
`
`25
`
`26
`
`27
`
`28
`
`29
`
`30
`
`31
`
`if ServerBusy and NextArrivalTime < ServiceFinishedtime or
`not ServerBusy then
`SimTime = NextArrivalTime
`
`else
`SimTime = ServiceFinishedtime
`
`(The reason for checking ServerBusy is that ServiceFinishedtime will be undefined if ServerBusy is false.)
`The entire pseudocode would then be
`
`QueueLength = 0
`NJobsServed = 0
`SumResidenceTimes = 0
`ServerBusy = false
`generate NextArrivalTime
`SimTime = 0.0;
`while (1) do
`if ServerBusy and NextArrivalTime < ServiceFinishedtime or
`not ServerBusy then
`SimTime = NextArrivalTime
`
`else
`SimTime = ServiceFinishedtime
`if SimTime > MaxSimTime then break
`if SimTime = NextArrivalTime then
`QueueLength++
`generate NextArrivalTime
`if not ServerBusy then
`ServerBusy = true
`jobobject.ArrivalTime = SimTime
`currentjob = jobobject
`generate ServiceFinishedtime
`QueueLength--
`else // the case SimTime = ServiceFinishedtime
`NJobsServed++
`SumResidenceTimes += SimTime - currentjob.ArrivalTime
`if QueueLength > 0 then
`generate ServiceFinishedtime
`QueueLength--
`else
`ServerBusy = false
`print out SumResidenceTimes / NJobsServed
`
`The event-oriented paradigm formalizes this idea. We store an event set, which is the set of all pending
`events. In our queue example above, for instance, there will always be at least one event pending, namely
`the next arrival, and sometimes a second pending event, namely the completion of a service. Our code above
`simply inspects the scheduled event times of all pending events (again, there will be either one or two of
`them in our example here), and updates SimTime to the minimum among them.
`In the general case, there may be many events in the event set, but the principle is still the same—in each
`iteration of the while loop, we update SimTime to the minimum among the scheduled event times. Note
`also that in each iteration of the while loop, a new event is generated and added to the set; be sure to look at
`the pseudocode above and verify this.
`
`5
`
`

`
`Case 2:15-cv-00225-JRG-RSP Document 60-5 Filed 09/28/15 Page 7 of 34 PageID #: 1052
`
`Thus a major portion of the execution time for the program will consist of a find-minimum operation within
`the event set. Accordingly, it is desirable to choose a data structure for the set which will facilitate this
`operation, such as a heap-based priority queue. In many event-oriented packages, though, the event set is
`implemented simply as a linearly-linked list. This will be sufficiently efficient as long as there usually aren’t
`too many events in the event set; again, in the queue example above, the maximum size of the event set is 2.
`(We will return to the issue of efficient event lists in a later unit.)
`
`Again, note the contrast between this and continuous simulation models. The shortcut which is the heart
`of the event-oriented paradigm was only possible because of the discrete nature of system change. So this
`paradigm is not possible in models in which the states are continuous in nature.
`
`The event-oriented paradigm was common in the earlier years of simulation, used in packages in which code
`in a general-purpose programming language such as C called functions in a simulation library. It still has
`some popularity today. Compared to the main alternative, the process-oriented paradigm, the chief virtues
`of the event-oriented approach are:
`
`• Ease of implementation. The process-oriented approach requires something like threads, and in those
`early days there were no thread packages available. One needed to write one’s own threads mecha-
`nisms, by writing highly platform-dependent assembly-language routines for stack manipulation.
`• Execution speed. The threads machinery of process-oriented simulation really slows down execution
`speed (even if user-level threads are used).
`• Flexibility. If for example one event will trigger two others, it is easy to write this into the application
`code.
`
`2.3 The Process-Oriented Paradigm
`
`Here each simulation activity is modeled by a process. The idea of a process is similar to the notion by
`the same name in Unix, and indeed one could write process-oriented simulations using Unix processes.
`However, these would be inconvenient to write, difficult to debug, and above all they would be slow.
`
`As noted earlier, the old process-oriented software such as SIMULA and later CSIM were highly platform-
`dependent, due to the need for stack manipulation. However, these days this problem no longer exists, due
`to the fact that modern systems include threads packages (e.g. pthreads in Unix, Java threads, Windows
`threads and so on). Threads are sometimes called “lightweight” processes.
`
`If we were to simulate a queuing system as above, but using the process-oriented paradigm, we would have
`two threads, one simulating the arrivals and the other simulating the operation of the server. Those would
`be the application-specific threads (so NumActiveAppThreads = 2 in the code below), and we would also
`have a general thread to manage the event set.
`
`Our arrivals thread would look something like
`
`1
`
`2
`
`3
`
`4
`
`5
`
`6
`
`NumActiveAppThreads++
`while SimTime < MaxSimTime do
`generate NextArrivalTime
`add an arrival event for time NextArrivalTime to the event set
`sleep until wakened by the event-set manager
`jobobject.ArrivalTime = SimTime
`
`6
`
`

`
`Case 2:15-cv-00225-JRG-RSP Document 60-5 Filed 09/28/15 Page 8 of 34 PageID #: 1053
`
`7
`
`8
`
`1
`
`2
`
`3
`
`4
`
`5
`
`6
`
`7
`
`8
`
`9
`
`10
`
`11
`
`12
`
`1
`
`2
`
`3
`
`4
`
`5
`
`6
`
`1
`
`2
`
`3
`
`4
`
`5
`
`6
`
`7
`
`add jobobject to the machine queue
`thread exit
`
`The server thread would look something like
`
`NumActiveAppThreads++
`while SimTime < MaxSimTime do
`sleep until QueueLength > 0
`while QueueLength > 0 do
`remove queue head and assign to jobobject
`QueueLength--
`generate ServiceFinishedtime
`add a service-done event for time ServiceFinishedtime to the event set
`sleep until wakened by the event-set manager
`SumResidenceTimes += SimTime - jobobject.ArrivalTime
`NJobsServed++
`thread exit
`
`The event set manager thread would look something like
`
`while SimTime < MaxSimTime do
`sleep until event set is nonempty
`delete the minimum-time event E from the event set
`update SimTime to the time scheduled for E
`wake whichever thread had added E to the event set
`thread exit
`
`The function main() would look something like this:
`
`QueueLength = 0
`NJobsServed = 0
`SumResidenceTimes = 0
`ServerBusy = false
`start the 3 threads
`sleep until all 3 threads exit
`print out SumResidenceTimes / NJobsServed
`
`Note that the event set manager would be library code, while the other modules shown above would be
`application code.
`
`Two widely used oper-source process-oriented packages are C++SIM, available at http://cxxsim.
`ncl.ac.uk and SimPy, available at http://simpy.sourceforge.net.
`
`The process-oriented paradigm produces more modular code. This is probably easier to write and easier for
`others to read. It is considered more elegant, and is the more popular of the two main world views today.
`
`3 Introduction to the SimPy Simulation Language
`
`SimPy (rhymes with “Blimpie”) is a package for process-oriented discrete-event simulation. It is written in,
`and called from, Python. I like the clean manner in which it is designed, and the use of Python generators—
`
`7
`
`

`
`Case 2:15-cv-00225-JRG-RSP Document 60-5 Filed 09/28/15 Page 9 of 34 PageID #: 1054
`
`and for that matter, Python itself—is a really strong point. If you haven’t used Python before, you can learn
`enough about it to use SimPy quite quickly; see my quick introduction to Python, at my Python tutorials
`page, http://heather.cs.ucdavis.edu/˜matloff/python.html.
`
`Instructions on how to obtain and install SimPy are given in Appendix A.
`
`Instead of using threads, as is the case for most process-oriented simulation packages, SimPy makes novel
`use of Python’s generators capability.2 Generators allow the programmer to specify that a function can be
`prematurely exited and then later re-entered at the point of last exit, enabling coroutines, meaning functions
`that alternate execution with each other. The exit/re-entry points are marked by Python’s yield keyword.
`Each new call to the function causes a resumption of execution of the function at the point immediately
`following the last yield executed in that function. As you will see below, that is exactly what we need for
`DES.
`
`For convenience, I will refer to each coroutine (or, more accurately, each instance of a coroutine), as a
`thread.3
`
`3.1 SimPy Overview
`
`Here are the major SimPy classes which we will cover in this introduction:4
`
`• Process: simulates an entity which evolves in time, e.g. one customer who needs to be served by an
`ATM machine; we will refer to it as a thread, even though it is not a formal Python thread
`• Resource: simulates something to be queued for, e.g. the machine
`
`Here are the major SimPy operations/function calls we will cover in this introduction:
`
`• activate(): used to mark a thread as runnable when it is first created
`• simulate(): starts the simulation
`• yield hold: used to indicate the passage of a certain amount of time within a thread; yield is a Python
`operator whose first operand is a function to be called, in this case a code for a function that performs
`the hold operation in the SimPy library
`• yield request: used to cause a thread to join a queue for a given resource (and start using it immedi-
`ately if no other jobs are waiting for the resource)
`• yield release: used to indicate that the thread is done using the given resource, thus enabling the next
`thread in the queue, if any, to use the resource
`• yield passivate: used to have a thread wait until “awakened” by some other thread
`2Python 2.2 or better is required. See my Python generators tutorial at the above URL if you wish to learn about generators, but
`you do not need to know about them to use SimPy.
`3This tutorial does not assume the reader has a background in threads programming. In fact, readers who do have that back-
`ground will have to unlearn some of what they did before, because our threads here will be non-preemptive, unlike the preemptive
`type one sees in most major threads packages.
`4Others will be covered in our followup tutorial at AdvancedSimpy.pdf.
`
`8
`
`

`
`Case 2:15-cv-00225-JRG-RSP Document 60-5 Filed 09/28/15 Page 10 of 34 PageID #:
` 1055
`
`• reactivate(): does the “awakening” of a previously-passivated thread
`• cancel(): cancels all the events associated with a previously-passivated thread
`Here is how the flow of control goes from one function to another:
`
`• When main() calls simulate() main() blocks. The simulation itself then begins, and main() will not
`run again until the simulation ends. (When main() resumes, typically it will print out the results of
`the simulation.)
`• Anytime a thread executes yield, that thread will pause. SimPy’s internal functions will then run, and
`will restart some thread (possibly the same thread).
`• When a thread is finally restarted, its execution will resume right after whichever yield statement was
`executed last in this thread.
`
`Note that activate(), reactivate() and cancel do NOT result in a pause to the calling function. Such a pause
`occurs only when yield is invoked. Those with extensive experience in threads programming (which, as
`mentioned, we do NOT assume here) will recognize this the non-preemptive approach to threads. In my
`opinion, this is a huge advantage, for two reasons:
`
`• Your code is not cluttered up with a lot of lock/unlock operations.
`• Execution is deterministic, which makes both writing and debugging the program much easier.
`(A disadvantage is that SimPy, in fact Python in general, cannot run in a parallel manner on multiprocessor
`machines.)
`
`3.2
`
`Introduction to SimPy Programming
`
`In
`We will demonstrate the usage of SimPy by presenting three variations on a machine-repair model.
`each case, we are modeling a system consisting of two machines which are subject to breakdown, but with
`different repair patterns:
`
`• MachRep1.py: There are two repairpersons, so that the two machines can be repaired simultaneously
`if they are both down at once.
`• MachRep2.py: Here there is only one repairperson, so if both machines are down then one machine
`must queue for the repairperson while the other machine is being repaired.
`• MachRep3.py: Here there is only one repairperson, and he/she is not summoned until both machines
`are down.
`
`In all cases, the up times and repair times are assumed to be exponentially distributed with means 1.0 and
`0.5, respectively. Now, let’s look at the three programs.5
`5You can make your own copies of these programs by downloading the raw .tex file for this tutorial, and then editing out the
`material other than the program you want.
`
`9
`
`

`
`Case 2:15-cv-00225-JRG-RSP Document 60-5 Filed 09/28/15 Page 11 of 34 PageID #:
` 1056
`
`3.2.1 MachRep1.py: Our First SimPy Program
`
`Here is the code:
`
`#!/usr/bin/env python
`
`# MachRep1.py
`
`# Introductory SimPy example: Two machines, which sometimes break down.
`# Up time is exponentially distributed with mean 1.0, and repair time is
`# exponentially distributed with mean 0.5. There are two repairpersons,
`# so the two machines can be repaired simultaneously if they are down
`# at the same time.
`
`# Output is long-run proportion of up time. Should get value of about
`# 0.66.
`
`import SimPy.Simulation # required
`import random
`
`class G: # global variables
`Rnd = random.Random(12345)
`
`class MachineClass(SimPy.Simulation.Process):
`UpRate = 1/1.0 # reciprocal of mean up time
`RepairRate = 1/0.5 # reciprocal of mean repair time
`TotalUpTime = 0.0 # total up time for all machines
`NextID = 0 # next available ID number for MachineClass objects
`def __init__(self): # required constructor
`SimPy.Simulation.Process.__init__(self) # must call parent constructor
`# instance variables
`self.StartUpTime = 0.0 # time the current up period started
`self.ID = MachineClass.NextID
`# ID for this MachineClass object
`MachineClass.NextID += 1
`def Run(self): # required constructor
`while 1:
`# record current time, now(), so can see how long machine is up
`self.StartUpTime = SimPy.Simulation.now()
`# hold for exponentially distributed up time
`UpTime = G.Rnd.expovariate(MachineClass.UpRate)
`yield SimPy.Simulation.hold,self,UpTime # simulate UpTime
`# update up time total
`MachineClass.TotalUpTime += SimPy.Simulation.now() - self.StartUpTime
`RepairTime = G.Rnd.expovariate(MachineClass.RepairRate)
`# hold for exponentially distributed repair time
`yield SimPy.Simulation.hold,self,RepairTime
`
`def main():
`SimPy.Simulation.initialize() # required
`# set up the two machine threads
`for I in range(2):
`# create a MachineClass object
`M = MachineClass()
`# register thread M, executing M’s Run() method,
`SimPy.Simulation.activate(M,M.Run()) # required
`# run until simulated time 10000
`MaxSimtime = 10000.0
`SimPy.Simulation.simulate(until=MaxSimtime) # required
`print "the percentage of up time was", \
`MachineClass.TotalUpTime/(2*MaxSimtime)
`
`if __name__ == ’__main__’: main()
`
`First, some style issues:
`
`10
`
`1
`
`2 3
`
`4 5
`
`6
`7
`8
`9
`10
`11
`12
`13
`14
`15
`16
`17
`18
`19
`20
`21
`22
`23
`24
`25
`26
`27
`28
`29
`30
`31
`32
`33
`34
`35
`36
`37
`38
`39
`40
`41
`42
`43
`44
`45
`46
`47
`48
`49
`50
`51
`52
`53
`54
`55
`56
`57
`58
`
`

`
`Case 2:15-cv-00225-JRG-RSP Document 60-5 Filed 09/28/15 Page 12 of 34 PageID #:
` 1057
`
`• My style is to put all global variables into a Python class, which I usually call G. See my Python
`introductory tutorial, cited earlier, if you wish to know my reasons.
`• In order to be able to use debugging tools, I always define a function main() which is my “main”
`program, and include the line
`
`if __name__ == ’__main__’: main()
`
`Again, see my Python introductory tutorial if you wish to know the reasons.
`• In this first SimPy example, I am using the “wordier” form of Python’s import facility:
`
`import SimPy.Simulation
`
`This leads to rather cluttered code, such as
`
`SimPy.Simulation.simulate(until=MaxSimtime)
`
`instead of
`
`simulate(until=MaxSimtime)
`
`The latter could be used had we done the import via
`
`from SimPy.Simulation import *
`
`But in this first SimPy program, I wanted to clearly distinguish SimPy’s functions from the others.
`The same holds for the functions in the Python library random. So, in this program, we use long
`names.
`
`Let’s look at main(). Since we are simulating two machines, we create two objects of our MachineClass
`class. These will be the basis for our two machine threads. Here MachineClass is a class that I wrote, as a
`subclass of SimPy’s built-in class Process.
`By calling SimPy’s activate() function on the two instances of MachineClass, we tell SimPy to create a
`thread for each of them, which will execute the Run() function for their class. This puts them on SimPy’s
`internal “ready” list of threads that are ready to run.
`The call to SimPy’s simulate() function starts the simulation. The next statement, the print, won’t execute
`for quite a while, since it won’t be reached until the call to simulate() returns, and that won’t occur until the
`end of the simulation.
`Python allows named arguments in function calls,6, and this feature is used often in the SimPy library. For
`example, SimPy’s simulate() function has many arguments, one of which is named until.7 In our call here,
`we have only specified the value of until, omitting the values of the other arguments. That tells the Python
`interpreter that we accept whatever default values the other arguments have, but we want the argument until
`to have the value 10000.0. That argument has the meaning that we will run the simulation for a simulated
`time span of duration 10000.0.
`In general, I’ll refer to the functions like MachineClass.Run() in this example) as process execution meth-
`ods (PEMs). (Functions in Python are called methods.)
`6See my Python introductory tutorial.
`7Look in the file Simulation.py of the SimPy library to see the entire code for simulate().
`
`11
`
`

`
`Case 2:15-cv-00225-JRG-RSP Document 60-5 Filed 09/28/15 Page 13 of 34 PageID #:
` 1058
`
`The object G.Rnd is an instance of the Random class in the random module of the Python library. This
`will allow us to generate random numbers, the heart of the simulation. We have arbitrarily initialized the
`seed to 12345.
`
`Since we are assuming up times and repair times are exponentially distributed, our code calls the function
`random.Random.expovariate(). Its argument is the reciprocal of the mean. Here we have taken the mean
`up time and repair times to be 1.0 and 0.5, respectively, just as an example.
`Note too that Python’s random class contains a variety of random number generators. To see what is
`available, get into interactive mode in Python and type
`
`>>> import random
`>>> dir(random)
`
`To find out what the functions do, use Python’s online help facility, e.g.
`
`>>> help(random.expovariate)
`
`The call to SimPy’s initialize() function is required for all SimPy programs.
`Now, let’s look at MachineClass. First we define two class variables,8 TotalUpTime and NextID. As
`the comment shows, TotalUpTime will be used to find the total up time for all machines, so that we can
`eventually find out what proportion of the time the machines are up. Be sure to make certain you understand
`why TotalUpTime must be a class variable rather than an instance variable.
`().9 Since our class here, MachineClass, is a subclass
`init
`Next, there is the class’ constructor function,
`of the SimPy built-in class Process, the first thing we must do is call the latter’s constructor; our program
`will not work if we forget this (it will also fail if we forget the argument self in either constructor).
`Finally, we set several of the class’ instance variables, explained in the comments. Note in particular the ID
`variable. You should always put in some kind of variable like this, not necessarily because it is used in the
`simulation code itself, but rather as a debugging aid.
`
`If you have experience with pre-emptive thread systems, note that we did NOT need to protect the line
`
`MachineClass.NextID += 1
`
`with a lock variable. This is because a SimPy thread retains control until voluntarily relinquishing it via a
`yield. Our thread here will NOT be interrupted in the midst of incrementing MachineClass.NextID.
`Now let’s look at the details of Machine.Run(), where the main action of the simulation takes place.
`The SimPy function now() yields the current simulated time. We are starting this machine in up mode, i.e.
`no failure has occurred yet. Remember, we want to record how much of the time each machine is up, so
`we need to have a variable which shows when the current up period for this machine began. With this in
`mind, we had our code self.StartUpTime = SimPy.Simulation.now() record the current time, so that later
`the code
`8If you are not familiar with the general object-oriented programming terms class variable and instance variable, see my
`Python introductory tutorial.
`9Some programmers consider this to be a bit different from a constructor function, but I’ll use that term here.
`
`12
`
`

`
`Case 2:15-cv-00225-JRG-RSP Document 60-5 Filed 09/28/15 Page 14 of 34 PageID #:
` 1059
`
`MachineClass.TotalUpTime += SimPy.Simulation.now() - self.StartUpTime
`
`will calculate the duration of this latest uptime period, and add it to our running total.
`Again, make sure you understand why StartUpTime needs to be an instance variable rather than a class
`variable.
`
`A point to always remember about simulation programming is that you must constantly go back and forth
`between two mental views of things. On the one hand, there is what I call the “virtual reality” view, where
`you are imagining what would happen in the real system you are simulating. On the other hand, there is the
`“nuts and bolts programming” view, in which you are focused on what actual program statesments do. With
`these two views in mind, let’s discuss the lines
`
`UpTime = G.Rnd.expovariate(MachineClass.UpRate)
`yield SimPy.Simulation.hold,self,UpTime
`
`First, from a “virtual reality” point of view, what the yield does is simulate the passage of time, specifically,
`UpTime amount of time, while the machine goes through an up period, at the end of which a breakdown
`occurs.
`Now here’s the “nuts and bolts programming” point of view: Python’s yield construct is a like a return,
`as it does mean an exit from the function and the passing of a return value to the caller. In this case, that
`return value is the tuple (SimPy.Simulation.hold,self,UpTime). Note by the way that the first element in
`that tuple is in SimPy cases always the name of a function in the SimPy library. The difference between
`yield and return is that the “exit” from the function is only temporary. The SimPy internals will later call
`this function again, and instead of starting at the beginning, it will “pick up where it left off.” In other words,
`the statement
`
`yield SimPy.Simulation.hold,self,UpTime
`
`will cause a temporary exit from the function but later we will come back and resume execution at the line
`
`MachineClass.TotalUpTime += SimPy.Simulation.now() - self.StartUpTime
`
`The term “yield” alludes to the fact that this thread physically relinquishes control of the Python interpreter.
`Execution of this thread will be suspended, and another thread will be run. Later, after simulated time
`has advanced to the end of the up period, control will return to this thread, resuming exactly where the
`suspension occurred.
`
`The second yield,
`
`RepairTime = G.Rnd.expovariate(MachineClass.RepairRate)
`yield SimPy.Simulation.hold,self,RepairTime
`
`works similarly, suspending execution of the thread for a simulated exponentially-distributed amount of time
`to model the repair time.
`In other words, the while loop within MachineClass.Run() simulates a repeated cycle of up time, down
`time, up time, down time, ... for this machine.
`
`13
`
`

`
`Case 2:15-cv-00225-JRG-RSP Document 60-5 Filed 09/28/15 Page 15 of 34 PageID #:
` 1060
`
`It is very important to understand how control transfers back and

This document is available on Docket Alarm but you must sign up to view it.


Or .

Accessing this document will incur an additional charge of $.

After purchase, you can access this document again without charge.

Accept $ Charge
throbber

Still Working On It

This document is taking longer than usual to download. This can happen if we need to contact the court directly to obtain the document and their servers are running slowly.

Give it another minute or two to complete, and then try the refresh button.

throbber

A few More Minutes ... Still Working

It can take up to 5 minutes for us to download a document if the court servers are running slowly.

Thank you for your continued patience.

This document could not be displayed.

We could not find this document within its docket. Please go back to the docket page and check the link. If that does not work, go back to the docket and refresh it to pull the newest information.

Your account does not support viewing this document.

You need a Paid Account to view this document. Click here to change your account type.

Your account does not support viewing this document.

Set your membership status to view this document.

With a Docket Alarm membership, you'll get a whole lot more, including:

  • Up-to-date information for this case.
  • Email alerts whenever there is an update.
  • Full text search for other cases.
  • Get email alerts whenever a new case matches your search.

Become a Member

One Moment Please

The filing “” is large (MB) and is being downloaded.

Please refresh this page in a few minutes to see if the filing has been downloaded. The filing will also be emailed to you when the download completes.

Your document is on its way!

If you do not receive the document in five minutes, contact support at support@docketalarm.com.

Sealed Document

We are unable to display this document, it may be under a court ordered seal.

If you have proper credentials to access the file, you may proceed directly to the court's system using your government issued username and password.


Access Government Site

We are redirecting you
to a mobile optimized page.





Document Unreadable or Corrupt

Refresh this Document
Go to the Docket

We are unable to display this document.

Refresh this Document
Go to the Docket