Module extras

This package contains additional functionality that is not a core part of the simulator but rather makes it more convenient to set up and use it. Currently this involves code for performing multiple simulations while varying parameters (“parameter scan”), reading and writing parameter sets and simulation output to and from disk as JSON/JSON Lines. In addition, convenience methods for easily creating graph transport spaces are included.

Simulations and Parameter Scans

This module allows to configure and execute a set of simulations while varying specific parameters, i.e. performing a parameter scan. The typical workflow is as follows:

# create SimulationSet instance
simulation_set = SimulationSet(
    base_params={"general": {"n_reqs": 10}},
    product_params={"general": {"n_vehicles": [10, 100], "seat_capacity": [2, 8]}},
    data_dir=tmp_path,
    debug=True,
)

# execute simulations
simulation_set.run()

Parameter Configuration

SimulationSet takes three main arguments: base_params, zip_params and product_params. Base parameters are parameters which are kept constant across all simulations defined by the simulation set. Here, the values of the inner dict are the actual parameters. For zip and product parameters, lists of values are supplied as the inner dictionary’s values. Zip parameters are varied simultaneously across the simulations, i.e., the first simulation will use the first parameter value for all of the parameters in zip_params, the second simulation will use the second parameter values, and so on. For zip parameters it is important that all lists of parameter values are of equal length. The lists in product parameters on the other hand will be multiplied as a Cartesian product. Here the lengths do not have to match, all possible combinations will be simulated.

Each of the arguments takes a dictionary of dictionaries. Currently, four top-level keys are supported: general, dispatcher, request_generator, and analytics. The inner dictionaries contain the actual parameters to be varied. The structure of the outer dictionary is thus as follows:

{
    "general": {...},
    "dispatcher": {...},
    "request_generator": {...},
    "analytics": {...},
}

If any of the top-level keys are missing, the respective parameters are taken from the default base parameters.

Currently, the following parameters are supported:

  • Valid keys for general:

    • Either n_reqs: int or t_cutoff: float. If setting t_cutoff, n_reqs must be set to None and vice versa.

    • Either n_vehicles: int and initial_location: Location or initial_locations: dict[ID, Location]. If setting initial_locations, n_vehicles and initial_location must both be set to None and vice versa.

    • seat_capacity: int – Seat capacity of the vehicles

    • space: TransportSpace – The transport space to use

    • transportation_request_cls: Type[TransportationRequest] – The TransportationRequest class to use (primarily necessary to switch between the Python and Cython implementations)

    • vehicle_state_cls: Type[VehicleState] – The VehicleState class to use (again, primarily necessary to switch between the Python and Cython implementations)

    • fleet_state_cls: Type[FleetState] – The FleetState class to use (again, primarily necessary to switch between the Python and Cython implementations)

  • Valid values for dispatcher:

    • dispatcher_cls: Type[Dispatcher] – The dispatcher type to use

    • Any dispatcher keyword argument, will be supplied to the dispatcher upon instantiation

  • Valid values for request_generator:

    • request_generator: Type[RequestGenerator] – The request generator type to use

    • Any request generator keyword argument, will be supplied to the request generator upon instantiation

  • Valid values for analytics:

    • d_avg: float – Average direct request distance.

As for the top-level keys, if any of the inner keys are missing, the respective parameters are taken from the default base parameters in SimulationSet, which are currently set as follows:

{
    "general": {
        "n_reqs": 100,
        "t_cutoff": None,
        "space": Euclidean2D(coord_range=[(0, 1), (0, 1)], velocity=1),
        "n_vehicles": 10,
        "initial_location": (0, 0),
        "initial_locations": None,
        "seat_capacity": 8,
        "transportation_request_cls": ridepy.data_structures.TransportationRequest,
        "vehicle_state_cls": ridepy.vehicle_state.VehicleState,
        "fleet_state_cls": ridepy.fleet_state.SlowSimpleFleetState,
    },
    "dispatcher": {
        "dispatcher_cls": ridepy.util.dispatchers.ridepooling.BruteForceTotalTravelTimeMinimizingDispatcher
    },
    "request_generator": {
        "request_generator_cls": ridepy.util.request_generators.RandomRequestGenerator,
        "rate": 1,
    },
}

