2. How Does It Work?

Simulus works in two ways. One way is through events. You can schedule events and process them. We call it direct event scheduling. The other way is through processes. You can create processes and have them run and interact. We call it process scheduling. Of course, there’s a third way, by combining them and having both event schedule and process scheduling in the models.

2.1. Direct Event Scheduling

In simulus, an event is simply a function to be invoked at a designated time. This can be illustrated using the following hello-world example.

2.1.1. The Hello-World Example

[1]:
# %load "../examples/basics/helloworld.py"
import simulus

def print_message():
    print("Hello world at time", sim.now)

sim = simulus.simulator()
sim.sched(print_message, until=10)
sim.run()

Hello world at time 10

In order to use the simulator, we need to first import the simulus package and then create a simulator using the simulator() function. A simulator maintains an event list where all events will be sorted in their timestamp order. A simulator also keeps the current simulation time, which gets advanced while executing the events on the event list. One can find out the current time of the simulator by inspecting the now variable of the simulator.

In the example, the function print_message() is scheduled to run at time 10. We schedule the function using the sched() method of the simulator, and passing the name of the function with the until argument to specify the time at which the function should be invoked. In discrete-event simulation terminology, the print_message() function is also called an event handler. In simulus, an event handler can be any Python function or method.

To run the simulation, we simply use the run() method of the simulator. Without an argument, the run() method will process all events on the event list; the function returns only when there are no more events on the event list.

2.1.2. Passing Arguments to Event Handlers

The next example shows how to pass arguments between sched() and the event handler.

[2]:
# %load "../examples/basics/passargs.py"
import simulus

def print_params_1(a, b, c="named"):
    print("print_params_1(a=%r, b=%r, c=%r) invoked at time %g" %
          (a, b, c, sim.now))

def print_params_2(mysim, a, b, *args, c="named", **kwargs):
    print("print_params_2(a=%r, b=%r, args=%r, c=%r, kwargs=%r) invoked at time %g" %
          (a, b, args, c, kwargs, mysim.now))

class myclass:
    def __init__(self, a):
        self.a = a

    def print_params_3(self, b, c):
        print("print_params_3(a=%r, b=%r, c=%r) invoked at %g" %
              (self.a, b, c, sim.now))

sim = simulus.simulator()
sim.sched(print_params_1, "hello", 100, until=10)
sim.sched(print_params_2, sim, "hello", 100, "yes", "no", arg1=True, arg2=2, c=1, until=20)
cls = myclass(10)
sim.sched(myclass.print_params_3, cls, 11, 12, until=30)
sim.sched(cls.print_params_3, 11, 12, until=40)
sim.run()

print_params_1(a='hello', b=100, c='named') invoked at time 10
print_params_2(a='hello', b=100, args=('yes', 'no'), c=1, kwargs={'arg1': True, 'arg2': 2}) invoked at time 20
print_params_3(a=10, b=11, c=12) invoked at 30
print_params_3(a=10, b=11, c=12) invoked at 40

In the previous hello-world example, we did not pass any arguments to the print_message() function. The sim variable is module scoped. That is, as long as the function stays in the same module where sim is defined, it’ll be fine. But this would not work in a more complicated scenario, e.g., when we develop a model that spreads into multiple python modules, or when the functions need parameters.

In this example, we schedule three functions: print_params_1() at time 10, print_params_1() at time 20, and print_params_3() at both time 30 and time 40. We can pass arguments from sched() to an event handler by placing the arguments right after the function name (the first argument of sched()). Here we take advantage of Python’s ability to take both positional arguments and keyword arguments. In Python, the single-asterisk form of *args can be used to take a non-keyword variable-length list of arguments passed to a function. The double asterisk form of **kwargs can be used to take a keyword, variable-length dictionary of arguments passed to a function.

In print_params_1(), the variables a and b are positional arguments, and c is a keyword argument. In print_params_2(), the variables mysim, a, and b are three positional arguments, which take sim, the string “hello”, and the value 100 from the call to sched(). The additional positional arguments, “yes” and “no”, are placed into the list args. The variable c is a keyword argument. The rest of the keyword arguments are placed into the dictionary kwargs.

print_params_3() is a method inside myclass. We show two ways to identify it as an event handler. One way is pass the class method as the name of the function, followed by the class instance as the first positional argument. The other way is to directly use the method of the specific class instance. Python is smart enough to unpack it to be the class method and the class instance when sched() is called.

