The purpose of this project is to get you thinking about automating a scientific experiment and optimizing the parameters of a process.

This is the second part of a three-part project where we'll be simulating the elephant population in Kruger National Part, South Africa. This week we'll be focusing on how varying the parameters changes the population management strategies. Rather than manually exploring the parameter, space, however, we'll automate the process and use a search method to find the optimal darting probability given the simulation parameters.


Setting Up

If you have not already done so, mount your personal space, create a new project6 folder and bring up TextWrangler and a Terminal. Also, copy your elephant.py file from project 5 into your project 6 working directory. You will need it later in the project.

searchSortedList( )

Create a new file, search.py. Then create a function searchSortedList() that takes in two parameters, a list and a value. You can use the following template.

def searchSortedList( myList, value ):
    # assign to the variable done, the value False
    # assign to the variable found, the value False
    # assign to the variable count, the value 0
    # assign to the variable maxIdx, the one less than the length of mylist
    # assign to the variable minIdx, the value 0

    # start a while loop that executes while done is not True
        # increment count  (which keeps track of how many times the loop executes

        # assign to testIndex the average of maxIdx and minIdx (use integer math)

        # if the myList value at testIndex is less than value
            # assign to minIdx the value testIndex + 1
        # elif the myList value at testIndex is greater than value
            # assign to maxIdx the value testIndex - 1
        # else
            # set done to True
            # set found to True

        # if maxIdx is less than minIdx
            # set done to True
            # set found to False

    return (found, count)

This kind of search method is called binary search, because each time through the while loop, it essentially splits the list into two parts. Because the original list is sorted, it can use only one of those two parts for the remainder of the search, ignoring the other part.

Once you have coded that up, import the random package, copy the following test function, and run it see if your function finds the value 42 in the list (it should). Think about why it can take a different number of steps each time you run the program.

def test():

    a = []
    for i in range(10000):
        a.append( random.randint(0,100000) )



    print(searchSortedList( a, 42 ))

if __name__ == "__main__":

This function is practice for creating the optimization function for the elephant simulation. That function will have a similar structure, and it will also execute a binary search, but it will have more parameters and be more flexible in how it works.

defaultParameters( )

The next task is to add a function to your elephant.py file. The function should be called defaultParameters(). It should take no arguments and return a list with all of the necessary parameters for the simulation with their default values.

elephantSim( )

Next, also in your elephant.py file, create a function elephantSim(). The arguments to the function should be the probability of darting (probDart) and a parameter list, which should have a default value of None. In other words, set it up as the following.

def elephantSim( probDart, inputParameters = None ):

The first thing to do is set up the parameters. If inputParameters is None, then assign to parameters the result of calling your defaultParameters() function. Otherwise, assign to parameters the value of inputParameters.

Assign to the probability of darting element of the parameters list the value of the probDart parameter (this should overwrite the default parameter value for darting probability).

Next, assign to a variable results the result of calling the runSimulation() function with the parameters list. Then loop 4 times, each time adding the result of calling runSimulation() to the results.

results = results + runSimulation(parameters)

This should repeat the simulation five times and collect all of the results in a single list. Because runSimulation(parameters) returns of a list of lists, the structure of results is a list of lists. Each sub-list consists of the total population and other statistics for a given simulation year.

Finally, loop over the results list and calculate the average total population. Remember, the total population for each year is the first element in each sublist. The function should return an integer value, the carrying capacity minus the average total population (cast as an integer) of the simulations.

When you are finished with this, use the following test function to evaluate your elephantSim() function. It tests the darting probability for five different values. The difference value should go from negative to positive for the default simulation parameters.

When you are done with the lab exercises, you may start on the rest of the project.

© 2019 Eric Aaron (with contributions from Colby CS colleagues).