In Cython mode, the respective Cython/C++ implementations of the TransportSpace, Dispatcher, TransportationRequest, and VehicleState classes are used:

{
    "general": {
        "n_reqs": 100,
        "t_cutoff": None,
        "space": Euclidean2D(velocity=1.0),
        "n_vehicles": 10,
        "initial_location": (0, 0),
        "initial_locations": None,
        "seat_capacity": 8,
        "transportation_request_cls": ridepy.data_structures_cython.data_structures.TransportationRequest,
        "vehicle_state_cls": ridepy.vehicle_state_cython.vehicle_state.VehicleState,
        "fleet_state_cls": ridepy.fleet_state.SlowSimpleFleetState,
    },
    "dispatcher": {
        "dispatcher_cls": ridepy.util.dispatchers_cython.dispatchers.BruteForceTotalTravelTimeMinimizingDispatcher
    },
    "request_generator": {
        "request_generator_cls": ridepy.util.request_generators.RandomRequestGenerator,
        "rate": 1,
    },
}

The order of precedence is, last taking highest: default_base_params, base_params, zip_params, product_params.

Executing simulations

Simulations are executed when SimulationSet.run() is called. Independent simulations are performed through executing perform_single_simulation() for each parameter set using multiprocessing. The events that are generated by the simulation are written to disk in the JSON Lines format. The simulation parameters are also written to disk, in separate JSON files. This includes all data necessary to perform the respective simulation. For more detail, see JSON IO. For each simulation run, a unique identfier is generated and the data is stored to <uuid>.jsonl for the events and <uuid>_params.json for the simulation parameters. The identifier hashes the parameter set, thereby allowing to continue an interrupted simulation set run later. The IDs generated can be retrieved using SimulationSet.simulation_ids. Alternatively the filenames of the resulting JSON/JSONL files are also directly available through SimulationSet.param_paths and SimulationSet.event_paths.

class SimulationSet(*, data_dir, base_params=None, zip_params=None, product_params=None, cython=True, debug=False, max_workers=None, process_chunksize=1, jsonl_chunksize=1000, event_path_suffix='.jsonl', param_path_suffix='_params.json', validate=True, comment=None, name=None)[source]

A set of simulations. The parameter space is defined through constant base_params, zipped zip_params and cartesian product product_params. A set of a single simulation is also allowed. Through SimulationSet.run, configurable multiprocessing is implemented, allowing for parallelization of simulation runs at different parameters.

Parameters:
  • data_dir (str | Path) – Directory in which to store the parameters and events.

  • base_params (dict[str, dict[str, Any]] | None) – Dictionary setting parameters that are kept constant throughout the simulation set, optional.

  • zip_params (dict[str, dict[str, Sequence[Any]]] | None) – Dictionary setting parameters that are varied together throughout the simulation set, optional. The values for each inner dict should be lists that all match in lengths.

  • product_params (dict[str, dict[str, Sequence[Any]]] | None) – Dictionary setting parameters of which the cartesian product (i.e. all possible combinations of the supplied parameters) is created and varied throughout the simulation set, optional.

  • cython (bool) – Use cython.

  • debug (bool) – Print debug info.

  • max_workers (int | None) – Maximum number of multiprocessing workers. Defaults to number of processors on the machine if None or not given.

  • process_chunksize (int) – Number of simulations to submit to each multiprocessing worker at a time.

  • jsonl_chunksize (int) – Maximum number of events to keep in memory before saving to disk

  • param_path_suffix (str) – Parameters will be stored under “data_dir/<simulation_id><suffix>”

  • event_path_suffix (str) – Simulation events will be stored under “data_dir/<simulation_id><event_path_suffix>”

  • validate (bool) – Check validity of the supplied dictionary (unknown outer and inner keys, equal length for zip_params)

  • name (str | None) – Optional, filename-safe name.

  • comment (str | None) – Optional human-readable comment.

