networkit-rs 0.1.0

Rust bindings for Networkit
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# NetworKit graph I/O tutorial\n",
    "\n",
    "This notebook will guide you through reading and writing graphs to files using NetworKit, i.e. the following will be covered,\n",
    "- Reading and writing graphs graph from a file\n",
    "    - Using the different file formats supported by NetworKit\n",
    "- Converting a graph to a specific format "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import networkit as nk"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Reading a graph from a file\n",
    "\n",
    "NetworKit supports several graph formats which can be found via [graphio.Format](https://networkit.github.io/dev-docs/python_api/graphio.html?highlight=format#networkit.graphio.Format).\n",
    "Although most graphs support both reading and writing, a few do not support both alike. If you will be reading large graphs often, it is recommended to convert your graph to the ```NetworkitBinaryGraph``` format as this is currently the fastest reader available in NetworKit. For information on how to convert graphs between formats in NetworKit, see the section on [converting graphs](#converting)\n",
    "\n",
    "### SNAP file format\n",
    "\n",
    "The (optional) first line of a file denotes the problem line p <n> <m> <undirected/directed> <weight_type> <0 or 1-indexed>.\n",
    "\n",
    "The problem line is followed by a list of exactly m edges. \n",
    "The format is `<u v w>` for a weighted graph, and `<u v>` for an unweighted graph.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The [SNAPGraphReader(directed = False, remapNodes = True, nodeCount = 0)](https://networkit.github.io/dev-docs/python_api/graphio.html?highlight=snap#networkit.graphio.SNAPGraphReader) constructor expects 3 optional values, i.e., ```directed``` which is true if the graph is directed, ```remapNodes``` indicates whether nodes should be remapped to other node ids in order to create consecutive node ids and the number of nodes in the graph as ```nodeCount``` which is used to preallocate memory for the number of nodes."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Reading a file in SNAP using the default constructor values can done like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G = nk.readGraph(\"../input/wiki-Vote.txt\", nk.Format.SNAP)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can now access the graph object via ```G```. Alternatively, you can explicitly use the ```SNAPGraphReader``` class as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G = nk.graphio.SNAPGraphReader().read(\"../input/wiki-Vote.txt\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Passing other values to the ```SNAPGraphReader``` can be done by creating a ```SNAPGraphReader``` object and then calling the ```read``` method on it like is done below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "snapReader = nk.graphio.SNAPGraphReader(True, False, 7115)\n",
    "G = snapReader.read(\"../input/wiki-Vote.txt\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we want to write ```G``` to a file, we can use [writeGraph()](https://networkit.github.io/dev-docs/python_api/networkit.html?highlight=writegraph#networkit.writeGraph), and pass ```G```, the ```path``` to the file the graph should be written to and the format to the method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "if not os.path.isdir('./output/'):\n",
    "    os.makedirs('./output')\n",
    "nk.writeGraph(G,\"./output/wikiSNAP\", nk.Format.SNAP)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### EdgeList file format"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The [EdgeList](https://networkit.github.io/dev-docs/python_api/networkit.html?highlight=edgelist#networkit.Format.EdgeList) file format is a simple format that stores each node's adjacency array in a seperate line. The ```EdgeList``` file format has several variations, all differing in the character used to seperate nodes in an edge list or the ID of the first node.  The constructor [EdgeListReader(separator, firstNode, commentPrefix = \"#\", continuous = True, directed = False)](https://networkit.github.io/dev-docs/python_api/graphio.html?highlight=edgelist#networkit.graphio.EdgeListReader) expects 5 parameters that dictate the exact format of the edge lists. NetworKit provides five standard ```EdgeListReader```s: \n",
    "\n",
    "1. ```EdgeListSpaceZero``` with ```seperator``` being a whitespace and ```firstNode```'s ID is 0.\n",
    "2. ```EdgeListSpaceOne``` with ```seperator``` being a whitespace and ```firstNode```'s ID is 1.\n",
    "3. ```EdgeListTabZero``` with ```seperator``` being a tab and ```firstNode```'s ID is 0.\n",
    "4. ```EdgeListTab0ne``` with ```seperator``` being a tab and ```firstNode```'s ID is 1.\n",
    "5. ```EdgeListCommaOne``` with ```seperator```being a comma and ```firstNode```'s ID is 1.\n",
    "\n",
    "Reading can be done in the same way as in the previous example. You can specify a different format for the ```EdgeListReader``` by calling its constructor, and passing the values to it. Assuming we want to use a '$' as a seperator, the first node is 0, and comments are prefixed by a semi-colon, we can do the following:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Specify seperator, firstNode and commentPrefix for the EdgeListReader\n",
    "edgeListReader = nk.graphio.EdgeListReader('$', 0, ';')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Reading a file with one of the exisiting ```EdgeListReader```s, e.g. ```EdgeListTabOne``` can be done by calling the ```readGraph``` method and specifying the format as ```EdgeListTabOne``` <a id='edgelist'></a>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G = nk.readGraph(\"../input/example.edgelist\", nk.Format.EdgeListTabOne)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can write ```G``` to a file by calling the ```writeGraph()``` method, and passing ```G```, the ```path``` to the file the graph should be written to, and the format to ```writeGraph()```."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "if not os.path.isdir('./output'):\n",
    "    os.makedirs('./output')\n",
    "nk.writeGraph(G, './output/example.edgelist.TabOne', nk.Format.EdgeListTabOne)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### METIS file format\n",
    "The [METIS](https://networkit.github.io/dev-docs/python_api/networkit.html?highlight=metis#networkit.Format.METIS) format stores a graph of N nodes is stored in a file of N+1 lines. The first line lists the number of nodes and the number of edges seperated by a whitespace. If the first line contains more than two values, the extra values indicate the weights. Each line then contains a node's adjacency list. Comment lines begin with a \"%\" sign. A file in ```METIS``` format can be read using the ```readGraph``` method or by explicitly using the [METISGraphReader](https://networkit.github.io/dev-docs/python_api/graphio.html?highlight=metis#networkit.graphio.METISGraphReader) class:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G = nk.readGraph(\"../input/celegans_metabolic.graph\", nk.Format.METIS)\n",
    "# Alternative:\n",
    "metisReader = nk.graphio.METISGraphReader()\n",
    "G = metisReader.read(\"../input/celegans_metabolic.graph\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Writing a file in ```METIS``` format is the same as for the other formats we have seen so far:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "if not os.path.isdir('./output/'):\n",
    "    os.makedirs('./output')\n",
    "nk.writeGraph(G,\"./output/celegans_metabolicMETIS\", nk.Format.METIS)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### GraphML format\n",
    "\n",
    "The [GML](https://networkit.github.io/dev-docs/python_api/graphio.html?highlight=gml#networkit.graphio.Format.GML) format is an XML-based file format for graphs. For me details, please refer to the [GML format specification.](http://www.fim.uni-passau.de/fileadmin/files/lehrstuhl/brandenburg/projekte/gml/gml-technical-report.pdf) Reading a file in ```GML``` is done in the same way as is reading other formats."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G = nk.readGraph(\"../input/jazz2_directed.gml\", nk.Format.GML)\n",
    "# Alternative:\n",
    "gmlReader = nk.graphio.GMLGraphReader()\n",
    "G = gmlReader.read(\"../input/jazz2_directed.gml\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Writing a file in ```GML``` format is the same as for the other formats we have seen so far:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "if not os.path.isdir('./output/'):\n",
    "    os.makedirs('./output')\n",
    "nk.writeGraph(G,\"./output/jazz2_directedGML\", nk.Format.GML)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### GraphViz/ DOT file format\n",
    "\n",
    "NetworKit currently only supports writing of the ```DOT``` file format. More information on the ```DOT``` file format can be found [here](https://www.graphviz.org/doc/info/lang.html). We can read a graph in any format, and then write it in ```DOT``` as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "# Read graph in GML \n",
    "G = nk.readGraph(\"../input/jazz2_directed.gml\", nk.Format.GML)\n",
    "\n",
    "if not os.path.isdir('./output/'):\n",
    "    os.makedirs('./output')\n",
    "# Write G in GraphViz/DOT\n",
    "nk.writeGraph(G,\"./output/jazz2_directedGraphViz\", nk.Format.GraphViz)\n",
    "# Write G in DOT\n",
    "nk.writeGraph(G,\"./output/jazz2_directedDOT\", nk.Format.DOT)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### LFR \n",
    "\n",
    "Graphs in [LFR](https://networkit.github.io/dev-docs/python_api/graphio.html?highlight=lfr#networkit.graphio.Format.LFR) are identical to those in the ```EdgeListTabOne``` format. Therefore, in order to read a graph in ```LFR```, the ```EdgeListTabOne``` reader is used. Refer to the section about the ```EdgeListTabOne``` reader [here.](#edgelist)\n",
    "Alternatively, you can also read the graph by specifying ```LFR``` as the format. In this case, NetworKit calls the ```EdgeListTabOne```reader internally. The same goes for writing a graph to a file in the ```LFR``` file format."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G = nk.readGraph(\"../input/network_overlapping.dat\", nk.Format.LFR)\n",
    "\n",
    "import os\n",
    "if not os.path.isdir('./output/'):\n",
    "    os.makedirs('./output')\n",
    "nk.writeGraph(G,\"./output/network_overlapping.dat\", nk.Format.LFR)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### KONECT file format\n",
    "\n",
    "The reader [KONECTGraphReader(remapNodes = False, handlingmethod = DISCARD_EDGES)](https://networkit.github.io/dev-docs/python_api/graphio.html?highlight=konect#networkit.graphio.KONECTGraphReader) expects two parameters; Node ids are remapped to consecutive ids if```remapNodes```is set to true. If your graph contains multiple edges between nodes, ```handlingmethod``` specifies how NetworKit should handle the multiple edges. ```handlingmethod``` can take any of the following three values:\n",
    " - DISCARD_EDGES = 0, //Reads and selects the first edge which occurs and discards all following\n",
    " - SUM_WEIGHTS_UP = 1, //If an edge occurs again, the weight of it is added to the existing edge\n",
    " - KEEP_MINIUM_WEIGHT = 2 //The edge with the lowest weight is kept\n",
    "\n",
    "In order to read a graph with multiple edges in while summing the weights of the multiple edges, you can pass the parameters to the ```KONECTGraphReader``` as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "konectReader = nk.graphio.KONECTGraphReader(True, 1)\n",
    "G = konectReader.read(\"../input/foodweb-baydry.konect\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "NetworKit currently only supports reading of the ```KONECT``` file format. If you want to write your graph to a file, you can write the graph in another format of your choice."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### GraphToolBinary file format\n",
    "\n",
    "The [GraphToolBinaryReader](https://networkit.github.io/dev-docs/python_api/graphio.html?highlight=graphtool#networkit.graphio.GraphToolBinaryReader) reads graphs written in the binary format described [here](https://graph-tool.skewed.de/static/doc/gt_format.html). The graph's properties are stored in the file, and therefore, no constructor arguments are passed to the ```reader```. Reading a graph from a file in ```GraphToolBinaryReader```can be done like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G = nk.readGraph(\"../input/power.gt\", nk.Format.GraphToolBinary)\n",
    "# Alternative:\n",
    "graphToolReader = nk.graphio.GraphToolBinaryReader()\n",
    "G = graphToolReader.read(\"../input/power.gt\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When writing the graph to the file, the writer [GraphToolBinaryWriter(littleEndianness = True)](https://networkit.github.io/dev-docs/python_api/graphio.html?highlight=graphtool#networkit.graphio.GraphToolBinaryWriter) expects a Boolean value indicating the endianness of the machine. Set ```littleEndianness``` to true if you are running a little endian machine. The example below shows how you can pass the endianness to the ```GraphToolBinaryWriter```."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "if not os.path.isdir('./output/'):\n",
    "    os.makedirs('./output')\n",
    "nk.writeGraph(G,\"./output/power.gt\", nk.Format.GraphToolBinary, littleEndianness=True)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ThrillBinary file format\n",
    "\n",
    "The [ThrillBinaryReader(n)](https://networkit.github.io/dev-docs/python_api/graphio.html?highlight=thrill#networkit.graphio.ThrillGraphBinaryReader) reads a graph format consisting of a serialized DIA of vector<uint32_t> from the [Thrill format](http://project-thrill.org/). The constructor optionally takes a 64-but unsigned integer ```n``` which is the number of nodes in the graph. Reading is more efficient if the ```ThrillBinaryReader``` knows the number of nodes. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G = nk.readGraph(\"../input/celegans_metabolic.thrill\", nk.Format.ThrillBinary)\n",
    "# Alternative:\n",
    "thrillBinaryReader = nk.graphio.ThrillGraphBinaryReader()\n",
    "G = thrillBinaryReader.read(\"../input/celegans_metabolic.thrill\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Using the [ThrillBinaryWriter](https://networkit.github.io/dev-docs/python_api/graphio.html?highlight=thrill#networkit.graphio.ThrillGraphBinaryWriter), writing is similar to the other writers: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "if not os.path.isdir('./output/'):\n",
    "    os.makedirs('./output')\n",
    "nk.writeGraph(G,\"./output/foodweb-baydry.thrill\", nk.Format.ThrillBinary)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### NetworkitBinaryGraph file format\n",
    "\n",
    "The [NetworkitBinaryGraph](https://networkit.github.io/dev-docs/python_api/graphio.html?highlight=binary#networkit.graphio.NetworkitBinaryReader) is a custom binary NetworKit file format for reading and writing graphs. It is not only much faster than existing formats, it is also compressed. The graph properties are stored directly in the file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G = nk.readGraph(\"../input/foodweb-baydry.networkit\", nk.Format.NetworkitBinary)\n",
    "#Alternative:\n",
    "networkitBinaryReader = nk.graphio.NetworkitBinaryReader()\n",
    "G = networkitBinaryReader.read(\"../input/foodweb-baydry.networkit\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The [NetworkitBinaryWriter(chunks = 32, weightsType = NetworkitBinaryWeights::AUTO_DETECT)](https://networkit.github.io/dev-docs/python_api/graphio.html?highlight=binary#networkit.graphio.NetworkitBinaryWriter) constructor takes two optional parameters. The ```NetworkitBinaryWriter``` groups nodes in to ```chunks``` which reduces the space needed to save a graph. Futhermore, it takes the type of weights as an optional parameter. If none is passed, the ```NetworkitBinaryWriter``` detects the type of weights automatically. ```weightsType``` can be any of the following options: \n",
    "    - none = 0, // The graph is not weighted\n",
    "\t- unsignedFormat = 1, //The weights are unsigned integers\n",
    "\t- signedFormat = 2, //The weights are signed integers\n",
    "\t- doubleFormat = 3, //The weights are doubles\n",
    "\t- floatFormat = 4, //The weights are floats\n",
    "\t- autoDetect \n",
    "You can pass the number of chunks and type of weights to the writer as follows(assuming signed weights):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "if not os.path.isdir('./output/'):\n",
    "    os.makedirs('./output')\n",
    "nk.writeGraph(G,\"./output/foodweb-baydry.networkit\", nk.Format.NetworkitBinary, chunks=16, NetworkitBinaryWeights=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Convert graphs to other formats\n",
    "Not all graph formats support reading and writing alike, and therefore, one may want to convert a graph to a different format.\n",
    "For example, if you want to convert `'../input/wiki-Vote.txt'` in the SNAP format to GML and save it in the `./output` directory, you can either use the [convertGraph(fromFormat, toFormat, fromPath, toPath=None)](https://networkit.github.io/dev-docs/python_api/graphio.html?highlight=convert#networkit.graphio.convertGraph) function from ```graphio```:\n",
    "<a id='converting'></a>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nk.graphio.convertGraph(nk.Format.SNAP, nk.Format.GML, \"../input/wiki-Vote.txt\", \"output/example.gml\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "or you can pass the new format to the [writeGraph](https://networkit.github.io/dev-docs/python_api/graphio.html?highlight=writegraph#networkit.graphio.writeGraph) method: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "if not os.path.isdir('./output/'):\n",
    "    os.makedirs('./output')\n",
    "nk.writeGraph(G,\"./output/example.gml\", nk.Format.GML)"
   ]
  }
 ],
 "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.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}