Skip to content

Commit a77bcc6

Browse files
committed
Cleaned up symmetric TSP, which can now solve the full-sized model
1 parent 1e645c5 commit a77bcc6

File tree

2 files changed

+104
-251
lines changed

2 files changed

+104
-251
lines changed

traveling_salesman/tsp.ipynb

Lines changed: 52 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,9 @@
136136
"\n",
137137
"Consider a salesperson that needs to visit customers at each state capital of the continental US. The salesperson wants to identify the shortest route that goes to all the state capitals.\n",
138138
"\n",
139-
"This modeling example requires to import the following libraries.\n",
140-
"* **math** access to mathematical functions.\n",
141-
"* **itertools** implements a number of iterator building blocks.\n",
142-
"* **folium** creates maps.\n",
143-
"* **gurobipy** calls Gurobi algorithms to solve MIP models.\n"
139+
"This modeling example requires the following libraries that are not part of the standard Python distribution:\n",
140+
"* **folium**: to create maps.\n",
141+
"* **gurobipy**: provides Gurobi algorithms to solve MIP models.\n"
144142
]
145143
},
146144
{
@@ -153,7 +151,7 @@
153151
},
154152
{
155153
"cell_type": "code",
156-
"execution_count": 1,
154+
"execution_count": null,
157155
"metadata": {},
158156
"outputs": [],
159157
"source": [
@@ -174,18 +172,18 @@
174172
"cell_type": "markdown",
175173
"metadata": {},
176174
"source": [
177-
"### Preprocessing\n",
178-
"The following function calculates the distance of each pair of state capitals combination."
175+
"### Data computation\n",
176+
"The following function calculates the distance for each pair of state capitals. Since we are solving the _symmetric_ traveling salesman problem, we use _combinations_ of cities."
179177
]
180178
},
181179
{
182180
"cell_type": "code",
183-
"execution_count": 2,
181+
"execution_count": null,
184182
"metadata": {},
185183
"outputs": [],
186184
"source": [
187185
"import math\n",
188-
"from itertools import combinations,product\n",
186+
"from itertools import combinations\n",
189187
"\n",
190188
"# Compute pairwise distance matrix\n",
191189
"\n",
@@ -195,7 +193,39 @@
195193
" diff = (c1[0]-c2[0], c1[1]-c2[1])\n",
196194
" return math.sqrt(diff[0]*diff[0]+diff[1]*diff[1])\n",
197195
"\n",
198-
"dist = {(c1, c2): distance(c1, c2) for c1, c2 in product(capitals, capitals) if c1 != c2}"
196+
"dist = {(c1, c2): distance(c1, c2) for c1, c2 in combinations(capitals, 2)}"
197+
]
198+
},
199+
{
200+
"cell_type": "markdown",
201+
"metadata": {},
202+
"source": [
203+
"### Model Code\n",
204+
"We now write the model for the TSP, by defining decision variables, constraints, and objective function. Because this is the _symmetric_ traveling salesman problem, we can make it more efficient by setting the _object_ x[j,i] to x[i,j], instead of a constraint."
205+
]
206+
},
207+
{
208+
"cell_type": "code",
209+
"execution_count": null,
210+
"metadata": {},
211+
"outputs": [],
212+
"source": [
213+
"import gurobipy as gp\n",
214+
"from gurobipy import GRB\n",
215+
"\n",
216+
"# tested with Python 3.7 & Gurobi 9.0.0\n",
217+
"\n",
218+
"m = gp.Model()\n",
219+
"\n",
220+
"# Variables: is city 'i' adjacent to city 'j' on the tour?\n",
221+
"vars = m.addVars(dist.keys(), obj=dist, vtype=GRB.BINARY, name='x')\n",
222+
"\n",
223+
"# Symmetric direction: Copy the object\n",
224+
"for i, j in vars.keys():\n",
225+
" vars[j, i] = vars[i, j] # edge in opposite direction\n",
226+
"\n",
227+
"# Constraints: two edges incident to each city\n",
228+
"cons = m.addConstrs(vars.sum(c, '*') == 2 for c in capitals)"
199229
]
200230
},
201231
{
@@ -208,7 +238,7 @@
208238
},
209239
{
210240
"cell_type": "code",
211-
"execution_count": 3,
241+
"execution_count": null,
212242
"metadata": {},
213243
"outputs": [],
214244
"source": [
@@ -225,23 +255,8 @@
225255
" if len(tour) < len(capitals):\n",
226256
" # add subtour elimination constr. for every pair of cities in subtour\n",
227257
" model.cbLazy(gp.quicksum(model._vars[i, j] for i, j in combinations(tour, 2))\n",
228-
" <= len(tour)-1)"
229-
]
230-
},
231-
{
232-
"cell_type": "markdown",
233-
"metadata": {},
234-
"source": [
235-
"### Finding Shortest Route\n",
236-
"The following function determines the shortest route from a given set of edges."
237-
]
238-
},
239-
{
240-
"cell_type": "code",
241-
"execution_count": 4,
242-
"metadata": {},
243-
"outputs": [],
244-
"source": [
258+
" <= len(tour)-1)\n",
259+
"\n",
245260
"# Given a tuplelist of edges, find the shortest subtour\n",
246261
"\n",
247262
"def subtour(edges):\n",
@@ -265,91 +280,18 @@
265280
"cell_type": "markdown",
266281
"metadata": {},
267282
"source": [
268-
"### Model Deployment\n",
269-
"We now determine the model for the TSP, by defining decision variables, constraints, and objective function. Next, we start the optimization and Gurobi finds the optimal route for the TSP. "
283+
"## Solve the model"
270284
]
271285
},
272286
{
273287
"cell_type": "code",
274-
"execution_count": 5,
288+
"execution_count": null,
275289
"metadata": {},
276-
"outputs": [
277-
{
278-
"name": "stdout",
279-
"output_type": "stream",
280-
"text": [
281-
"Using license file c:\\gurobi\\gurobi.lic\n",
282-
"Changed value of parameter lazyConstraints to 1\n",
283-
" Prev: 0 Min: 0 Max: 1 Default: 0\n",
284-
"Gurobi Optimizer version 9.1.0 build v9.1.0rc0 (win64)\n",
285-
"Thread count: 4 physical cores, 8 logical processors, using up to 8 threads\n",
286-
"Optimize a model with 2304 rows, 2256 columns and 6768 nonzeros\n",
287-
"Model fingerprint: 0x029d7617\n",
288-
"Variable types: 0 continuous, 2256 integer (2256 binary)\n",
289-
"Coefficient statistics:\n",
290-
" Matrix range [1e+00, 1e+00]\n",
291-
" Objective range [6e-01, 5e+01]\n",
292-
" Bounds range [1e+00, 1e+00]\n",
293-
" RHS range [2e+00, 2e+00]\n",
294-
"Presolve removed 2256 rows and 1128 columns\n",
295-
"Presolve time: 0.01s\n",
296-
"Presolved: 48 rows, 1128 columns, 2256 nonzeros\n",
297-
"Variable types: 0 continuous, 1128 integer (1128 binary)\n",
298-
"\n",
299-
"Root relaxation: objective 3.223715e+02, 72 iterations, 0.00 seconds\n",
300-
"\n",
301-
" Nodes | Current Node | Objective Bounds | Work\n",
302-
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
303-
"\n",
304-
" 0 0 322.37153 0 12 - 322.37153 - - 0s\n",
305-
" 0 0 326.22035 0 18 - 326.22035 - - 0s\n",
306-
" 0 0 331.56424 0 16 - 331.56424 - - 0s\n",
307-
" 0 0 333.84959 0 14 - 333.84959 - - 0s\n",
308-
" 0 0 334.15410 0 33 - 334.15410 - - 0s\n",
309-
"H 0 0 494.5024872 334.15410 32.4% - 0s\n",
310-
" 0 2 334.15410 0 33 494.50249 334.15410 32.4% - 0s\n",
311-
"* 131 58 9 363.5306008 336.61730 7.40% 5.0 0s\n",
312-
"* 145 64 5 350.8980740 336.61730 4.07% 5.7 0s\n",
313-
"\n",
314-
"Cutting planes:\n",
315-
" Zero half: 8\n",
316-
" Lazy constraints: 32\n",
317-
"\n",
318-
"Explored 223 nodes (1444 simplex iterations) in 0.33 seconds\n",
319-
"Thread count was 8 (of 8 available processors)\n",
320-
"\n",
321-
"Solution count 3: 350.898 363.531 494.502 \n",
322-
"\n",
323-
"Optimal solution found (tolerance 1.00e-04)\n",
324-
"Best objective 3.508980740395e+02, best bound 3.508980740395e+02, gap 0.0000%\n",
325-
"\n",
326-
"User-callback calls 634, time in user-callback 0.10 sec\n"
327-
]
328-
}
329-
],
290+
"outputs": [],
330291
"source": [
331-
"import gurobipy as gp\n",
332-
"from gurobipy import GRB\n",
333-
"\n",
334-
"# tested with Python 3.7 & Gurobi 9.0.0\n",
335-
"\n",
336-
"m = gp.Model()\n",
337-
"\n",
338-
"# Variables: is city 'i' adjacent to city 'j' on the tour?\n",
339-
"\n",
340-
"vars = m.addVars(dist.keys(), obj=dist, vtype=GRB.BINARY, name='e')\n",
341-
"for i, j in vars.keys():\n",
342-
" m.addConstr(vars[j, i] == vars[i, j]) # edge in opposite direction\n",
343-
"\n",
344-
"# Constraints: two edges incident to each city\n",
345-
"\n",
346-
"m.addConstrs(vars.sum(c, '*') == 2 for c in capitals)\n",
347-
"\n",
348-
"# Optimize the model\n",
349-
"\n",
350292
"m._vars = vars\n",
351293
"m.Params.lazyConstraints = 1\n",
352-
"m.optimize(subtourelim)\n"
294+
"m.optimize(subtourelim)"
353295
]
354296
},
355297
{
@@ -363,7 +305,7 @@
363305
},
364306
{
365307
"cell_type": "code",
366-
"execution_count": 6,
308+
"execution_count": null,
367309
"metadata": {},
368310
"outputs": [],
369311
"source": [
@@ -385,23 +327,9 @@
385327
},
386328
{
387329
"cell_type": "code",
388-
"execution_count": 7,
330+
"execution_count": null,
389331
"metadata": {},
390-
"outputs": [
391-
{
392-
"data": {
393-
"text/html": [
394-
"<div style=\"width:100%;\"><div style=\"position:relative;width:100%;height:0;padding-bottom:60%;\"><iframe src=\"about:blank\" style=\"position:absolute;width:100%;height:100%;left:0;top:0;border:none !important;\" data-html=PCFET0NUWVBFIGh0bWw+CjxoZWFkPiAgICAKICAgIDxtZXRhIGh0dHAtZXF1aXY9ImNvbnRlbnQtdHlwZSIgY29udGVudD0idGV4dC9odG1sOyBjaGFyc2V0PVVURi04IiAvPgogICAgCiAgICAgICAgPHNjcmlwdD4KICAgICAgICAgICAgTF9OT19UT1VDSCA9IGZhbHNlOwogICAgICAgICAgICBMX0RJU0FCTEVfM0QgPSBmYWxzZTsKICAgICAgICA8L3NjcmlwdD4KICAgIAogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9ucG0vbGVhZmxldEAxLjUuMS9kaXN0L2xlYWZsZXQuanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY29kZS5qcXVlcnkuY29tL2pxdWVyeS0xLjEyLjQubWluLmpzIj48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSJodHRwczovL21heGNkbi5ib290c3RyYXBjZG4uY29tL2Jvb3RzdHJhcC8zLjIuMC9qcy9ib290c3RyYXAubWluLmpzIj48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSJodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9MZWFmbGV0LmF3ZXNvbWUtbWFya2Vycy8yLjAuMi9sZWFmbGV0LmF3ZXNvbWUtbWFya2Vycy5qcyI+PC9zY3JpcHQ+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9ucG0vbGVhZmxldEAxLjUuMS9kaXN0L2xlYWZsZXQuY3NzIi8+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vbWF4Y2RuLmJvb3RzdHJhcGNkbi5jb20vYm9vdHN0cmFwLzMuMi4wL2Nzcy9ib290c3RyYXAubWluLmNzcyIvPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL21heGNkbi5ib290c3RyYXBjZG4uY29tL2Jvb3RzdHJhcC8zLjIuMC9jc3MvYm9vdHN0cmFwLXRoZW1lLm1pbi5jc3MiLz4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly9tYXhjZG4uYm9vdHN0cmFwY2RuLmNvbS9mb250LWF3ZXNvbWUvNC42LjMvY3NzL2ZvbnQtYXdlc29tZS5taW4uY3NzIi8+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL0xlYWZsZXQuYXdlc29tZS1tYXJrZXJzLzIuMC4yL2xlYWZsZXQuYXdlc29tZS1tYXJrZXJzLmNzcyIvPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL3Jhd2Nkbi5naXRoYWNrLmNvbS9weXRob24tdmlzdWFsaXphdGlvbi9mb2xpdW0vbWFzdGVyL2ZvbGl1bS90ZW1wbGF0ZXMvbGVhZmxldC5hd2Vzb21lLnJvdGF0ZS5jc3MiLz4KICAgIDxzdHlsZT5odG1sLCBib2R5IHt3aWR0aDogMTAwJTtoZWlnaHQ6IDEwMCU7bWFyZ2luOiAwO3BhZGRpbmc6IDA7fTwvc3R5bGU+CiAgICA8c3R5bGU+I21hcCB7cG9zaXRpb246YWJzb2x1dGU7dG9wOjA7Ym90dG9tOjA7cmlnaHQ6MDtsZWZ0OjA7fTwvc3R5bGU+CiAgICAKICAgICAgICAgICAgPG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwKICAgICAgICAgICAgICAgIGluaXRpYWwtc2NhbGU9MS4wLCBtYXhpbXVtLXNjYWxlPTEuMCwgdXNlci1zY2FsYWJsZT1ubyIgLz4KICAgICAgICAgICAgPHN0eWxlPgogICAgICAgICAgICAgICAgI21hcF8xZGM2NGI0YjJiYWY0NDI2ODE1NjgxZGYwNjk0ZWVmNyB7CiAgICAgICAgICAgICAgICAgICAgcG9zaXRpb246IHJlbGF0aXZlOwogICAgICAgICAgICAgICAgICAgIHdpZHRoOiAxMDAuMCU7CiAgICAgICAgICAgICAgICAgICAgaGVpZ2h0OiAxMDAuMCU7CiAgICAgICAgICAgICAgICAgICAgbGVmdDogMC4wJTsKICAgICAgICAgICAgICAgICAgICB0b3A6IDAuMCU7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIDwvc3R5bGU+CiAgICAgICAgCjwvaGVhZD4KPGJvZHk+ICAgIAogICAgCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImZvbGl1bS1tYXAiIGlkPSJtYXBfMWRjNjRiNGIyYmFmNDQyNjgxNTY4MWRmMDY5NGVlZjciID48L2Rpdj4KICAgICAgICAKPC9ib2R5Pgo8c2NyaXB0PiAgICAKICAgIAogICAgICAgICAgICB2YXIgbWFwXzFkYzY0YjRiMmJhZjQ0MjY4MTU2ODFkZjA2OTRlZWY3ID0gTC5tYXAoCiAgICAgICAgICAgICAgICAibWFwXzFkYzY0YjRiMmJhZjQ0MjY4MTU2ODFkZjA2OTRlZWY3IiwKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBjZW50ZXI6IFs0MC4wLCAtOTUuMF0sCiAgICAgICAgICAgICAgICAgICAgY3JzOiBMLkNSUy5FUFNHMzg1NywKICAgICAgICAgICAgICAgICAgICB6b29tOiA0LAogICAgICAgICAgICAgICAgICAgIHpvb21Db250cm9sOiB0cnVlLAogICAgICAgICAgICAgICAgICAgIHByZWZlckNhbnZhczogZmFsc2UsCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICk7CgogICAgICAgICAgICAKCiAgICAgICAgCiAgICAKICAgICAgICAgICAgdmFyIHRpbGVfbGF5ZXJfYzA1MTY4YTYyOThlNGEwZjg5OTkyNmRhN2VhY2ZiOTcgPSBMLnRpbGVMYXllcigKICAgICAgICAgICAgICAgICJodHRwczovL3tzfS50aWxlLm9wZW5zdHJlZXRtYXAub3JnL3t6fS97eH0ve3l9LnBuZyIsCiAgICAgICAgICAgICAgICB7ImF0dHJpYnV0aW9uIjogIkRhdGEgYnkgXHUwMDI2Y29weTsgXHUwMDNjYSBocmVmPVwiaHR0cDovL29wZW5zdHJlZXRtYXAub3JnXCJcdTAwM2VPcGVuU3RyZWV0TWFwXHUwMDNjL2FcdTAwM2UsIHVuZGVyIFx1MDAzY2EgaHJlZj1cImh0dHA6Ly93d3cub3BlbnN0cmVldG1hcC5vcmcvY29weXJpZ2h0XCJcdTAwM2VPRGJMXHUwMDNjL2FcdTAwM2UuIiwgImRldGVjdFJldGluYSI6IGZhbHNlLCAibWF4TmF0aXZlWm9vbSI6IDE4LCAibWF4Wm9vbSI6IDE4LCAibWluWm9vbSI6IDAsICJub1dyYXAiOiBmYWxzZSwgIm9wYWNpdHkiOiAxLCAic3ViZG9tYWlucyI6ICJhYmMiLCAidG1zIjogZmFsc2V9CiAgICAgICAgICAgICkuYWRkVG8obWFwXzFkYzY0YjRiMmJhZjQ0MjY4MTU2ODFkZjA2OTRlZWY3KTsKICAgICAgICAKICAgIAogICAgICAgICAgICB2YXIgcG9seV9saW5lXzUzNDJmZjI5N2ZhMjQ2NzViOWYzOGI0NTU5NDA4ZTI0ID0gTC5wb2x5bGluZSgKICAgICAgICAgICAgICAgIFtbMzIuMzYxNTM4LCAtODYuMjc5MTE4XSwgWzMwLjQ1MTgsIC04NC4yNzI3N10sIFszMy43NiwgLTg0LjM5XSwgWzM0LjAsIC04MS4wMzVdLCBbMzUuNzcxLCAtNzguNjM4XSwgWzM3LjU0LCAtNzcuNDZdLCBbMzguOTcyOTQ1LCAtNzYuNTAxMTU3XSwgWzM5LjE2MTkyMSwgLTc1LjUyNjc1NV0sIFs0MC4yMjE3NDEsIC03NC43NTYxMzhdLCBbNDEuNzY3LCAtNzIuNjc3XSwgWzQxLjgyMzU1LCAtNzEuNDIyMTMyXSwgWzQyLjIzNTIsIC03MS4wMjc1XSwgWzQ0LjMyMzUzNSwgLTY5Ljc2NTI2MV0sIFs0My4yMjAwOTMsIC03MS41NDkxMjddLCBbNDQuMjY2MzksIC03Mi41NzE5NF0sIFs0Mi42NTk4MjksIC03My43ODEzMzldLCBbNDAuMjY5Nzg5LCAtNzYuODc1NjEzXSwgWzM4LjM0OTQ5NywgLTgxLjYzMzI5NF0sIFszOS45NjIyNDUsIC04My4wMDA2NDddLCBbNDIuNzMzNSwgLTg0LjU0NjddLCBbNDMuMDc0NzIyLCAtODkuMzg0NDQ0XSwgWzQ0Ljk1LCAtOTMuMDk0XSwgWzQxLjU5MDkzOSwgLTkzLjYyMDg2Nl0sIFszOS4wNCwgLTk1LjY5XSwgWzQwLjgwOTg2OCwgLTk2LjY3NTM0NV0sIFs0NC4zNjc5NjYsIC0xMDAuMzM2Mzc4XSwgWzQ2LjgxMzM0MywgLTEwMC43NzkwMDRdLCBbNDEuMTQ1NTQ4LCAtMTA0LjgwMjA0Ml0sIFszOS43MzkxNjY3LCAtMTA0Ljk4NDE2N10sIFs0MC43NTQ3LCAtMTExLjg5MjYyMl0sIFs0Ni41OTU4MDUsIC0xMTIuMDI3MDMxXSwgWzQzLjYxMzczOSwgLTExNi4yMzc2NTFdLCBbNDcuMDQyNDE4LCAtMTIyLjg5MzA3N10sIFs0NC45MzExMDksIC0xMjMuMDI5MTU5XSwgWzM4LjU1NTYwNSwgLTEyMS40Njg5MjZdLCBbMzkuMTYwOTQ5LCAtMTE5Ljc1Mzg3N10sIFszMy40NDg0NTcsIC0xMTIuMDczODQ0XSwgWzM1LjY2NzIzMSwgLTEwNS45NjQ1NzVdLCBbMzUuNDgyMzA5LCAtOTcuNTM0OTk0XSwgWzMwLjI2NjY2NywgLTk3Ljc1XSwgWzMwLjQ1ODA5LCAtOTEuMTQwMjI5XSwgWzMyLjMyLCAtOTAuMjA3XSwgWzM0LjczNjAwOSwgLTkyLjMzMTEyMl0sIFszOC41NzI5NTQsIC05Mi4xODkyODNdLCBbMzkuNzgzMjUsIC04OS42NTAzNzNdLCBbMzkuNzkwOTQyLCAtODYuMTQ3Njg1XSwgWzM4LjE5NzI3NCwgLTg0Ljg2MzExXSwgWzM2LjE2NSwgLTg2Ljc4NF0sIFszMi4zNjE1MzgsIC04Ni4yNzkxMThdXSwKICAgICAgICAgICAgICAgIHsiYnViYmxpbmdNb3VzZUV2ZW50cyI6IHRydWUsICJjb2xvciI6ICIjMzM4OGZmIiwgImRhc2hBcnJheSI6IG51bGwsICJkYXNoT2Zmc2V0IjogbnVsbCwgImZpbGwiOiBmYWxzZSwgImZpbGxDb2xvciI6ICIjMzM4OGZmIiwgImZpbGxPcGFjaXR5IjogMC4yLCAiZmlsbFJ1bGUiOiAiZXZlbm9kZCIsICJsaW5lQ2FwIjogInJvdW5kIiwgImxpbmVKb2luIjogInJvdW5kIiwgIm5vQ2xpcCI6IGZhbHNlLCAib3BhY2l0eSI6IDEuMCwgInNtb290aEZhY3RvciI6IDEuMCwgInN0cm9rZSI6IHRydWUsICJ3ZWlnaHQiOiAzfQogICAgICAgICAgICApLmFkZFRvKG1hcF8xZGM2NGI0YjJiYWY0NDI2ODE1NjgxZGYwNjk0ZWVmNyk7CiAgICAgICAgCjwvc2NyaXB0Pg== onload=\"this.contentDocument.open();this.contentDocument.write(atob(this.getAttribute('data-html')));this.contentDocument.close();\" allowfullscreen webkitallowfullscreen mozallowfullscreen></iframe></div></div>"
395-
],
396-
"text/plain": [
397-
"<folium.folium.Map at 0x1b8c6c73198>"
398-
]
399-
},
400-
"execution_count": 7,
401-
"metadata": {},
402-
"output_type": "execute_result"
403-
}
404-
],
332+
"outputs": [],
405333
"source": [
406334
"# Map the solution\n",
407335
"\n",
@@ -469,7 +397,7 @@
469397
"name": "python",
470398
"nbconvert_exporter": "python",
471399
"pygments_lexer": "ipython3",
472-
"version": "3.7.1"
400+
"version": "3.8.2"
473401
}
474402
},
475403
"nbformat": 4,

0 commit comments

Comments
 (0)