The sched() function takes its own arguments. They are all keyword arguments, such as until. The sched() function will first filter out the recognized keyword arguments for itself, before scheduling the user intended function to be invoked at the designated time. All positional arguments and all unfiltered keyword arguments are passed onto the event handler.

In Python, the parameters of a function need to occur in a particular order: first, the formal positional arguments, then *args, followed by the keyword arguments, and finally **kwargs. That’s exactly what we did for the print_params_2 function.

2.1.3. The Life of a Professor

In simulus, one can of course schedule more functions to run within an event handler. The whole point of simulation is to help capture and examine the complicated logic or processes of the world (or system) as the events unfold.

In the next example, we show the “complicated” life of a professor.

[3]:
# %load "../examples/basics/professor.py"
import simulus

from time import gmtime, strftime
def strnow():
    return strftime("%H:%M:%S", gmtime(sim.now))

def wake_up():
    print("professor wakes up at "+strnow())
    sim.sched(start_coffee, offset=5*60) # 5 minutes from now
    sim.sched(breakfast, offset=2*3600+30*60) # 2 hours and 30 minutes from now
    sim.sched(shower, offset=2*3600+50*60) # 2 hours and 50 minutes from now
    sim.sched(leave, offset=3*3600+30*60) # 3 hours and 30 minutes from now

def start_coffee():
    print("professor starts drinking coffee at "+strnow())
    sim.sched(finish_coffee, offset=15*60) # 15 minutes from now
    sim.sched(start_read, offset=5*60) # 5 minutes from now

def finish_coffee():
    print("professor finishes drinking coffee at "+strnow())

def start_read():
    print("professor starts reading at "+strnow())
    sim.sched(finish_read, offset=2*3600) # 2 hours from now

def finish_read():
    print("professor finishes reading at "+strnow())

def breakfast():
    print("professor breakfasts at "+strnow())

def shower():
    print("professor shows at "+strnow())

def leave():
    print("professor leaves home and drives to school at "+strnow())
    sim.sched(arrive, offset=45*60) # 45 minutes from now

def arrive():
    print("professor arrives at school at "+strnow())

def meeting1():
    print("professor has first meeting at "+strnow())

def meeting2():
    print("professor has second meeting at "+strnow())

sim = simulus.simulator()
sim.sched(wake_up, until=4*3600) # 4:00
sim.sched(meeting1, until=9*3600) # 9:00
sim.sched(meeting2, until=10*3600) # 10:00
sim.run()

professor wakes up at 04:00:00
professor starts drinking coffee at 04:05:00
professor starts reading at 04:10:00
professor finishes drinking coffee at 04:20:00
professor finishes reading at 06:10:00
professor breakfasts at 06:30:00
professor shows at 06:50:00
professor leaves home and drives to school at 07:30:00
professor arrives at school at 08:15:00
professor has first meeting at 09:00:00
professor has second meeting at 10:00:00

This example is mostly self-explanatory. There’s but one subtle point. The simulation time in simulus is represented as an integer or a floating point number. In this example, we represent time in seconds from midnight. To make things a bit more comprehensible for the printout, in this example we use the standard time module in Python to convert the seconds into human readable formats: hours, minutes, and seconds.

2.1.4. Canceling and Rescheduling Events

So far, the professor’s schedule is rather boring. In real life, we make appointments and we cancel them sometimes. We also reschedule appointments. Let’s make things a bit more interesting for the professor’s life. Let’s assume that today there’s a big traffic jam. So instead of taking only 45 minutes, the professor takes 2 hours 45 minutes to get to school. Obviously, the professor will miss the first meeting and get late for the second one. So she cancels the first meeting and reschedules the second meeting.

To do that we need to use the return value from the sched() method. It is actually a reference to an event that’s got scheduled onto the simulator’s event list. Event in simulus is an opaque object. That is, we can make a reference to the event (in order to cancel or reschedule the event), but we should not directly access the member variables and methods within the event instance. In this example, we call the simulator’s cancel() and resched() methods and pass the event as the argument. The professor cancels and reschedules the events when she is about to leave home (assuming she can predict that the traffic jam is about to happen by then).

