{
"nbformat_minor": 2,
"nbformat": 4,
"cells": [
{
"source": [
"$$\n",
"\\def\\CC{\\bf C}\n",
"\\def\\QQ{\\bf Q}\n",
"\\def\\RR{\\bf R}\n",
"\\def\\ZZ{\\bf Z}\n",
"\\def\\NN{\\bf N}\n",
"$$\n",
"# Dictionaries and Graph Theory\n",
"\n",
"Authors \n",
"- Franco Saliola\n",
"- M\u00e9lodie Lapointe\n",
"\n",
"## Dictionaries\n",
"\n",
"A *dictionary* is another builtin datatype. Unlike lists or tuples, which are indexed by a range of numbers, dictionaries are indexed by *keys* , which can be any immutable type. Strings and numbers can always be keys. Dictionaries are sometimes called \"associative arrays\" in other programming languages.\n",
"\n",
"There are several ways to define dictionaries. Below are equivalent ways to define a dictionary that pairs 'key1' with 'value1' and 'key2' with 'value2'."
],
"cell_type": "markdown",
"metadata": {}
},
{
"execution_count": null,
"cell_type": "code",
"source": [
"d = {'key1':'value1', 'key2':'value2'}\n",
"d"
],
"outputs": [
{
"execution_count": 1,
"output_type": "execute_result",
"data": {
"text/plain": [
"{'key2': 'value2', 'key1': 'value1'}"
]
},
"metadata": {}
}
],
"metadata": {}
},
{
"execution_count": null,
"cell_type": "code",
"source": [
"d = dict(key1='value1', key2='value2')\n",
"d"
],
"outputs": [
{
"execution_count": 1,
"output_type": "execute_result",
"data": {
"text/plain": [
"{'key2': 'value2', 'key1': 'value1'}"
]
},
"metadata": {}
}
],
"metadata": {}
},
{
"execution_count": null,
"cell_type": "code",
"source": [
"d = dict([('key1','value1'), ('key2','value2')])\n",
"d"
],
"outputs": [
{
"execution_count": 1,
"output_type": "execute_result",
"data": {
"text/plain": [
"{'key2': 'value2', 'key1': 'value1'}"
]
},
"metadata": {}
}
],
"metadata": {}
},
{
"execution_count": null,
"cell_type": "code",
"source": [
"d = {1:[2,2], 'a':(6,5), 0:{1:2}}\n",
"d"
],
"outputs": [
{
"execution_count": 1,
"output_type": "execute_result",
"data": {
"text/plain": [
"{'a': (6, 5), 1: [2, 2], 0: {1: 2}}"
]
},
"metadata": {}
}
],
"metadata": {}
},
{
"source": [
"Dictionaries behave as lists, tuples, and strings for several important operations.\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"*Operation* | *Syntax for lists* | *Syntax for dictionaries* |

Accessing elements | **L[3]** | **D[3]** |

Length | **len(L)** | **len(D)** |

Modifying | **L[3] = 17** | **D[3] = 17** |

Deleting items | **del L[3]** | **del D[3]** |

\n",
"\n",
"**Exercise:** In the directed graph below, the vertex 1 points to the vertices in the list \\[2, 3\\].\n",
"\n",
"![image](./graph0.png)\n",
"\n",
"Create a dictionary with keys the vertices of the above directed graph, and with values the lists of vertices pointed to by the vertex."
],
"cell_type": "markdown",
"metadata": {}
},
{
"execution_count": null,
"cell_type": "code",
"source": [
"E = {0:[1,1,2],1:[2,3],2:[3],3:[4,4]};E"
],
"outputs": [
{
"execution_count": 1,
"output_type": "execute_result",
"data": {
"text/plain": [
"{0: [1, 1, 2], 1: [2, 3], 2: [3], 3: [4, 4]}"
]
},
"metadata": {}
}
],
"metadata": {}
},
{
"source": [
"**Exercise:** Use the **DiGraph** command to contruct the above directed graph, and plot the directed graph ( *Hint* : In the documentation for **DiGraph**, take a look at the ' *dictionary of lists* ' example.)"
],
"cell_type": "markdown",
"metadata": {}
},
{
"execution_count": null,
"cell_type": "code",
"source": [
"G = DiGraph(E)\n",
"G.show()"
],
"outputs": [],
"metadata": {}
},
{
"source": [
"**Exercise:** Find the *adjacency matrix* of the graph you constructed above."
],
"cell_type": "markdown",
"metadata": {}
},
{
"execution_count": null,
"cell_type": "code",
"source": [
"G.adjacency_matrix()"
],
"outputs": [
{
"execution_count": 1,
"output_type": "execute_result",
"data": {
"text/plain": [
"[0 2 1 0 0]\n",
"[0 0 1 1 0]\n",
"[0 0 0 1 0]\n",
"[0 0 0 0 2]\n",
"[0 0 0 0 0]"
]
},
"metadata": {}
}
],
"metadata": {}
},