run(dry_run=False, info=False)[source]

Run the simulations configured through base_params, zip_params and product_params using multiprocessing. The parameters and resulting output events are written to disk in JSON/JSON Lines format. For more detail see Executing simulations.

Access simulations results
Parameters:
  • dry_run (bool) – If True, do not actually simulate.

  • info (bool) – Info/benchmark mode. If true, record the time it took to run each simulation run.

simulation_ids

Get simulation IDs.

param_paths

Get list of JSON parameter files.

event_paths

Get list of resulting output event JSON Lines file paths.

perform_single_simulation(params, *, data_dir, jsonl_chunksize=1000, debug=False, event_path_suffix='.jsonl', param_path_suffix='_params.json', info_path_suffix='_info.json', dry_run=False, info=False, info_contents=None)[source]

Execute a single simulation run based on a parameter dictionary and save parameters and result events to disk.

Parameters:
  • params (dict[str, dict[str, Any]]) –

    Parameter dictionary to base the simulation on. Must contain the following keys:

    • general
      • either n_reqs or t_cutoff

      • seat_capacity

      • (initial_location and n_vehicles) or initial_locations

      • space

      • transportation_request_cls

      • vehicle_state_cls

      • fleet_state_cls

    • request_generator
      • request_generator_cls

    • dispatcher
      • dispatcher_cls

  • data_dir (Path) – Existing directory in which to store parameters and events.

  • jsonl_chunksize (int) – Number of simulation events to keep in memory before writing them to disk at once.

  • debug (bool) – Print debug info to stdout.

  • param_path_suffix (str) – Parameters will be stored under “data_dir/<simulation_id><suffix>”

  • event_path_suffix (str) – Simulation events will be stored under “data_dir/<simulation_id><event_path_suffix>”

  • dry_run (bool) – If True, do not actually simulate. Just pretend to and return the corresponding ID.

  • info (bool) – Info/benchmark mode. If true, record the time it took to run the simulation run in a separate info file.

  • info_contents (dict[str, Any] | None) – Additional contents to write to the info file.

  • info_path_suffix (str)

Returns:

simulation ID

Return type:

str

Running analytics on the simulation results

The simulation results can be automatically analyzed using the ridepy.extras.analytics module, storing the results to disk.

SimulationSet.run_analytics(only_stops_and_requests=False, update_existing=False, check_for_changes=True, stops_path_suffix='_stops.pq', requests_path_suffix='_requests.pq', vehicle_quantities_path_suffix='_vehicle_quantities.pq', system_quantities_filename='system_quantities.pq')[source]

Compute analytics from simulation events and store them to disk in parquet format.

Parameters:
  • only_stops_and_requests (bool) – Only compute stops and requests, not vehicle and system quantities.

  • update_existing (bool | list[str]) – Recompute existing outputs. If a list is given, only recompute the list entries. Valid list items are ‘system_quantities’, ‘vehicle_quantities’, ‘stops’, and ‘requests’.

  • check_for_changes (bool) – If True, only update system quantities if simulation ids have changed. If False, do update system quantities in any case.

  • stops_path_suffix (str) – Appending this suffix to the simulation ID yields the path to the parquet stops file.

  • requests_path_suffix (str) – Appending this suffix to the simulation ID yields the path to the parquet requests file.

  • vehicle_quantities_path_suffix (str) – Appending this suffix to the simulation ID yields the path to the parquet vehicle quantities file.

  • system_quantities_filename (str) – Filename of the parquet file to store the system quantities in.

Return type:

None

JSON IO

This IO module implements functionality for reading and writing to JSON/JSON Lines format.

Simulation parameter configurations can be saved and restored using save_params_json() and read_params_json(). The IO module handles serialization and deserialization of the RequestGenerator, the dispatcher and the TransportSpace used for the simulation. This allows to recreate any simulation from its saved parameter dictionary. Note though that this does not serialize the actual objects. If the implementation of e.g. the dispatcher is modified, the simulation result will change.