Another difference of this example from the previous one is that we make professor’s waking up a daily repeatable event (with a 24 hour interval). In this case, the professor always gets up at 4 AM every day and carries out the same set of activities (my goodness!). If we run() the simulation without an argument, it would never return as there will always be events on the event list. Because of that, in this example, we call run() with a designated simulation end-time using the keyword argument until. Specifically for this example, we run the simulation for a duration of three days.

[4]:
# %load "../examples/basics/professor-flex.py"
import simulus

from time import gmtime, strftime
def strnow():
    return strftime("%H:%M:%S", gmtime(sim.now))

def wake_up():
    print("professor wakes up at "+strnow())
    sim.sched(start_coffee, offset=5*60) # 5 minutes from now
    sim.sched(breakfast, offset=2*3600+30*60) # 2 hours and 30 minutes from now
    sim.sched(shower, offset=2*3600+50*60) # 2 hours and 50 minutes from now
    sim.sched(leave, offset=3*3600+30*60) # 3 hours and 30 minutes from now

def start_coffee():
    print("professor starts drinking coffee at "+strnow())
    sim.sched(finish_coffee, offset=15*60) # 15 minutes from now
    sim.sched(start_read, offset=5*60) # 5 minutes from now

def finish_coffee():
    print("professor finishes drinking coffee at "+strnow())

def start_read():
    print("professor starts reading at "+strnow())
    sim.sched(finish_read, offset=2*3600) # 2 hours from now

def finish_read():
    print("professor finishes reading at "+strnow())

def breakfast():
    print("professor breakfasts at "+strnow())

def shower():
    print("professor showers at "+strnow())

def leave():
    print("professor leaves home and drives to school at "+strnow())
    if sim.now < 24*3600:
        # traffic jam at the first day
        sim.sched(arrive, offset=2*3600+45*60) # 2 hours and 45 minutes from now
        # the two meetings are only at the first day
        sim.cancel(e1)
        sim.resched(e2, until=11*3600) # 11:00
    else:
        # no traffic jam in the following days
        sim.sched(arrive, offset=45*60) # 45 minutes from now

def arrive():
    print("professor arrives at school at "+strnow())

def meeting1():
    print("professor has first meeting at "+strnow())

def meeting2():
    print("professor has second meeting at "+strnow())

sim = simulus.simulator()
sim.sched(wake_up, until=4*3600, repeat_intv=24*3600) # 4:00
e1 = sim.sched(meeting1, until=9*3600) # 9:00
e2 = sim.sched(meeting2, until=10*3600) # 10:00
sim.run(until=72*3600)

professor wakes up at 04:00:00
professor starts drinking coffee at 04:05:00
professor starts reading at 04:10:00
professor finishes drinking coffee at 04:20:00
professor finishes reading at 06:10:00
professor breakfasts at 06:30:00
professor showers at 06:50:00
professor leaves home and drives to school at 07:30:00
professor arrives at school at 10:15:00
professor has second meeting at 11:00:00
professor wakes up at 04:00:00
professor starts drinking coffee at 04:05:00
professor starts reading at 04:10:00
professor finishes drinking coffee at 04:20:00
professor finishes reading at 06:10:00
professor breakfasts at 06:30:00
professor showers at 06:50:00
professor leaves home and drives to school at 07:30:00
professor arrives at school at 08:15:00
professor wakes up at 04:00:00
professor starts drinking coffee at 04:05:00
professor starts reading at 04:10:00
professor finishes drinking coffee at 04:20:00
professor finishes reading at 06:10:00
professor breakfasts at 06:30:00
professor showers at 06:50:00
professor leaves home and drives to school at 07:30:00
professor arrives at school at 08:15:00

2.1.5. More on Simulators

By default, a simulator starts at time zero. Instead, we can use the init_time argument to change the start simulation time when we create the simulator.

A simulator can also have an optional name using the name argument. If specified, the simulator’s name should be unique. In this case, one can use the get_simulator() function to retrieve the simulator using the name. If the name is not unique, a simulator created at a later time will replace the one created earlier with the same name. In that case, the earlier simulator will no longer be retrievable using the name. A simulator’s name is optional. A simulator can be created without a name. In this case, one cannot use the get_simulator() function to retrieve it. Giving a simulator a name can be useful when we deal with distributed simulation. We will discuss it more when we get to simulus’ parallel and distributed simulation support.

