diff --git a/model.ipynb b/model.ipynb index 147b9c1..516cec9 100644 --- a/model.ipynb +++ b/model.ipynb @@ -4,31 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Lost Luggage Distribution Problem\n", - "\n", - "## Objective and Prerequisites\n", - "\n", - "In this example, you’ll learn how to use mathematical optimization to solve a vehicle routing problem with time windows, which involves helping a company figure out the minimum number of trucks required to deliver pieces of lost or delayed baggage to their rightful owners and determining the optimal assignment of trucks to customers.\n", - "\n", - "This model is example 27 from the fifth edition of Model Building in Mathematical Programming by H. Paul Williams on pages 287-289 and 343-344.\n", - "\n", - "This modeling example is at the advanced level, where we assume that you know Python and the Gurobi Python API and that you have advanced knowledge of building mathematical optimization models. Typically, the objective function and/or constraints of these examples are complex or require advanced features of the Gurobi Python API.\n", - "\n", - "**Download the Repository**
\n", - "You can download the repository containing this and other examples by clicking [here](https://github.com/Gurobi/modeling-examples/archive/master.zip). \n", - "\n", - "**Gurobi License**
\n", - "In order to run this Jupyter Notebook properly, you must have a Gurobi license. If you do not have one, you can request an [evaluation license](https://www.gurobi.com/downloads/request-an-evaluation-license/?utm_source=3PW&utm_medium=OT&utm_campaign=WW-MU-MUI-OR-O_LEA-PR_NO-Q3_FY20_WW_JPME_Lost_Luggage_Distribution_COM_EVAL_GitHub&utm_term=Lost%20Luggage%20Distribution&utm_content=C_JPM) as a *commercial user*, or download a [free license](https://www.gurobi.com/academia/academic-program-and-licenses/?utm_source=3PW&utm_medium=OT&utm_campaign=WW-MU-EDU-OR-O_LEA-PR_NO-Q3_FY20_WW_JPME_Lost_Luggage_Distribution_COM_EVAL_GitHub&utm_term=Lost%20Luggage%20Distribution&utm_content=C_JPM) as an *academic user*." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Problem Description\n", - "\n", - "A small company with six trucks has a contract with a number of airlines to pick up lost or delayed baggage, belonging to customers in the London area, from Heathrow airport at 6 p.m. each evening. The contract stipulates that each customer must have their baggage delivered by 8 p.m. The company requires a model to advise them what is the minimum number of trucks they need to use and to which customers each van should deliver and in what order. There is no practical capacity limitation on each van. Each van can hold all baggage that needs to be delivered in a two-hour period. To solve this problem, we can formulate an optimization model that minimizes the number of trucks that need to be used.\n", - "\n" + "# Model" ] }, { @@ -81,11 +57,9 @@ "\n", "**Depot**: Depot is visited by every truck used. \n", "\n", - "\n", - "\n", - "$$\\sum{y_{0k}}=K$$\n", + "\\begin{equation}\n", + "\\sum_{k \\in V} y_{0,k} \\geq \\sum_{k \\in V} z_k\n", + "\\end{equation}\n", "\n", "**Arriving at a location**: If location $j$ is visited by truck $k$, then the truck is coming from another location $i$.\n", "\n", @@ -113,8 +87,9 @@ "**Timeline**: keep track of time to avoid subtours via big $M$ and either-or\n", "$$s_i+d_{ij}-s_j \\leq M(1-x_{ijk})$$\n", "\n", - "**max time contraint**:\n", - "$$C_N = T_{max}$$" + "**max time contraint**: this is simplified at the moment, we are assuming each vehicle only takes 1 tour.\n", + "\n", + "$$C_0 = T_{max}$$" ] }, { @@ -128,7 +103,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 138, "metadata": {}, "outputs": [], "source": [ @@ -138,6 +113,7 @@ "from itertools import permutations\n", "import gurobipy as gp\n", "from gurobipy import GRB\n", + "import matplotlib.pyplot as plt\n", "\n", "# tested with Python 3.7.0 & Gurobi 9.1.0" ] @@ -152,18 +128,20 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 139, "metadata": {}, "outputs": [], "source": [ "# number of locations, including the depot. The index of the depot is 0\n", - "n = 17\n", + "n = 5\n", "locations = [*range(n)]\n", "\n", "# number of trucks\n", - "K = 6\n", + "K = 3\n", "trucks = [*range(K)]\n", "\n", + "\n", + "\n", "# Create n random points\n", "# Depot is located at (0,0) coordinates\n", "random.seed(1)\n", @@ -180,6 +158,38 @@ "M = 1000 #TODO: to be changes" ] }, + { + "cell_type": "code", + "execution_count": 140, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 140, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.scatter([x[0] for x in points], [x[1] for x in points])" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -191,18 +201,9 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 141, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Set parameter Username\n", - "Academic license - for non-commercial use only - expires 2022-06-17\n" - ] - } - ], + "outputs": [], "source": [ "m = gp.Model('lost_luggage_distribution.lp')\n", "\n", @@ -217,8 +218,8 @@ "# Number of trucks used is a decision variable\n", "z = m.addVars(trucks, vtype=GRB.BINARY, name='used')\n", "\n", - "# Travel time per van\n", - "t = m.addVars(trucks, ub=120, name='travelTime')\n", + "# Travel time per truck\n", + "t = m.addVars(trucks, name='travelTime') #TODO: add ub here for max TT\n", "\n", "# Maximum travel time\n", "T = m.addVar(name='maxTravelTime')\n", @@ -244,33 +245,26 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 142, "metadata": {}, "outputs": [], "source": [ "# Truck utilization constraint\n", - "\n", "visitCustomer = m.addConstrs((y[i,k] <= z[k] for k in trucks for i in locations if i > 0), name='visitCustomer' )" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "No van travels for more than 120 min. We make a small change from the original H.P. Williams version to introduce a slack variable for the travel time for each van, t[k]." - ] - }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 143, "metadata": {}, "outputs": [], "source": [ "# Travel time constraint\n", "# Exclude the time to return to the depot\n", "\n", - "travelTime = m.addConstrs((gp.quicksum(time[i,j]*x[i,j,k] for i,j in time.keys() if j > 0) == t[k] for k in trucks), \n", - " name='travelTimeConstr' )" + "travelTime = m.addConstrs((gp.quicksum(time[i,j]*x[i,j,k] for i,j in time.keys() if j > 0) == t[k] for k in trucks), name='travelTimeConstr')\n", + "\n", + "maxTravelTime = m.addConstrs((t[k] <= T for k in trucks), name='maxTravelTimeConstr')" ] }, { @@ -282,7 +276,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 144, "metadata": {}, "outputs": [], "source": [ @@ -294,12 +288,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Heathrow (depot) is visited by every van used." + "Depot is visited by every van used." ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 145, "metadata": {}, "outputs": [], "source": [ @@ -316,7 +310,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 146, "metadata": {}, "outputs": [], "source": [ @@ -333,7 +327,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 147, "metadata": {}, "outputs": [], "source": [ @@ -350,7 +344,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 148, "metadata": {}, "outputs": [], "source": [ @@ -361,19 +355,57 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Relate the maximum travel time to the travel times of each van" + "NEW: Adding time windows" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 149, "metadata": {}, "outputs": [], "source": [ - "maxTravelTime = m.addConstrs((t[k] <= T for k in trucks), name='maxTravelTimeConstr')\n", + "# openingConstr = m.addConstrs((o[i]<=serviceTime[i] for i in locations)\n", + "# , name='openingConstr')\n", "\n", - "# Alternately, as a general constraint:\n", - "# maxTravelTime = m.addConstr(s == gp.max_(t), name='maxTravelTimeConstr')" + "# closingConstr = m.addConstrs((c[i]>=serviceTime[i] for i in locations)\n", + "# , name='closingConstr') " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "NEW: timeline" + ] + }, + { + "cell_type": "code", + "execution_count": 150, + "metadata": {}, + "outputs": [], + "source": [ + "#d[i,j] is our final goal\n", + "# timelineConstr = m.addConstrs((serviceTime[i] + time[i,j] - serviceTime[j] <= M*(1-x[i,j,k]) for i,j,k in x.keys())\n", + "# , name='timelineConstr'\n", + "# )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "NEW: max time" + ] + }, + { + "cell_type": "code", + "execution_count": 151, + "metadata": {}, + "outputs": [], + "source": [ + "# maxTimeConstr = m.addConstr(c[0] == T\n", + "# , name='maxTimeConstr'\n", + "# )" ] }, { @@ -388,7 +420,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 152, "metadata": {}, "outputs": [], "source": [ @@ -407,7 +439,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 153, "metadata": {}, "outputs": [], "source": [ @@ -455,7 +487,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 154, "metadata": {}, "outputs": [ { @@ -465,13 +497,13 @@ "Set parameter LazyConstraints to value 1\n", "Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)\n", "Thread count: 4 physical cores, 8 logical processors, using up to 8 threads\n", - "Optimize a model with 334 rows, 1798 columns and 5492 nonzeros\n", - "Model fingerprint: 0xaa4ca881\n", - "Variable types: 58 continuous, 1740 integer (1740 binary)\n", + "Optimize a model with 55 rows, 97 columns and 269 nonzeros\n", + "Model fingerprint: 0xfd3b97cd\n", + "Variable types: 19 continuous, 78 integer (78 binary)\n", "Coefficient statistics:\n", - " Matrix range [1e+00, 7e+01]\n", + " Matrix range [1e+00, 6e+01]\n", " Objective range [1e+00, 1e+00]\n", - " Bounds range [1e+00, 1e+02]\n", + " Bounds range [1e+00, 6e+01]\n", " RHS range [1e+00, 1e+00]\n", "\n", "---------------------------------------------------------------------------\n", @@ -481,120 +513,80 @@ "Multi-objectives: applying initial presolve ...\n", "---------------------------------------------------------------------------\n", "\n", + "Presolve removed 41 rows and 63 columns\n", "Presolve time: 0.01s\n", - "Presolved: 334 rows and 1798 columns\n", + "Presolved: 14 rows and 34 columns\n", "---------------------------------------------------------------------------\n", "\n", "Multi-objectives: optimize objective 1 (Number of trucks) ...\n", "---------------------------------------------------------------------------\n", "\n", - "Presolve time: 0.01s\n", - "Presolved: 334 rows, 1798 columns, 5492 nonzeros\n", - "Variable types: 58 continuous, 1740 integer (1740 binary)\n", + "Found heuristic solution: objective 3.0000000\n", + "Presolve time: 0.00s\n", + "Presolved: 14 rows, 34 columns, 57 nonzeros\n", + "Variable types: 19 continuous, 15 integer (15 binary)\n", "\n", - "Root relaxation: objective 1.000000e+00, 706 iterations, 0.02 seconds (0.01 work units)\n", + "Root relaxation: cutoff, 18 iterations, 0.00 seconds (0.00 work units)\n", "\n", " Nodes | Current Node | Objective Bounds | Work\n", " Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n", "\n", - " 0 0 1.27085 0 113 - 1.27085 - - 0s\n", - " 0 0 2.00000 0 151 - 2.00000 - - 0s\n", - " 0 0 2.00000 0 120 - 2.00000 - - 0s\n", - " 0 0 2.00000 0 140 - 2.00000 - - 0s\n", - " 0 0 2.00000 0 14 - 2.00000 - - 0s\n", - " 0 0 2.00000 0 4 - 2.00000 - - 0s\n", - " 0 0 2.00000 0 23 - 2.00000 - - 0s\n", - " 0 0 2.00000 0 18 - 2.00000 - - 0s\n", - " 0 0 2.00000 0 17 - 2.00000 - - 0s\n", - " 0 0 2.00000 0 17 - 2.00000 - - 0s\n", - " 0 2 2.00000 0 14 - 2.00000 - - 1s\n", - "* 699 455 71 4.0000000 2.00000 50.0% 30.6 1s\n", - "H 1049 310 3.0000000 2.00000 33.3% 32.9 1s\n", - " 1177 296 2.00000 14 47 3.00000 2.00000 33.3% 58.2 5s\n", - "H 1922 152 2.0000000 2.00000 0.00% 100 8s\n", + " 0 0 cutoff 0 3.00000 3.00000 0.00% - 0s\n", "\n", - "Cutting planes:\n", - " Gomory: 1\n", - " Implied bound: 7\n", - " Clique: 7\n", - " MIR: 1\n", - " Zero half: 1\n", - " RLT: 1\n", - " Lazy constraints: 120\n", - "\n", - "Explored 1943 nodes (213182 simplex iterations) in 8.25 seconds (5.98 work units)\n", + "Explored 1 nodes (18 simplex iterations) in 0.04 seconds (0.00 work units)\n", "Thread count was 8 (of 8 available processors)\n", "\n", - "Solution count 3: 2 3 4 \n", + "Solution count 1: 3 \n", "\n", "Optimal solution found (tolerance 1.00e-04)\n", - "Best objective 2.000000000000e+00, best bound 2.000000000000e+00, gap 0.0000%\n", + "Best objective 3.000000000000e+00, best bound 3.000000000000e+00, gap 0.0000%\n", "---------------------------------------------------------------------------\n", "\n", "Multi-objectives: optimize objective 2 (Travel time) ...\n", "---------------------------------------------------------------------------\n", "\n", "\n", - "Loaded user MIP start with objective 117\n", - "\n", - "Presolve time: 0.02s\n", - "Presolved: 335 rows, 1798 columns, 5498 nonzeros\n", - "Variable types: 58 continuous, 1740 integer (1740 binary)\n", + "Loaded user MIP start with objective 2e+09\n", "\n", - "Root simplex log...\n", + "Presolve removed 1 rows and 0 columns\n", + "Presolve time: 0.00s\n", + "Presolved: 14 rows, 34 columns, 57 nonzeros\n", + "Variable types: 19 continuous, 15 integer (15 binary)\n", "\n", - "Iteration Objective Primal Inf. Dual Inf. Time\n", - " 0 0.0000000e+00 1.600000e+01 0.000000e+00 8s\n", - " 653 2.9010676e+01 0.000000e+00 0.000000e+00 8s\n", - "\n", - "Root relaxation: objective 2.901068e+01, 653 iterations, 0.04 seconds (0.02 work units)\n", + "Root relaxation: objective 4.891130e+01, 16 iterations, 0.00 seconds (0.00 work units)\n", "\n", " Nodes | Current Node | Objective Bounds | Work\n", " Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n", "\n", - " 0 0 89.68911 0 69 117.00000 89.68911 23.3% - 8s\n", - " 0 0 93.64408 0 67 117.00000 93.64408 20.0% - 8s\n", - " 0 0 94.43074 0 17 117.00000 94.43074 19.3% - 8s\n", - " 0 0 95.46321 0 59 117.00000 95.46321 18.4% - 8s\n", - " 0 0 95.50664 0 60 117.00000 95.50664 18.4% - 8s\n", - "H 0 0 116.4889483 95.50664 18.0% - 8s\n", - " 0 0 95.66792 0 62 116.48895 95.66792 17.9% - 8s\n", - " 0 0 95.68927 0 64 116.48895 95.68927 17.9% - 8s\n", - " 0 0 95.70252 0 67 116.48895 95.70252 17.8% - 8s\n", - " 0 0 95.72314 0 58 116.48895 95.72314 17.8% - 8s\n", - " 0 0 95.73173 0 59 116.48895 95.73173 17.8% - 8s\n", - " 0 0 95.75299 0 58 116.48895 95.75299 17.8% - 8s\n", - " 0 0 95.80745 0 62 116.48895 95.80745 17.8% - 8s\n", - " 0 0 95.81155 0 61 116.48895 95.81155 17.8% - 8s\n", - " 0 0 95.82275 0 65 116.48895 95.82275 17.7% - 8s\n", - " 0 0 95.82353 0 65 116.48895 95.82353 17.7% - 8s\n", - " 0 0 95.82353 0 65 116.48895 95.82353 17.7% - 8s\n", - " 0 2 96.72441 0 65 116.48895 96.72441 17.0% - 8s\n", - "* 217 126 21 114.8599746 98.13859 14.6% 10.1 8s\n", - "* 255 136 23 113.7234014 100.13030 12.0% 10.1 8s\n", - "* 610 227 10 112.4116095 103.12886 8.26% 10.0 8s\n", - "* 837 192 15 105.4165478 104.05065 1.30% 10.6 9s\n", + " 0 0 48.91130 0 7 2.0000e+09 48.91130 100% - 0s\n", + "H 0 0 57.1401785 48.91130 14.4% - 0s\n", + " 0 0 50.85132 0 7 57.14018 50.85132 11.0% - 0s\n", + " 0 0 50.99656 0 7 57.14018 50.99656 10.8% - 0s\n", + " 0 0 53.87547 0 7 57.14018 53.87547 5.71% - 0s\n", + " 0 0 54.35828 0 8 57.14018 54.35828 4.87% - 0s\n", + " 0 0 54.35828 0 7 57.14018 54.35828 4.87% - 0s\n", + " 0 0 55.17040 0 8 57.14018 55.17040 3.45% - 0s\n", + " 0 0 55.17040 0 6 57.14018 55.17040 3.45% - 0s\n", + " 0 0 55.17040 0 6 57.14018 55.17040 3.45% - 0s\n", + " 0 1 55.17040 0 6 57.14018 55.17040 3.45% - 0s\n", "\n", "Cutting planes:\n", - " Gomory: 2\n", - " Clique: 1\n", - " Zero half: 9\n", - " Mod-K: 2\n", - " Lazy constraints: 62\n", + " MIR: 1\n", + " Relax-and-lift: 2\n", "\n", - "Explored 1155 nodes (12200 simplex iterations) in 9.03 seconds (6.32 work units)\n", + "Explored 2 nodes (37 simplex iterations) in 0.08 seconds (0.00 work units)\n", "Thread count was 8 (of 8 available processors)\n", "\n", - "Solution count 5: 105.417 112.412 113.723 ... 116.489\n", + "Solution count 2: 57.1402 2e+09 \n", "\n", "Optimal solution found (tolerance 1.00e-04)\n", - "Best objective 1.054165478154e+02, best bound 1.054165478154e+02, gap 0.0000%\n", + "Best objective 5.714017850865e+01, best bound 5.714017850865e+01, gap 0.0000%\n", "\n", "---------------------------------------------------------------------------\n", - "Multi-objectives: solved in 9.04 seconds (6.32 work units), solution count 8\n", + "Multi-objectives: solved in 0.09 seconds (0.00 work units), solution count 2\n", "\n", "\n", - "User-callback calls 13351, time in user-callback 0.29 sec\n" + "User-callback calls 481, time in user-callback 0.02 sec\n" ] } ], @@ -620,7 +612,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 155, "metadata": { "scrolled": true }, @@ -629,19 +621,10 @@ "name": "stdout", "output_type": "stream", "text": [ - "Route for van 0: 0 -> 3 -> 16 -> 15 -> 2 -> 7 -> 14 -> 6 -> 5 -> 13 -> 0. Travel time: 105.42 min\n", - "Route for van 1: 0 -> 9 -> 8 -> 1 -> 12 -> 10 -> 4 -> 11 -> 0. Travel time: 104.78 min\n" - ] - }, - { - "ename": "NameError", - "evalue": "name 's' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", - "\u001b[1;32m~\\AppData\\Local\\Temp/ipykernel_13220/3456469386.py\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 12\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34mf\". Travel time: {round(t[k].X,2)} min\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 13\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 14\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34mf\"Max travel time: {round(s.X,2)}\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[1;31mNameError\u001b[0m: name 's' is not defined" + "Route for van 0: 0 -> 3 -> 2 -> 0. Travel time: 49.6 min\n", + "Route for van 1: 0 -> 1 -> 0. Travel time: 36.88 min\n", + "Route for van 2: 0 -> 4 -> 0. Travel time: 57.14 min\n", + "Max travel time: 57.14\n" ] } ], @@ -659,7 +642,7 @@ " break\n", " print(f\". Travel time: {round(t[k].X,2)} min\")\n", "\n", - "print(f\"Max travel time: {round(s.X,2)}\")" + "print(f\"Max travel time: {round(T.X,2)}\")" ] }, {