
{
"source": [
"**Exercise:** Compute the square of the adjacency matrix. Give a graph-theoretic intepretation of the numbers in this matrix. Does your intepretation hold for the cube of the adjacency matrix?"
],
"cell_type": "markdown",
"metadata": {}
},
{
"execution_count": null,
"cell_type": "code",
"source": [
"(G.adjacency_matrix())^2"
],
"outputs": [
{
"execution_count": 1,
"output_type": "execute_result",
"data": {
"text/plain": [
"[0 0 2 3 0]\n",
"[0 0 0 1 2]\n",
"[0 0 0 0 2] \n",
"[0 0 0 0 0]\n",
"[0 0 0 0 0]"
]
},
"metadata": {}
}
],
"metadata": {}
},
{
"source": [
"## The Seven Bridges of K\u00f6nigsberg\n",
"\n",
"**Exercise:** The *Seven Bridges of K\u00f6nigsberg* is the following famous historical problem solved by Leonhard Euler in 1735. This is the problem that started graph theory.\n",
"\n",
"> The city of K\u00f6nigsberg in Prussia (now Kaliningrad, Russia) was set on both sides of the Pregel River, and included two large islands which were connected to each other and the mainland by seven bridges. The problem was to find a walk through the city that would cross each bridge once and only once. The islands could not be reached by any route other than the bridges, and every bridge must have been crossed completely every time (one could not walk halfway onto the bridge and then turn around to come at it from another side).\n",
">\n",
"> \n",
"\n",
"1. Enter the graph on the right into Sage (use the **Graph** command, not the **DiGraph** command)."
],
"cell_type": "markdown",
"metadata": {}
},
{
"execution_count": null,
"cell_type": "code",
"source": [
"D = {1:[2,2,3,4,4],2:[1,1,3],3:[1,2,4],4:[1,1,3]}\n",
"G = Graph(D)\n",
"G.show()"
],
"outputs": [],
"metadata": {}
},
{
"source": [
"1. Solve the problem; that is, does such a walk exist? ( *Hint:* Take a look at the documentation for the **eulerian\\_circuit** method; look up *Eulerian circuit* in Wikipedia if you don't know its definition.)"
],
"cell_type": "markdown",
"metadata": {}
},
{
"execution_count": null,
"cell_type": "code",
"source": [
"G.eulerian_circuit()"
],
"outputs": [
{
"execution_count": 1,
"output_type": "execute_result",
"data": {
"text/plain": [
"False"
]
},
"metadata": {}
}
],
"metadata": {}
},
{
"source": [
"*(The quotation and the image are from the Wikipedia page [Seven Bridges of K\u00f6nigsberg](http://en.wikipedia.org/wiki/Seven_Bridges_of_K%C3%B6nigsberg) ; the problem is from* *[William Stein's](http://modular.math.washington.edu/)* *Graph Theory Worksheet for [Math 480b \\[2009](http://wiki.wstein.org/09/480b) \\])*\n",
"\n",
"## The Coxeter Graph\n",
"\n",
"The *Coxeter graph* is the graph with 28 vertices $v_{i,j}$, for $0 \\leq i, j, \\leq 6$, and with edges described by the rules:\n",
"\n",
"1. $v_{0,i}$ is connected to $v_{1,i}, v_{2,i}, v_{3,i}$ for all $0\\leq i \\leq 6$;\n",
"2. $v_{1,j}$ is connected to $v_{1, j+1 (mod\\, 7)}$ for all $0\\leq j \\leq 6$;\n",
"3. $v_{2,j}$ is connected to $v_{2, j+2 (mod\\, 7)}$ for all $0\\leq j \\leq 6$;\n",
"4. $v_{3,j}$ is connected to $v_{3, j+3 (mod\\, 7)}$ for all $0\\leq j \\leq 6$.\n",
"\n",
"**Exercise:** Construct a dictionary **V** such that **V\\[(i,j)\\]** is the list of vertices **(r,s)** that are connected to **(i,j)**. Use this dictionary to construct and plot the *Coxeter graph* . ( *Hints:* Note that writing **V\\[i,j\\]** is shorthand for writing **V\\[(i,j)\\]**. You should be able to generate the lists of vertices by using loops and list comprehensions.)"
],
"cell_type": "markdown",
"metadata": {}
},
{
"execution_count": null,
"cell_type": "code",
"source": [
"V = {(0,j):[(i,j) for i in [1,2,3]] for j in range(7)}\n",
"V.update({(i,j):[(i,(j+i)%7)] for i in [1,2,3] for j in range(7)})\n",
"V"
],
"outputs": [],
"metadata": {}
},