All the previous examples create had one simulator at a time. In fact, one can simultaneously run multiple simulators, each maintaining its own event list and its own current simulation time. If multiple simulators are created, they will all run independently. The event handlers at different simulators are invoked in separate timelines. That is, the current time at one simulator has no relation to the current time of another simulator. This is the default behavior. Simulators can also be time synchronized (using the sync() function). We will return to this topic later when we discuss the parallel and distributed simulation support of simulus.

Each simulator processes its own events when we call the run() function. We can specify either offset or until when we call run(), in which case the simulator will process all events with timestamps smaller than the designated time. The offset argument specifies a relative time from the simulator’s current simulation time. The until argument specifies an absolute time. When the run() function returns, the current time of the simulator will be advanced to the designated time and all events on the event list with timestamps smaller than the designated time have already been processed. If both offset and until are ignored, the simulator will run as long as there are events on the event list. The user should be aware that, in this case, a simulator may run forever as in some simulation models there are always new events generated when the event handlers are invoked (like in the previous example).

We can also step through simulation by processing one event at a time. This is achieved by the simulator’s step() function, which only processes the next event on the event list and advances the simulation time to the time of the event before it returns. If there is no event available on the event list, the method has no effect and returns immediately.

To determine the time of the next event, we can use the simulator’s peek() function. We can also list all future events on the event list using the simulator’s show_calendar() function. This function is supposed to be used only for debugging purposes. Listing all events on an event list can be quite expensive for some simulators.

The following example illustrates some of the functions we just mentioned in this section.

[5]:
# %load "../examples/basics/twosims.py"
import simulus

def handle(sim):
    print("'%s' handles event at time %g" % (sim.name, sim.now))

sim1 = simulus.simulator(name="sim1", init_time=100)
sim2 = simulus.simulator(name="sim2", init_time=-100)

for i in range(5, 100, 20):
    sim1.sched(handle, sim1, offset=i)
for i in range(5, 200, 30):
    sim2.sched(handle, sim2, offset=i)
sim1.show_calendar()
sim2.show_calendar()

while True:
    t1, t2 = sim1.peek(), sim2.peek()
    if t1 < simulus.infinite_time:
        sim1.step()
    if t2 < simulus.infinite_time:
        sim2.step()
    if t1 == simulus.infinite_time and \
       t2 == simulus.infinite_time:
        break

list of all future events (num=5) at time 100 on simulator 'sim1':
  105: dir_evt=handle()
  125: dir_evt=handle()
  145: dir_evt=handle()
  165: dir_evt=handle()
  185: dir_evt=handle()
list of all future events (num=7) at time -100 on simulator 'sim2':
  -95: dir_evt=handle()
  -65: dir_evt=handle()
  -35: dir_evt=handle()
  -5: dir_evt=handle()
  25: dir_evt=handle()
  55: dir_evt=handle()
  85: dir_evt=handle()
'sim1' handles event at time 105
'sim2' handles event at time -95
'sim1' handles event at time 125
'sim2' handles event at time -65
'sim1' handles event at time 145
'sim2' handles event at time -35
'sim1' handles event at time 165
'sim2' handles event at time -5
'sim1' handles event at time 185
'sim2' handles event at time 25
'sim2' handles event at time 55
'sim2' handles event at time 85

In this example, we create two simulators, the first starting from time 100 and the second from time -100. (Yes, we can use negative simulation time!) For the first simulator, we schedule a function to be invoked for 5 times at a regular time interval of 20 starting at an offset of 5 from the simulator’s start time. For the second simulator, we schedule the same function to be invoked for 7 times at a regular time interval of 30 starting at an offset of 5 from that simulator’s start time. We step through the scheduled events of the two simulators, processing one event at a time for each simulator in an alternative fashion as long as the simulation has events.

2.2. Process Scheduling

Another way to model the world is to use simulation processes. Conceptually, a process is just a thread of control, similar to running through a sequential program that constitutes a bunch of statements, if-else branches, while-loops, and function calls. During its execution, a simulation process can be suspended, either sleeping for some time, or being blocked when requesting for some resources currently unavailable. The process will resume execution after the specified time has passed or when the resource blocking conditions have been removed.

2.2.1. Hello-World Using Process

The following is a modified hello-world example, which we now use a process.

[6]:
# %load "../examples/basics/helloworld-proc.py"
import simulus

def print_message():
    for _ in range(10):
        print("Hello world at time", sim.now)
        sim.sleep(1)

