{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n\n# Route Choice\n\nIn this example, we show how to perform route choice set generation using BFSLE and Link penalisation, for a city in La\nSerena Metropolitan Area in Chile.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        ".. admonition:: References\n\n  * :doc:`../../route_choice`\n\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        ".. seealso::\n    Several functions, methods, classes and modules are used in this example:\n\n    * :func:`aequilibrae.paths.Graph`\n    * :func:`aequilibrae.paths.RouteChoice`\n    * :func:`aequilibrae.matrix.AequilibraeMatrix`\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# Imports\nfrom uuid import uuid4\nfrom tempfile import gettempdir\nfrom os.path import join\n\nfrom aequilibrae.utils.create_example import create_example"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# We create the example project inside our temp folder\nfldr = join(gettempdir(), uuid4().hex)\n\nproject = create_example(fldr, \"coquimbo\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import logging\nimport sys"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# When the project opens, we can tell the logger to direct all messages to the terminal as well\nlogger = project.logger\nstdout_handler = logging.StreamHandler(sys.stdout)\nformatter = logging.Formatter(\"%(asctime)s;%(levelname)s ; %(message)s\")\nstdout_handler.setFormatter(formatter)\nlogger.addHandler(stdout_handler)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Model parameters\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import numpy as np"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We'll set the parameters for our route choice model. These are the parameters that will be used to calculate the\nutility of each path. In our example, the utility is equal to $distance * theta$,\nand the path overlap factor (PSL) is equal to $beta$.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# Distance factor\ntheta = 0.00011\n\n# PSL parameter\nbeta = 1.1"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Let's select a set of nodes of interest\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "nodes_of_interest = (71645, 74089, 77011, 79385)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Let's build all graphs\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "project.network.build_graphs()\n# We get warnings that several fields in the project are filled with NaNs.\n# This is true, but we won't use those fields."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We also see what graphs are available\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "project.network.graphs.keys()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We grab the graph for cars\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "graph = project.network.graphs[\"c\"]\n\n# Let's say that utility is just a function of distance, so we build our 'utility' field as distance * theta\ngraph.network = graph.network.assign(utility=graph.network.distance * theta)\n\n# Prepare the graph with all nodes of interest as centroids\ngraph.prepare_graph(np.array(nodes_of_interest))\n\n# And set the cost of the graph the as the utility field just created\ngraph.set_graph(\"utility\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Mock demand matrix\nWe'll create a mock demand matrix with demand 1 for every zone and prepare it for computation.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from aequilibrae.matrix import AequilibraeMatrix\n\nnames_list = [\"demand\", \"5x demand\"]\n\nmat = AequilibraeMatrix()\nmat.create_empty(zones=graph.num_zones, matrix_names=names_list, memory_only=True)\nmat.index = graph.centroids[:]\nmat.matrices[:, :, 0] = np.full((graph.num_zones, graph.num_zones), 10.0)\nmat.matrices[:, :, 1] = np.full((graph.num_zones, graph.num_zones), 50.0)\nmat.computational_view()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Create plot function\nBefore dive into the Route Choice class, let's define a function to plot assignment results.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import folium"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "def plot_results(link_loads):\n\n    link_loads = link_loads[link_loads[\"demand_tot\"] > 0]\n    max_load = link_loads[\"demand_tot\"].max()\n    links = project.network.links.data\n    loaded_links = links.merge(link_loads, on=\"link_id\", how=\"inner\")\n\n    loads_lyr = folium.FeatureGroup(\"link_loads\")\n\n    # Maximum thickness we would like is probably a 10, so let's make sure we don't go over that\n    factor = 10 / max_load\n\n    return loaded_links.explore(\n        color=\"red\",\n        style_kwds={\n            \"style_function\": lambda x: {\n                \"weight\": x[\"properties\"][\"demand_tot\"] * factor,\n            }\n        },\n    )"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Route Choice class\nHere we'll construct and use the Route Choice class to generate our route sets\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from aequilibrae.paths import RouteChoice"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "This object construct might take a minute depending on the size of the graph due to the construction of the compressed\nlink to network link mapping that's required. This is a one time operation per graph and is cached.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "rc = RouteChoice(graph)\n\n# Let's check the default parameters for the Route Choice class\nprint(rc.default_parameters)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Let's add the demand. If it's not provided, link loading cannot be preformed.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "rc.add_demand(mat)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "It is highly recommended to set either ``max_routes`` or ``max_depth`` to prevent runaway results.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "rc.set_choice_set_generation(\"bfsle\", max_routes=5)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We can now perform a computation for single OD pair if we'd like. Here we do one between the first and last centroid\nas well as an assignment.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "results = rc.execute_single(77011, 74089, demand=1.0)\nprint(results[0])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Because we asked it to also perform an assignment we can access the various results from that.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "res = rc.get_results()\nres.head()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "plot_results(rc.get_load_results())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Batch operations\nTo perform a batch operation we need to prepare the object first. We can either provide a list of tuple of the OD\npairs we'd like to use, or we can provided a 1D list and the generation will be run on all permutations.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "rc.prepare()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Now we can perform a batch computation with an assignment\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "rc.execute(perform_assignment=True)\nres = rc.get_results()\nres.head()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Since we provided a matrix initially we can also perform link loading based on our assignment results.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "rc.get_load_results()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We can plot these as well\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "plot_results(rc.get_load_results())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Select link analysis\nWe can also enable select link analysis by providing the links and the directions that we are interested in. Here we\nset the select link to trigger when (7369, 1) and (20983, 1) is utilised in \"sl1\" and \"sl2\" when (7369, 1) is\nutilised.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "rc.set_select_links({\"sl1\": [[(7369, 1), (20983, 1)]], \"sl2\": [[(7369, 1)]]})\nrc.execute(perform_assignment=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We can get then the results in a Pandas DataFrame for both the network.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "sl = rc.get_select_link_loading_results()\nsl"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We can also access the OD matrices for this link loading. These matrices are sparse and can be converted to\nSciPy sparse matrices for ease of use. They're stored in a dictionary where the key is the matrix name concatenated\nwith the select link set name via an underscore.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "rc.get_select_link_od_matrix_results()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "od_matrix = rc.get_select_link_od_matrix_results()[\"sl1\"][\"demand\"]\nod_matrix.to_scipy().toarray()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "project.close()"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.10.18"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}