A list of simulation output events can be saved and restored using save_events_json() and read_events_json(). The IO module handles serialization and deserialization of the various event types:

  • VehicleStateBeginEvent

  • VehicleStateEndEvent

  • PickupEvent

  • DeliveryEvent

  • RequestSubmissionEvent

  • RequestAcceptanceEvent

  • RequestRejectionEvent

Later this can e.g. be used as input for the analytics module:

stops, requests = get_stops_and_requests(
    events=read_events_json("events.jsonl"), space=read_params_json("params.json")
)
class ParamsJSONEncoder(*, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None)[source]

JSONEncoder to use when serializing a dictionary containing simulation parameters. This is able to serialize RequestGenerator, TransportSpace and dispatchers.

Example

json.dumps(params, cls=ParamsJSONEncoder)

Constructor for JSONEncoder, with sensible defaults.

If skipkeys is false, then it is a TypeError to attempt encoding of keys that are not str, int, float or None. If skipkeys is True, such items are simply skipped.

If ensure_ascii is true, the output is guaranteed to be str objects with all incoming non-ASCII characters escaped. If ensure_ascii is false, the output can contain non-ASCII characters.

If check_circular is true, then lists, dicts, and custom encoded objects will be checked for circular references during encoding to prevent an infinite recursion (which would cause an RecursionError). Otherwise, no such check takes place.

If allow_nan is true, then NaN, Infinity, and -Infinity will be encoded as such. This behavior is not JSON specification compliant, but is consistent with most JavaScript based encoders and decoders. Otherwise, it will be a ValueError to encode such floats.

If sort_keys is true, then the output of dictionaries will be sorted by key; this is useful for regression tests to ensure that JSON serializations can be compared on a day-to-day basis.

If indent is a non-negative integer, then JSON array elements and object members will be pretty-printed with that indent level. An indent level of 0 will only insert newlines. None is the most compact representation.

If specified, separators should be an (item_separator, key_separator) tuple. The default is (’, ‘, ‘: ‘) if indent is None and (‘,’, ‘: ‘) otherwise. To get the most compact JSON representation, you should specify (‘,’, ‘:’) to eliminate whitespace.

If specified, default is a function that gets called for objects that can’t otherwise be serialized. It should return a JSON encodable version of the object or raise a TypeError.

class ParamsJSONDecoder(*args, **kwargs)[source]

JSONDecoder to use when deserializing a dictionary containing simulation parameters. This is able to deserialize RequestGenerator, TransportSpace and dispatchers.

Example

json.loads(params, cls=ParamsJSONDecoder)
class EventsJSONEncoder(*, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None)[source]

JSONEncoder to use when serializing a list containing Event.

Example

json.dumps(events, cls=EventsJSONEncoder)

Constructor for JSONEncoder, with sensible defaults.

If skipkeys is false, then it is a TypeError to attempt encoding of keys that are not str, int, float or None. If skipkeys is True, such items are simply skipped.

If ensure_ascii is true, the output is guaranteed to be str objects with all incoming non-ASCII characters escaped. If ensure_ascii is false, the output can contain non-ASCII characters.

If check_circular is true, then lists, dicts, and custom encoded objects will be checked for circular references during encoding to prevent an infinite recursion (which would cause an RecursionError). Otherwise, no such check takes place.

If allow_nan is true, then NaN, Infinity, and -Infinity will be encoded as such. This behavior is not JSON specification compliant, but is consistent with most JavaScript based encoders and decoders. Otherwise, it will be a ValueError to encode such floats.

If sort_keys is true, then the output of dictionaries will be sorted by key; this is useful for regression tests to ensure that JSON serializations can be compared on a day-to-day basis.

If indent is a non-negative integer, then JSON array elements and object members will be pretty-printed with that indent level. An indent level of 0 will only insert newlines. None is the most compact representation.