sim = simulus.simulator()
sim.process(print_message, until=10)
sim.run()

Hello world at time 10
Hello world at time 11
Hello world at time 12
Hello world at time 13
Hello world at time 14
Hello world at time 15
Hello world at time 16
Hello world at time 17
Hello world at time 18
Hello world at time 19

In this example, the function print_message() is the starting function of a process. We create the process using the process() function of the simulator. We schedule the process to run at time 10 using the until argument. If the argument is missing, the process will start immediately at the same simulation time.

As with the original hello-world example that uses the sched() method, we can pass arguments to the starting function when we create the process—either as positional arguments, or keyword arguments, or both. They will be passed to the starting function when the process starts running at the designated time.

In this modified hello-world example, the process loops for 10 times; at each iteration, it prints out a hello message and sleeps for 1 second. The sleep() method takes one argument: either an offset argument, which specifies the amount of time the process will be put on hold, or an until argument, which provides the absolute simulation time at which the process should resume execution. We didn’t explicitly name the argument as offset as in this example; it’s the default positional argument.

2.2.2. The Professor’s Life as a Process

We now return to the previous example and use a process to simulate the professor’s complicated life.

[7]:
# %load "../examples/basics/professor-proc.py"
import simulus

from time import gmtime, strftime
def strnow():
    return strftime("%H:%M:%S", gmtime(sim.now))

def prof_life():
    while True:
        start_of_the_day = sim.now

        sim.sleep(4*3600) # 4 hours from midnight
        print("professor wakes up at "+strnow())

        sim.sleep(offset=5*60) # 5 minutes from now
        print("professor starts drinking coffee at "+strnow())

        sim.sleep(offset=5*60) # 5 minutes from now
        print("professor starts reading at "+strnow())

        sim.sleep(offset=(15-5)*60) # 15 minus 5 minutes from now
        print("professor finishes drinking coffee at "+strnow())

        sim.sleep(offset=2*3600-10*60) # 2 hours minus 10 minutes from now
        print("professor finishes reading at "+strnow())

        sim.sleep(until=start_of_the_day+6*3600+30*60) # 6:30
        print("professor breakfasts at "+strnow())

        sim.sleep(until=start_of_the_day+6*3600+50*60) # 6:50
        print("professor showers at "+strnow())

        sim.sleep(until=start_of_the_day+7*3600+30*60) # 7:30
        print("professor leaves home and drives to school at "+strnow())

        if sim.now < 24*3600:
            # traffic jam at the first day
            sim.sleep(offset=2*3600+45*60) # 2 hours and 45 minutes from now
            print("professor arrives at school at "+strnow())

            if sim.now < 9*3600:
                # if arrives before the 9 o'clock, attend both meetings
                sim.sleep(until=9*3600)
                print("professor has first meeting at "+strnow())

                sim.sleep(until=10*3600)
                print("professor has second meeting at "+strnow())
            else:
                # if late, no the first meeting and resched the second
                sim.sleep(until=11*3600)
                print("professor has second meeting at "+strnow())
        else:
            # no traffic jam in the following days
            sim.sleep(offset=45*60) # 45 minutes from now
            print("professor arrives at school at "+strnow())

        # the rest of the day is a blur for the professor
        rest_of_the_day(start_of_the_day)

def rest_of_the_day(start):
    # sleep until the start of the next day
    sim.sleep(until=start+24*3600)

sim = simulus.simulator()
sim.process(prof_life)
sim.run(until=72*3600)

professor wakes up at 04:00:00
professor starts drinking coffee at 04:05:00
professor starts reading at 04:10:00
professor finishes drinking coffee at 04:20:00
professor finishes reading at 06:10:00
professor breakfasts at 06:30:00
professor showers at 06:50:00
professor leaves home and drives to school at 07:30:00
professor arrives at school at 10:15:00
professor has second meeting at 11:00:00
professor wakes up at 04:00:00
professor starts drinking coffee at 04:05:00
professor starts reading at 04:10:00
professor finishes drinking coffee at 04:20:00
professor finishes reading at 06:10:00
professor breakfasts at 06:30:00
professor showers at 06:50:00
professor leaves home and drives to school at 07:30:00
professor arrives at school at 08:15:00
professor wakes up at 04:00:00
professor starts drinking coffee at 04:05:00
professor starts reading at 04:10:00
professor finishes drinking coffee at 04:20:00
professor finishes reading at 06:10:00
professor breakfasts at 06:30:00
professor showers at 06:50:00
professor leaves home and drives to school at 07:30:00
professor arrives at school at 08:15:00

