Skip to content

Instantly share code, notes, and snippets.

@tonicanada
Created October 1, 2022 21:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tonicanada/878b1fd2f19cde0253150eedd865a92c to your computer and use it in GitHub Desktop.
Save tonicanada/878b1fd2f19cde0253150eedd865a92c to your computer and use it in GitHub Desktop.
linearprogramming_maxnetworkflow_medium.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": [],
"collapsed_sections": [],
"authorship_tag": "ABX9TyNe/5Y0gIzvf8wa6I+iPOZA",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/tonicanada/878b1fd2f19cde0253150eedd865a92c/linearprogramming_maxnetworkflow_medium.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"source": [
"### Solving Maximum Network Flow with PuLP library"
],
"metadata": {
"id": "hrOhDwIGsifU"
}
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "EEPTAQiNrLL3",
"outputId": "5316a108-09c6-430e-9d54-a1962ba28999"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/\n",
"Collecting pulp\n",
" Downloading PuLP-2.6.0-py3-none-any.whl (14.2 MB)\n",
"\u001b[K |████████████████████████████████| 14.2 MB 2.0 MB/s \n",
"\u001b[?25hInstalling collected packages: pulp\n",
"Successfully installed pulp-2.6.0\n"
]
}
],
"source": [
"!pip install pulp"
]
},
{
"cell_type": "code",
"source": [
"from pulp import *\n",
"import pprint\n",
"import numpy as np"
],
"metadata": {
"id": "PJW1W9GVrRTj"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"![image.png](data:image/png;base64,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)"
],
"metadata": {
"id": "KcrijAGts49f"
}
},
{
"cell_type": "code",
"source": [
"# capacities_graph network (first row should correspond to S, last to T)\n",
"capacities_graph = [\n",
" [0, 18, 0, 15, 0, 0],\n",
" [0, 0, 9, 2, 10, 0],\n",
" [0, 0, 0, 0, 0, 13],\n",
" [0, 0, 0, 0, 3, 0],\n",
" [0, 0, 5, 0, 0, 12],\n",
" [0, 0, 0, 0, 0, 0],\n",
"]"
],
"metadata": {
"id": "uC-F2K9UtBDJ"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"# Function to label nodes \n",
"def create_node_labels(graph):\n",
" n = len(graph)\n",
" labels = []\n",
" for i in range(n):\n",
" if i == 0:\n",
" labels.append(\"S\")\n",
" elif i == n-1:\n",
" labels.append(\"T\")\n",
" else:\n",
" labels.append(chr(64+i))\n",
" return labels"
],
"metadata": {
"id": "P2ZHOHZ2tHaV"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"vertex = create_node_labels(capacities_graph)"
],
"metadata": {
"id": "Qjq-j3A0vsr4"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"# Function to create the variables\n",
"def create_variables(graph):\n",
" edges = []\n",
" node_labels = create_node_labels(graph)\n",
" n = len(graph)\n",
" for i in range(n):\n",
" for j in range(n):\n",
" if capacities_graph[i][j] > 0:\n",
" edges.append(f\"{node_labels[i]}_{node_labels[j]}\")\n",
" return edges"
],
"metadata": {
"id": "kRO8K45JthcP"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"edges = create_variables(capacities_graph)"
],
"metadata": {
"id": "LULqiT7CuI6_"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"# Define PuLP problem\n",
"prob = LpProblem(\"Network Max Flow\",LpMaximize)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "gISPAiNGvETH",
"outputId": "09659401-027a-4955-ef97-bed8b85b9bb7"
},
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stderr",
"text": [
"/usr/local/lib/python3.7/dist-packages/pulp/pulp.py:1352: UserWarning: Spaces are not permitted in the name. Converted to '_'\n",
" warnings.warn(\"Spaces are not permitted in the name. Converted to '_'\")\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"# Creating a Set of Variables\n",
"x = LpVariable.dicts(\"Edge\", edges, 0,cat = \"Integer\")\n",
"pprint.pprint(x)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "3PfUmPr3vSHS",
"outputId": "a2b04018-d212-40eb-ea12-b920bcaa5aeb"
},
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"{'A_B': Edge_A_B,\n",
" 'A_C': Edge_A_C,\n",
" 'A_D': Edge_A_D,\n",
" 'B_T': Edge_B_T,\n",
" 'C_D': Edge_C_D,\n",
" 'D_B': Edge_D_B,\n",
" 'D_T': Edge_D_T,\n",
" 'S_A': Edge_S_A,\n",
" 'S_C': Edge_S_C}\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"def flow_sent_from_source(edgs):\n",
" edges_from_source = []\n",
" for edge in edgs:\n",
" if edge[0] == \"S\":\n",
" edges_from_source.append(edge)\n",
" return edges_from_source"
],
"metadata": {
"id": "ADE69fvixOJn"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"edges_from_source = flow_sent_from_source(edges)"
],
"metadata": {
"id": "HM55v1qmx9Z-"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"# Defining cost function\n",
"prob += sum(x[i] for i in edges_from_source)"
],
"metadata": {
"id": "1R06KjDKyGhw"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"# Fuction that receives an edge and returns capacity\n",
"def get_capacity(edge, cap_graph):\n",
" vertex_from = edge[0]\n",
" vertex_to = edge[-1]\n",
" vertex_from_idx = vertex.index(vertex_from)\n",
" vertex_to_idx = vertex.index(vertex_to)\n",
" return cap_graph[vertex_from_idx][vertex_to_idx] "
],
"metadata": {
"id": "Pn3RVFq8vZCF"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"get_capacity(\"S_A\", capacities_graph)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "rFiPUTNJvjgQ",
"outputId": "568880e6-c460-4334-de70-c1bf8342b131"
},
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"18"
]
},
"metadata": {},
"execution_count": 31
}
]
},
{
"cell_type": "code",
"source": [
"# Adding constraints conservation of flow\n",
"for v in vertex:\n",
" flow_in = []\n",
" flow_out = []\n",
" for edge in x.keys():\n",
" if edge[-1] == v:\n",
" flow_in.append(edge)\n",
" if edge[0] == v:\n",
" flow_out.append(edge)\n",
" if (len(flow_in)>0 and len(flow_out) >0):\n",
" print(flow_in, flow_out)\n",
" prob += sum(x[i] for i in flow_in) == sum(x[i] for i in flow_out)"
],
"metadata": {
"id": "E3xjgPHev8KC",
"colab": {
"base_uri": "https://localhost:8080/"
},
"outputId": "ee59845b-368f-4503-c307-ca0c247ae1d0"
},
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"['S_A'] ['A_B', 'A_C', 'A_D']\n",
"['A_B', 'D_B'] ['B_T']\n",
"['S_C', 'A_C'] ['C_D']\n",
"['A_D', 'C_D'] ['D_B', 'D_T']\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"# Adding constraints capacitites of flow\n",
"for e in edges:\n",
" capacity_edge = get_capacity(e, capacities_graph)\n",
" prob += x[e] <= capacity_edge\n",
"\n",
"print(prob)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "6gTaWRpzwWd3",
"outputId": "0ea53707-664c-4382-87b7-de628a6b70dd"
},
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Network_Max_Flow:\n",
"MAXIMIZE\n",
"None\n",
"SUBJECT TO\n",
"_C1: - Edge_A_B - Edge_A_C - Edge_A_D + Edge_S_A = 0\n",
"\n",
"_C2: Edge_A_B - Edge_B_T + Edge_D_B = 0\n",
"\n",
"_C3: Edge_A_C - Edge_C_D + Edge_S_C = 0\n",
"\n",
"_C4: Edge_A_D + Edge_C_D - Edge_D_B - Edge_D_T = 0\n",
"\n",
"_C5: - Edge_A_B - Edge_A_C - Edge_A_D + Edge_S_A = 0\n",
"\n",
"_C6: Edge_A_B - Edge_B_T + Edge_D_B = 0\n",
"\n",
"_C7: Edge_A_C - Edge_C_D + Edge_S_C = 0\n",
"\n",
"_C8: Edge_A_D + Edge_C_D - Edge_D_B - Edge_D_T = 0\n",
"\n",
"_C9: Edge_S_A <= 18\n",
"\n",
"_C10: Edge_S_C <= 15\n",
"\n",
"_C11: Edge_A_B <= 9\n",
"\n",
"_C12: Edge_A_C <= 2\n",
"\n",
"_C13: Edge_A_D <= 10\n",
"\n",
"_C14: Edge_B_T <= 13\n",
"\n",
"_C15: Edge_C_D <= 3\n",
"\n",
"_C16: Edge_D_B <= 5\n",
"\n",
"_C17: Edge_D_T <= 12\n",
"\n",
"VARIABLES\n",
"0 <= Edge_A_B Integer\n",
"0 <= Edge_A_C Integer\n",
"0 <= Edge_A_D Integer\n",
"0 <= Edge_B_T Integer\n",
"0 <= Edge_C_D Integer\n",
"0 <= Edge_D_B Integer\n",
"0 <= Edge_D_T Integer\n",
"0 <= Edge_S_A Integer\n",
"0 <= Edge_S_C Integer\n",
"\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"prob.solve()"
],
"metadata": {
"id": "KScVx0VMxDG8",
"colab": {
"base_uri": "https://localhost:8080/"
},
"outputId": "2eda601e-451f-4c48-c822-cca86591a930"
},
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"1"
]
},
"metadata": {},
"execution_count": 44
}
]
},
{
"cell_type": "code",
"source": [
"print(\"Status:\", LpStatus[prob.status])"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "zdc9npzrydv8",
"outputId": "96e80438-cb87-4f79-dcd4-904c8a3c0292"
},
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Status: Optimal\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"# Solution\n",
"for e in edges:\n",
" print(x[e], x[e].value())"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "_eSlxO5Wyfhn",
"outputId": "cfaae5ed-5fb9-4813-d217-26ba0d60cb28"
},
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Edge_S_A 18.0\n",
"Edge_S_C 3.0\n",
"Edge_A_B 9.0\n",
"Edge_A_C 0.0\n",
"Edge_A_D 9.0\n",
"Edge_B_T 13.0\n",
"Edge_C_D 3.0\n",
"Edge_D_B 4.0\n",
"Edge_D_T 8.0\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"# Maximum amount of flow\n",
"value(prob.objective)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "1ARRMVAZyhKX",
"outputId": "c8e06f10-110e-4672-f1a5-7d0fff6d2f48"
},
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"21.0"
]
},
"metadata": {},
"execution_count": 48
}
]
},
{
"cell_type": "code",
"source": [],
"metadata": {
"id": "IXv5vVBpyujN"
},
"execution_count": null,
"outputs": []
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment