<jupyter_start><jupyter_text>Agent-Based Traffic Model BackgroundThis model is a looped implementation of the cellular automata (CA) described by Nagel and Schreckenberg (NaSch).The NaSch CA model splits agent (vehicle) actions into four stages:1. Acceleration2. Braking3. Randomisation4. Vehicle MovementIn this implementation the 4th action is separated from the other actions to simulate simultaneous activation of the agents.This isn't strictly necessary for non-multithreaded processes but ensures that vehicle positions wouldn't cause conflicts if it were multithreaded. ImplementationThe model is written in Python using the Mesa ABM framework which allows for easy visualisation.This is a demonstration of running a Mesa model in an IPython Notebook which is an alternative to running it using javascript visualisation in a webpage.The actual model and agent code are implemented in model.py, in the same directory as this notebook.Below, we will import the model class, instantiate it, run it, and plot the average speed of the agents.<jupyter_code>import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['figure.figsize'] = [10, 6]
plt.rcParams['figure.dpi'] = 100

from model import NaSchTraffic<jupyter_output><empty_output><jupyter_text>Now we instantiate a model instance: a 1x30 grid, with a 20% chance of an agent being placed in each cell, and a max vehicle speed of 4.<jupyter_code>model = NaSchTraffic(1, 60, 5, 4, seed=1)<jupyter_output><empty_output><jupyter_text>We want to run the model until it's settles, but it's hard to tell when that is so let's just run it for 100 steps:<jupyter_code>while model.running and model.schedule.steps < 100:
    model.step()
print(model.schedule.steps) # Show how many steps have actually run<jupyter_output>100<jupyter_text>The model has a DataCollector object, which checks and stores the average speed of the agents at every step.It also collects the individual speed and position of each agent at each step.It can also generate a pandas DataFrame of the data it has collected.<jupyter_code>model_out = model.datacollector.get_model_vars_dataframe()<jupyter_output><empty_output><jupyter_text>The dataframe for the model:<jupyter_code>model_out.head()<jupyter_output><empty_output><jupyter_text>Finally, we can plot the 'AverageSpeed' series:<jupyter_code>plt.plot(model_out.AverageSpeed)
plt.xlabel('Step Number')
plt.ylabel('Average Speed')
plt.show()<jupyter_output><empty_output><jupyter_text>For testing purposes, here is the dataframe for the agents giving each agent's x position and speed at each step.<jupyter_code>agent_out = model.datacollector.get_agent_vars_dataframe()
agent_out.head()<jupyter_output><empty_output><jupyter_text>Effect of speed limit and traffic vehicle_quantity on traffic average speedNow, we can do a parameter sweep to see how speed changes against number of vehicles and the max speed.First we make a new function to collect the average speed during the second half of the simulation.<jupyter_code>from mesa.batchrunner import BatchRunner
import itertools

def get_averages(model):
    """
    Find the average speed of all the agents over the last 30 steps.
    """

    total_averages = 0
    list_length = 0
    selected_averages = itertools.islice(model.averages, 60)
    for average_speed in selected_averages:
        total_averages += average_speed
        list_length+=1

    return total_averages / list_length

model_reporters={"AverageSpeed": get_averages}<jupyter_output><empty_output><jupyter_text>Now, we set up the batch run, with a dictionary of fixed and changing parameters.Let's vary the maximum speed, and the number of vehicles.<jupyter_code>fixed_params = {"height": 1, "width": 60}
variable_parms = {"general_max_speed": range(1, 6), "vehicle_quantity": range(1, 20+1)}<jupyter_output><empty_output><jupyter_text>Then we create a batch runner object to conduct the parameter sweep.The number of iterations is the number of runs it does of the whole parameter space.<jupyter_code>param_sweep = BatchRunner(NaSchTraffic,
                          variable_parameters=variable_parms, fixed_parameters=fixed_params,
                          iterations=10,
                          max_steps=120,
                          model_reporters=model_reporters)<jupyter_output><empty_output><jupyter_text>Then we run the parameter sweep (this can take a few minutes).<jupyter_code>param_sweep.run_all()<jupyter_output>1000it [00:21, 45.57it/s]<jupyter_text>Now we create the dataframe for the data collected like we did for the single model run.<jupyter_code>df = param_sweep.get_model_vars_dataframe()
df.head()<jupyter_output><empty_output><jupyter_text>A scatter plot can be used to show how the parameters affect each other.We have varied more than one parameter, so we should try to visualise the interactions.One way of achieving this is with coloured data points:<jupyter_code>plt.scatter(df.AverageSpeed, df.general_max_speed, c=df.vehicle_quantity, cmap=plt.cm.coolwarm)
plt.xlabel('Average Speed')
plt.ylabel('Max Speed')
bar = plt.colorbar()
bar.set_label('Number of Vehicles')
plt.grid(True)<jupyter_output><empty_output><jupyter_text>If coloured data points aren't showing the trends clearly enough another option is a 3D scatter plot:<jupyter_code>from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
# fig.tight_layout(pad=4)
ax = Axes3D(fig)
ax.scatter(df.vehicle_quantity, df.general_max_speed, df.AverageSpeed, c=df.vehicle_quantity, cmap=plt.cm.coolwarm)
ax.set_zlabel('Average Speed')
plt.xlabel('Number of Vehicles')
plt.ylabel('Max Speed')
plt.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.0749822316986496, 'fraction_numerical': 0.012437810945273632, 'mean_word_length': 5.645808736717828, 'pattern_counts': {'":': 5, '<': 56, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '14', 'dir': 'jupyter-structured-clean-dedup', 'id': '95010', 'license': 'MIT', 'n_tokens_mistral': 1781, 'n_tokens_neox': 1658, 'n_words': 577, 'path': 'analysis.ipynb', 'repo_name': 'edward-ij/nasch_traffic_model', 'size': '5628'}