---
jupytext:
  encoding: '# -*- coding: utf-8 -*-'
  formats: md:myst
  text_representation:
    extension: .md
    format_name: myst
    format_version: 0.13
    jupytext_version: 1.15.2
---

+++ {"editable": true, "slideshow": {"slide_type": ""}}

# RidePy Tutorial 1: Basics
This tutorial covers the basics of using RidePy: Setting up a simulation, running it, and doing basic analytics on it.

+++ {"editable": true, "slideshow": {"slide_type": ""}}

## Configuring the simulation and supplying initial values
### Choosing a simulation space

+++ {"editable": true, "slideshow": {"slide_type": ""}}

The first important step for performing a RidePy simulation is the choice of the physical space that the simulations should be run on. For this example, we choose the Euclidean 2D `TransportSpace` from the `util` package.

```{code-cell} ipython3
---
editable: true
slideshow:
  slide_type: ''
---
from ridepy.util.spaces import Euclidean2D

space = Euclidean2D()
```

+++ {"editable": true, "slideshow": {"slide_type": ""}}

### Choosing a way of generating requests for transportation

The basis for RidePy simulations are `TransportationRequest`s. Each of these consists of:

- `origin`
- `destination`
- `pickup_timewindow_min`
- `pickup_timewindow_max`
- `delivery_timewindow_min`
- `delivery_timewindow_max`

To generate these `TransportationRequest`s, we will use the `RandomRequestGenerator` from the `util` package:

```{code-cell} ipython3
---
editable: true
slideshow:
  slide_type: ''
---
from ridepy.util.request_generators import RandomRequestGenerator

rg = RandomRequestGenerator(
    rate=10,
    max_pickup_delay=3,
    max_delivery_delay_rel=1.9,
    space=space,
    seed=42,
)
```

+++ {"editable": true, "slideshow": {"slide_type": ""}}

### Setting up a fleet of vehicles

RidePy manages a fleet of vehicles using a `FleetState` object. It consumes a dictionary of `initial_locations` which maps arbitrary vehicle ids to their starting position in the simulation. The number of vehicles to set up is inferred from the number of entries in the `initial_conditions` dict. 

In addition, the fleet state needs to know about the space used for the simulation, and about the desired `Dispatcher`. The dispatcher function contains the algorithm that determines how stops to serve incoming requests are scheduled. In this case, we use the included `BruteForceTotalTravelTimeMinimizingDispatcher`.

```{code-cell} ipython3
---
editable: true
slideshow:
  slide_type: ''
---
n_buses = 50
initial_location = (0, 0)
```

```{code-cell} ipython3
---
editable: true
slideshow:
  slide_type: ''
---
from ridepy.fleet_state import SlowSimpleFleetState
from ridepy.vehicle_state import VehicleState
from ridepy.util.dispatchers import BruteForceTotalTravelTimeMinimizingDispatcher

fs = SlowSimpleFleetState(
    initial_locations={vehicle_id: initial_location for vehicle_id in range(n_buses)},
    seat_capacities=8,
    space=space,
    dispatcher=BruteForceTotalTravelTimeMinimizingDispatcher(),
    vehicle_state_class=VehicleState,
)
```

+++ {"editable": true, "slideshow": {"slide_type": ""}}

## Performing a simulation

To run the simulation we first take a slice of, in this case, 100 random requests out of the request generator we set up above...

```{code-cell} ipython3
---
editable: true
slideshow:
  slide_type: ''
---
import itertools as it

transportation_requests = it.islice(rg, 100)
```

+++ {"editable": true, "slideshow": {"slide_type": ""}}

...and feed them into `FleetState.simulate`. Note that both of these operations use iterators and no computation actually happens until the iterators are exhausted. For `FleetState.simulate`, this happens when we cast its output into a Python list. In the case of the request generator, the output is an iterator of `TransportationRequest` objects, in the case of the `simulate` method an iterator of `Event` objects. These events describe e.g. that a request was accepted or that a "customer" or "rider" (represented by its `TransportationRequest`) was picked up or delivered to her destination.

```{code-cell} ipython3
---
editable: true
slideshow:
  slide_type: ''
---
# exhaust the simulator's iterator
events = list(fs.simulate(transportation_requests))
```

+++ {"editable": true, "slideshow": {"slide_type": ""}}

## Processing the results

+++ {"editable": true, "slideshow": {"slide_type": ""}}

Running the simulations leaves us with a bunch of the events described above. This means that the raw output of the simulator looks something like this:

```{code-cell} ipython3
---
editable: true
slideshow:
  slide_type: ''
---
events[200:203]
```

+++ {"editable": true, "slideshow": {"slide_type": ""}}

### Obtaining all vehicle stops and requests

+++ {"editable": true, "slideshow": {"slide_type": ""}}

To directly gain some insights from these raw events, we use the `get_stops_and_requests` function from the `analytics` package. It consumes the raw events (and the simulation space) and creates two pandas dataframes: `stops`, and `requests`.

```{code-cell} ipython3
---
editable: true
slideshow:
  slide_type: ''
---
from ridepy.util.analytics import get_stops_and_requests

stops, requests = get_stops_and_requests(events=events, space=space)
```

+++ {"editable": true, "slideshow": {"slide_type": ""}}

`stops` contains the stoplists (retrospective "schedules") of all vehicles operated during the simulation:

```{code-cell} ipython3
---
editable: true
slideshow:
  slide_type: ''
tags: [output_scroll]
---
stops.iloc[5]
```

+++ {"editable": true, "slideshow": {"slide_type": ""}}

`requests` on the other hand contains all requests that we submitted by the request generator, along with detailed information about their status and service properties:

```{code-cell} ipython3
---
editable: true
slideshow:
  slide_type: ''
tags: [output_scroll]
---
requests.iloc[5]
```

+++ {"editable": true, "slideshow": {"slide_type": ""}}

### Further Analyzing the results

Using the `stops` and `requests` dataframes, it's now straightforward to analyze the simulation run further.

First, import some appropriate tooling:

```{code-cell} ipython3
---
editable: true
slideshow:
  slide_type: ''
---
%matplotlib inline

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
```

+++ {"editable": true, "slideshow": {"slide_type": ""}}

#### Relative travel time distribution
For example, we may obtain the distribution of the relative travel times (travel time using the service, compared to the direct trip distance)...

```{code-cell} ipython3
---
editable: true
slideshow:
  slide_type: ''
---
fig, ax = plt.subplots(figsize=(4,3), dpi=130)
requests[("inferred", "relative_travel_time")].hist(bins=np.r_[1:5:10j], ax=ax)
ax.grid(False)
ax.set_xlabel('Relative travel time')
ax.set_ylabel('Number of requests')
ax.set_yscale("log")
```

+++ {"editable": true, "slideshow": {"slide_type": ""}}

#### Waiting time distribution
... or of the waiting times (time between request submission and pick-up).

```{code-cell} ipython3
fig, ax = plt.subplots(figsize=(4,3), dpi=130)
requests[("inferred", "waiting_time")].hist(bins=np.r_[1:5:10j], ax=ax)
ax.grid(False)
ax.set_xlabel('Waiting time')
ax.set_ylabel('Number of requests')
ax.set_yscale("log")
```

```{code-cell} ipython3

```