Rather than having many event handlers as in the earlier example, the professor’s life now becomes one process. It’s one big loop. Each iteration of the loop represents one day of the professor’s life. As a process, the professor calls sleep() to advance the simulation time.

It is important to know that the sleep() method can only be called when a process is running, either in the starting function of the process, or in a function that’s directly or indirectly invoked by the starting function. Otherwise, simulus will raise an exception.

At each iteration, the professor goes through all the chores of the day. At the end of those chores, she calls the function rest_of_the_day() to simulate all the activities she would take for the rest of the day. We are still in the process when that function is called. In the function, the process sleeps until the beginning of the next day. After the function returns, the loop continues to the next iteration to simulate the next day.

2.2.3. Processes Everywhere

Of course, the world does not have just the professor in it. There are other professors and students, and many other people as well. If we simulate a multi-agent system, for example, each agent can be treated an autonomic entity with its own agenda. So it’s natural to simulate such a system using the processes to represent independent agents. Each agent may also use multiple processes. Recall that professor in the previous example actually has coffee and reads at the same time? Conceptually any two overlapping activities can be modeled as separate processes.

Our world is a complicated world. And processes interact. The agents in a multi-agent system may communicate and synchronize with one another and they compete for resources. Simulus provides the necessary facilities for processes to fulfill these functions. We will deal with the more complicated issues later.

As for now, let’s examine a simple example showing the creation and execution of multiple processes. We also show a simple way to synchronize the processes by having one process waiting for the completion of other processes.

[8]:
# %load "../examples/basics/homework.py"
import simulus

from random import seed, expovariate, gauss
seed(12345)

def student(student_id):
    print("student %d starts to work at %g" %
          (student_id, sim.now))
    sim.sleep(gauss(30, 5)) # work on part one
    sim.sleep(expovariate(1/10.)) # take some rest
    sim.sleep(gauss(60, 10)) # work on part two
    print("student %d finishes working at %g" %
          (student_id, sim.now))

def homework():
    print("homework assigned at "+str(sim.now))
    # five students working on the homework each starts at a random
    # time (exponentially distributed with mean of 10)
    students = []
    for i in range(5):
        s = sim.process(student, i, offset=expovariate(1/10.))
        students.append(s)
    # wait for all student processes to complete
    sim.wait(students)
    print("last student finishes homework at "+str(sim.now))

sim = simulus.simulator()
sim.process(homework, offset=10)
sim.run()

homework assigned at 10
student 1 starts to work at 10.1022
student 3 starts to work at 13.5473
student 4 starts to work at 14.5952
student 0 starts to work at 15.3892
student 2 starts to work at 27.4415
student 4 finishes working at 108.348
student 1 finishes working at 109.514
student 2 finishes working at 111.156
student 0 finishes working at 122.177
student 3 finishes working at 141.183
last student finishes homework at 141.18344556219594

In this example, a homework process is created and scheduled to run at time 10, at which the homework is said to be officially assigned. Five students, each represented as a separate process created by the homework process, will work on the assignment. Each student process starts at a random time, sleeps for some random time to represent the student’s working on the first part of the homework, sleeps again for some random time representing the student’s taking a break, and finally sleeps for another random time representing the student’s working on the second part of the homework. The homework process waits for all the student processes to finish using the magical wait() method. We will have much to say about the magic behind this method later.

This example is the first time in this tutorial we deal with random numbers. It needs a bit explanation. We use Python’s random package. In particular, we use the seed() function to select a random seed for the default random number generator, so that every time we run this example, we should get the same results. In simulation, we call the series of numbers generated by the random package pseudo-random numbers. The numbers indeed look random, but they are not really random. They follow a fixed sequence although seemingly unpredictable. As long as we start the random sequence with a fixed seed, the sequence is guaranteed be the same.

In this example, we use random numbers from two distributions. The expovariate() method generates an exponentially distributed random number. The argument to the method is one divided by the desired mean of the distribution. The gauss() method generates a Gaussian (normally) distributed random number. The arguments to the method is the desired mean and standard deviation of the normal distribution.