diff --git a/docs/guide/networks/multi-sample-shortest-paths.ipynb b/docs/guide/networks/multi-sample-shortest-paths.ipynb index de42761d..ed970de4 100644 --- a/docs/guide/networks/multi-sample-shortest-paths.ipynb +++ b/docs/guide/networks/multi-sample-shortest-paths.ipynb @@ -1,5 +1,24 @@ { "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Multi-sample shortest paths\n", + "\n", + "Here we will see how the multi-sample capabilities of CORNETO allow to exactly solve more complex problems on networks. \n", + "\n", + "Imagine you have a directed graph **G** with a defined set of vertices and edges. Each edge in this graph has two different costs associated with it, one for each sample (let's call these Sample 1 and Sample 2). These costs represent two possible scenarios or configurations. For each sample, we want to find the shortest path using the costs defined for that sample. We could run a standard shortest path method per sample. However, given that there can be multiple shortest paths with the same total cost, following that strategy, we cannot identify interesting solutions, for example, if there is a path that is the shortest for both samples. Without a multi-sample method, we need to enumerate all shortest paths per sample, and then compare them to find the answer. This is not only computationally expensive but also not feasible for large networks.\n", + "\n", + "Using CORNETO, it is possible to create a multi shortest path problem to identify a pair of shortest paths—one for each sample—that minimize the number of \"non-selected\" edges across both samples. This objective can be interpreted in two ways:\n", + "\n", + "1. If there are multiple shortest paths with the same total cost for a given sample, prefer the one with the fewest edges (i.e., the most direct route).\n", + " \n", + "2. If the shortest paths differ between the two samples, prioritize the selection of paths that share the greatest number of common edges. This ensures that, despite different costs, the paths are as similar as possible.\n", + "\n", + "We will see how to solve this problem using CORNETO's multi-sample capabilities." + ] + }, { "cell_type": "code", "execution_count": 1, @@ -270,6 +289,7 @@ "metadata": {}, "outputs": [], "source": [ + "# Weights for the 14 edges for the two samples\n", "c1_weights = [3, 6, 7, 1, 7, 1, 1, 2, 6, 2, 5, 2, 1, 6, 1]\n", "c2_weights = [5, 15, 17, 6, 14, 1, 2, 3, 6, 1, 3, 2, 2, 5, 2]" ] @@ -361,6 +381,7 @@ "source": [ "from corneto.methods import solve_shortest_path\n", "\n", + "# We solve independently each shortest path problem\n", "edges, P, Gc = solve_shortest_path(G, \"A\", \"H\", edge_weights=c1_weights, solver=\"SCIPY\")\n", "Gc.edge_subgraph(edges).plot(orphan_edges=False)" ] @@ -785,27 +806,28 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "(15.0, 22.0)" + "(15.0, 22.0, 5.0)" ] }, - "execution_count": 24, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "P.objectives[0].value, P.objectives[1].value # , P.objectives[2].value" + "# We obtain the same optimal cost for each shortest path, but now we identified a shortest path that is common to both samples\n", + "P.objectives[0].value, P.objectives[1].value, P.objectives[2].value" ] }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 38, "metadata": {}, "outputs": [ { @@ -830,18 +852,19 @@ " [ 1., 1.]])" ] }, - "execution_count": 25, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ + "# Even though flow is continuous, we have discrete values which indicate the shortest paths.\n", "P.expr.flow.value" ] }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 39, "metadata": {}, "outputs": [ { @@ -850,7 +873,7 @@ "5" ] }, - "execution_count": 26, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } @@ -863,7 +886,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 40, "metadata": {}, "outputs": [ { @@ -947,10 +970,10 @@ "\n" ], "text/plain": [ - "" + "" ] }, - "execution_count": 27, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } @@ -961,7 +984,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 41, "metadata": {}, "outputs": [ { @@ -1045,10 +1068,10 @@ "\n" ], "text/plain": [ - "" + "" ] }, - "execution_count": 28, + "execution_count": 41, "metadata": {}, "output_type": "execute_result" } @@ -1056,13 +1079,6 @@ "source": [ "Gc.edge_subgraph(np.flatnonzero(P.expr.flow.value[:, 1] > 0)).plot()" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": {