If specified, separators should be an (item_separator, key_separator) tuple. The default is (’, ‘, ‘: ‘) if indent is None and (‘,’, ‘: ‘) otherwise. To get the most compact JSON representation, you should specify (‘,’, ‘:’) to eliminate whitespace.

If specified, default is a function that gets called for objects that can’t otherwise be serialized. It should return a JSON encodable version of the object or raise a TypeError.

sort_params(params)[source]

Returns a copy of the two-level nested dict params which is sorted in both levels.

Parameters:

params (dict) – Parameter dictionary, two levels of nesting

Returns:

params – Sorted params dict

Return type:

dict

create_params_json(*, params, sort=True)[source]

Convert a dictionary containing simulation parameters to pretty JSON. Parameter dictionaries may contain anything that is supported by ParamsJSONEncoder and ParamsJSONDecoder, e.g. RequestGenerator, TransportSpace`s and dispatchers. For additional detail, see :ref:`Executing Simulations.

Parameters:
  • params (dict) – dictionary containing the params to save

  • sort – if sort is True, sort the dict recursively to ensure consistent order.

Return type:

str

save_params_json(*, param_path, params)[source]

Save a dictionary containing simulation parameters to pretty JSON, overwriting existing. Parameter dictionaries may contain anything that is supported by ParamsJSONEncoder and ParamsJSONDecoder, e.g. RequestGenerator, TransportSpace`s and dispatchers. For additional detail, see :ref:`Executing Simulations.

Parameters:
  • param_path (Path) – JSON output file path

  • params (dict) – dictionary containing the params to save

Return type:

None

read_params_json(param_path)[source]

Read a dictionary containing simulation parameters from JSON. Parameter dictionaries may contain anything that is supported by ParamsJSONEncoder and ParamsJSONDecoder, e.g. RequestGenerator, TransportSpace`s and dispatchers. For additional detail, see :ref:`Executing Simulations.

Parameters:

param_path (Path)

Returns:

parameter dictionary

Return type:

dict

save_events_json(*, jsonl_path, events)[source]

Save events iterable to a file according to JSONL specs, appending to existing. For additional detail, see Executing simulations.

Parameters:
  • jsonl_path (Path) – JSON Lines output file path

  • events (Iterable) – iterable containing the events to save

Return type:

None

read_events_json(jsonl_path)[source]

Read events from JSON lines file, where each line of the file contains a single event. For additional detail, see Executing simulations.

Parameters:

jsonl_path (Path) – JSON Lines input file path

Returns:

List of dicts

Return type:

list[dict]

create_info_json(info)[source]

Convert a dictionary containing simulation info to pretty JSON.

Parameters:

info (dict) – dictionary containing the info to save

Return type:

str

Spaces

This module implements thin convenience wrappers around networkx to create common network topologies to be used as transport spaces.

make_nx_grid(dim=(3, 3), periodic=False, edge_distance=1)[source]

Return a lattice nx.Graph

Use in conjunction with spaces.Graph or spaces_cython.Graph like

Graph.from_nx(make_nx_grid())
Parameters:
  • dim (Tuple[int, int]) – dimensions of the graph: (n, k) for n x k vertices

  • periodic (bool) – periodic boundaries

  • edge_distance (float) – edge weight

Returns:

graph

Return type:

Graph

make_nx_cycle_graph(order=10, edge_distance=1)[source]

Return a cyclic nx.Graph

Use in conjunction with spaces.Graph or spaces_cython.Graph like

Graph.from_nx(make_nx_cycle_graph())
Parameters:
  • order (int) – number of vertices to generate

  • edge_distance (float) – edge weight

Returns:

graph

Return type:

Graph

make_nx_star_graph(order=10, edge_distance=1)[source]

Return a cyclic nx.Graph

Use in conjunction with spaces.Graph or spaces_cython.Graph like

Graph.from_nx(make_nx_star_graph())
Parameters:
  • order (int) – number of vertices to generate in total (this includes the center node, which will have index 0)

  • edge_distance (float) – edge weight

Returns:

graph

Return type:

Graph