
{
"source": [
"## Spectrum of a graph\n",
"\n",
"The *spectrum* of a graph is the set of eigenvalues of the adjacency matrix of the graph. The *spectrum* of the Coxeter graph is\n",
"\n",
"- $-1-\\sqrt{6}$ with multiplicity 6,\n",
"- $-1$ with multiplicity 7,\n",
"- $\\sqrt{2}-1$ with multiplicity 6,\n",
"- $2$ with multiplicity 8,\n",
"- $3$ with multiplicity 1.\n",
"\n",
"It turns out that no other graph has this same spectrum (in this case, we say that the graph *is determined by its spectrum* ).\n",
"\n",
"**Exercise:** Test to see that you correctly constructed the Coxeter graph in the previous exercise. That is, compute the **adjacency matrix** of the Coxeter graph, find the **eigenvalues** of the adjacency matrix, and then compare them with the above."
],
"cell_type": "markdown",
"metadata": {}
},
{
"execution_count": null,
"cell_type": "code",
"source": [
"G = Graph(V)\n",
"sorted(G.adjacency_matrix().eigenvalues())"
],
"outputs": [],
"metadata": {}
},
{
"source": [
"**Exercise:** The command **graphs(n)** generates all the graphs on $n$ vertices (up to isomorphism). Use this command to test whether there are two graphs with less than 7 vertices that have the same spectrum."
],
"cell_type": "markdown",
"metadata": {}
},
{
"execution_count": null,
"cell_type": "code",
"source": [
"spectrum = set({})\n",
"compteur = 0\n",
"for i in range(1,4):\n",
" for g in graphs(i):\n",
" compteur += 1\n",
" e = frozenset(g.adjacency_matrix().eigenvalues())\n",
" if spectrum.isdisjoint(set({e})):\n",
" spectrum.add(e)\n",
" else:\n",
" print('Two graphs have the same spectrum: ', e)\n",
" break"
],
"outputs": [
{
"execution_count": 1,
"output_type": "execute_result",
"data": {
"text/plain": [
"('Two graphs have the same spectrum: ', frozenset([0]))"
]
},
"metadata": {}
}
],
"metadata": {}
},
{
"source": [
"## Birthday Paradox\n",
"\n",
"In the following exercises, we will use Sage to estimate the probability that in a group of $n$ people, two of them will have the same birthday.\n",
"\n",
"**Exercise:** Using the command **graphs.RandomGNP**, create a function that returns a graph with $n$ vertices and where the probability that any two of the vertices is connected is 1/365."
],
"cell_type": "markdown",
"metadata": {}
},
{
"execution_count": null,
"cell_type": "code",
"source": [
"n = 23\n",
"G = graphs.RandomGNP(n,1/365)"
],
"outputs": [],
"metadata": {}
},
{
"source": [
"**Exercise:** Plot a graph **g** created by your function above using the **g.plot(layout='circular')**."
],
"cell_type": "markdown",
"metadata": {}
},
{
"execution_count": null,
"cell_type": "code",
"source": [
"G.plot(layout='circular')"
],
"outputs": [],
"metadata": {}
},
{
"source": [
"**Exercise:** Create 100 random graphs (using your above function) with $n=23$ vertices. What ratio of them contains an edge? ( *Hint:* For a graph **g**, the command **g.num\\_edges()** returns the number of edges in **g**.)"
],
"cell_type": "markdown",
"metadata": {}
},
{
"execution_count": null,
"cell_type": "code",
"source": [
"compteur = 0\n",
"for i in range(100):\n",
" G = graphs.RandomGNP(23,1/365)\n",
" if G.num_edges() > 0:\n",
" compteur += 1\n",
"compteur"
],
"outputs": [
{
"execution_count": 1,
"output_type": "execute_result",
"data": {
"text/plain": [
"51"
]
},
"metadata": {}
}
],
"metadata": {}
},
{
"source": [
"**Exercise:** Repeat the above exercise with $n=57$ vertices."
],
"cell_type": "markdown",
"metadata": {}
},
{
"execution_count": null,
"cell_type": "code",
"source": [
"compteur = 0\n",
"for i in range(100):\n",
" G = graphs.RandomGNP(57,1/365)\n",
" if G.num_edges() > 0:\n",
" compteur += 1\n",
"compteur"
],
"outputs": [
{
"execution_count": 1,
"output_type": "execute_result",
"data": {
"text/plain": [
"98"
]
},
"metadata": {}
}
],
"metadata": {}
},
{
"source": [
"**Exercise:** Repeat the above exercises for all the values $1, 2, ..., 120$. Plot the results using a line graph."
],
"cell_type": "markdown",
"metadata": {}
},
{
"execution_count": null,
"cell_type": "code",
"source": [
"ratio = []\n",
"for n in range(1,121):\n",
" compteur = 0\n",
" for i in range(100):\n",
" G = graphs.RandomGNP(n,1/365)\n",
" if G.num_edges() > 0:\n",
" compteur += 1\n",
" ratio.append(compteur)\n",
"l = [(i+1,ratio[i]) for i in range(120)]\n",
"line(l)"
],
"outputs": [],
"metadata": {}
}
],
"metadata": {
"kernelspec": {
"display_name": "sagemath",
"name": "sagemath"
}
}
}