diff --git a/year4/semester2/CS4423/materials/CS4423-W02-1.ipynb b/year4/semester2/CS4423/materials/CS4423-W02-1.ipynb
new file mode 100644
index 00000000..69393763
--- /dev/null
+++ b/year4/semester2/CS4423/materials/CS4423-W02-1.ipynb
@@ -0,0 +1,1106 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "toc": true
+ },
+ "source": [
+ "
Table of Contents
\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "# CS4423 : Week 02 - Lecture 1 - Networks [$\\color{red}{\\text{DRAFT}}$]\n",
+ "# More on Graphs, and `networkx`\n",
+ "\n",
+ "Niall Madden, \n",
+ "School of Mathematical and Statistical Sciences \n",
+ "University of Galway\n",
+ "\n",
+ "(These notes are adapted from Angela Carnevale's work)\n",
+ "\n",
+ "This notebook is at https://www.niallmadden.ie/2425-CS4423/W02/CS4423-W02-1.ipynb\n",
+ "You can read the HTML version at https://www.niallmadden.ie/2425-CS4423/W02/CS4423-W02-1.html\n",
+ "\n",
+ "This version of this notebook was written by Niall Madden, adapted from notebooks by Angela Carnevale.
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "## News: \n",
+ "### Labs\n",
+ "\n",
+ "Labs start next week, and an (reintroduction) to Python. This will run:\n",
+ "* Tuesday at 4 in AC215 (slight chance this might get moved to Tuesday at 3), and\n",
+ "* Wednesday at 10am in CA116a. \n",
+ "\n",
+ "These rooms are not labs: BYoD! (Bring Your Own Device)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Website\n",
+ "I now plan to post all notes to https://www.niallmadden.ie/2425-CS4423/ as well as to Canvas."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "## `networkx`\n",
+ "Last week we learned a little about the `networkx` package. We'll return to that now, while also revisiting some key ideas about graphs."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As ever, we'll start with importing the `networkx` module, as well as `numpy`: more about that later. And we'll define the `opts` option dictionary."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import networkx as nx\n",
+ "import numpy as np\n",
+ "opts = { \"with_labels\": True, \"node_color\": 'y' } # show labels; yellow noodes"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "To create a graph with nodes $1$, $2$, $3$, $4$, $5$, and edges between all even and odd labelled nodes:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "-"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "K32 = nx.Graph()\n",
+ "K32.add_edges_from([(1, 2), (1,4), (2,3), (2,5), (3,4), (4,5)])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We'll later learn this is the graph $K_{3,2}$. Now draw it:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "-"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "nx.draw(K32, **opts)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "source": [
+ "We can also be lazy, and just give $2$-letter strings for the edges: this implicitly defines the nodes too. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "-"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "K33 = nx.Graph([\"A1\", \"A2\", \"A3\", \"B1\", \"B2\", \"B3\", \"C1\", \"C2\", \"C3\"])\n",
+ "nx.draw(K33, **opts)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "source": [
+ "### Check basic properties:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(f\"K33 has {K33.number_of_nodes()} nodes and {K33.number_of_edges()} edges\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false
+ },
+ "outputs": [],
+ "source": [
+ "print(f\"This is the same as saying K33 order {K33.order()} and size {K33.size()}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To list the nodes and edges (as lists)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false
+ },
+ "outputs": [],
+ "source": [
+ "list(K33.nodes)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false
+ },
+ "outputs": [],
+ "source": [
+ "list(K33.edges)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "source": [
+ "A **loop** over a graph `G` will effectively loop over `G`'s nodes. As an example, (recall?) that the **degree** of a node is the number of edges incident to it (or, if you prefer, the number of neighbours)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false
+ },
+ "outputs": [],
+ "source": [
+ "for node in K33:\n",
+ " print(f\"node {node} has neighbours {list(K33.neighbors(node))}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "source": [
+ "### Adding and removing nodes and edges\n",
+ "\n",
+ "We say that \n",
+ "* `G.add_node('v')` adds a node to $G$ called 'v'\n",
+ "* `G.add_nodes_from([2, 3, 5])` adds all the nodes from a list\n",
+ "* `G.add_nodes_from(H])` adds all the nodes from Graph $H$ to Graph $G$\n",
+ "* `G.add_edge('x','y')` add edge from Node $x$ to Node $y$, adding one or both nodes, if needed.\n",
+ "* `G.add_edges_from([(1,5), (2,5), (3,5)])` add edges from a list\n",
+ "* `G.add_edges_from(H.edges)` add edges from another graph\n",
+ "* `G.remove_edge('x','y')` remove edge from $x$ to $y$, but keep the nodes\n",
+ "* `G.remove_node('x')` remove node $x$ and any edge it was incident to."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "## Neighbours and degree\n",
+ "\n",
+ "(As we've seen) \n",
+ "* The neighbours of a node are those that it shares an edge with\n",
+ "* the degree of a node is the number of edges incident to it (or, if you prefer, the number of neighbours).\n",
+ "\n",
+ "Let's look at an example:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "Edges1 = [('Aoife', 'Brian'), ('Aoife', 'Ciara'), ('Aoife', 'Daire'), ('Aoife', 'Ella'), \n",
+ " ('Aoife', 'Finn'), ('Brian', 'Ciara'), ('Brian', 'Finn'), ('Ciara', 'Daire'), \n",
+ " ('Daire', 'Ella'), ('Ella', 'Finn') ]\n",
+ "G1 = nx.Graph(Edges1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "nx.draw(G1, **opts)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "source": [
+ "This is example, which is known as a *wheel graph\" is chosen because it exhibits a famous concept in Network Science: *The Friendship Paradox*: your friends (probably) have more friends, on average, than you do!\n",
+ "\n",
+ "Explanation:\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "notes"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "#pos = nx.nx_agraph.graphviz_layout(G1)\n",
+ "#nx.draw(G1, pos=pos,**opts)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "## Important Graphs\n",
+ "\n",
+ "In this section we'll discuss some important examples of graphs, which we'll return to later as key examples of networks. These include\n",
+ "* Complete Graphs\n",
+ "* Bipartite and complete bipartite graphs\n",
+ "* Path graphs"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "source": [
+ "### Complete Graphs\n",
+ "\n",
+ "The [**complete graph**](https://en.wikipedia.org/wiki/Complete_graph)\n",
+ "on a vertex set $X$ is the graph with edge set all of $\\binom{X}{2}$. That is: every node is a neighbour of every other node. It is denoted $K_n$ where $n=|X|$. E.g., if $X=\\{0,1,2,3\\}$, then $K_4$ (\"the complete graph on 4 nodes\") has edges $E=\\{01, 02, 03, 12, 13, 23\\}$."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "-"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "nodes = range(4)\n",
+ "list(nodes)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "E4 = [(x, y) for x in nodes for y in nodes if x < y]\n",
+ "print(E4)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false
+ },
+ "outputs": [],
+ "source": [
+ "K4 = nx.Graph(E4)\n",
+ "nx.draw(K4)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "source": [
+ "While it is somewhat straightforward to find all $2$-element\n",
+ "subsets of a given set $X$ with a short `python` program,\n",
+ "it is probably more convenient (and possibly efficient) to use a function from the\n",
+ "`itertools` package for this purpose."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "-"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from itertools import combinations\n",
+ "nodes5 = range(5)\n",
+ "combinations(nodes5, 2)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false
+ },
+ "outputs": [],
+ "source": [
+ "print(list(combinations(nodes5, 2)))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "K5 = nx.Graph(combinations(nodes5, 2))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "-"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "nx.draw(K5, **opts)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "source": [
+ "`networkx` has a built-in function to create complete graphs: `complete_graph` [[doc]](https://networkx.org/documentation/stable//reference/generated/networkx.generators.classic.complete_graph.html)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false
+ },
+ "outputs": [],
+ "source": [
+ "nx.draw(nx.complete_graph(\"NETWORKS\"), **opts)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "nx.draw(nx.complete_graph(22), **opts)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Bipartite Graphs\n",
+ "\n",
+ "A graph is **bipartite** if we can divide the node set, $X$, into two subsets $X_1$ and $X_2$ such that \n",
+ "* $X_1 \\cap X_2 = \\emptyset$ (the sets have no edge in common)\n",
+ "* $X_1 \\cup X_2 = X$ \n",
+ "* For any edge $(u_1,u_2)$ we have $u_1 \\in X_1$ and $u_2 \\in X_2$. That is we only ever have edges between nodes from different sets. \n",
+ "\n",
+ "Such graphs are very common in Network Science, where nodes in the network represent two different types of entities. For example, we might have a graph where nodes represent students and modules, with edges between students and modules they are enrolled in (often called an affiliation network)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "Edges2 = [('Aoife', 'CS4423'), ('Aoife', 'CS319'), ('Aoife', 'MA432'), \n",
+ " ('Brian', 'CS4423'), ('Brian', 'CS319'), \n",
+ " ('Ciara', 'CS319'), ('Ciara', 'MA432'), \n",
+ " ('Daire', 'MA432') ]\n",
+ "G2 = nx.Graph(Edges2)\n",
+ "nx.draw(G2, **opts)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "source": [
+ "Somehow that previous graph did not catch the essence of the network: there are two different types of node. We could make that clearer, by colouring the nodes. Here we'll do it manually (later, automatically)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "-"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "print(G2.nodes)\n",
+ "color_list= ['c','y','y','y','c', 'c','c'] # y=yellow; c=cyan\n",
+ "nx.draw(G2, node_color=color_list, with_labels=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Complete Bipartite Graphs\n",
+ "\n",
+ "A **complete bipartite graph** is a particular bipartite graph where there is an edge between every node in $X_1$ and every node in $X_2$. Such graphs are denoted $K_{m,n}$ where $|X_1|=m$ and $|X_2|=n$. (We met $K_{2,2}$ and $K_{3,3}$ earlier). \n",
+ "\n",
+ "As usual, there is a built-in generator: `complete_bipartite_graph` [[doc]](https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.bipartite.generators.complete_bipartite_graph.html)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "K33 = nx.complete_bipartite_graph(3,3)\n",
+ "nx.draw(K33,**opts)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Path Graphs\n",
+ "\n",
+ "The **Path Graph** with $n$ nodes, denoted $P_n$, is one where two nodes have degree 1, and the other $n-2$ have degree 2:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "P4 = nx.Graph([\"ab\", \"bc\", \"cd\", \"de\"])\n",
+ "nx.draw(P4)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "source": [
+ "The built-in `nerworkx`generator is called `path_graph` [[doc]](https://networkx.org/documentation/stable/reference/generated/networkx.generators.classic.path_graph.html)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "P10 = nx.path_graph(10)\n",
+ "nx.draw(P10)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Cycle Graphs\n",
+ "\n",
+ "Our last example: the **cycle** graph, which as a path graph, but with an edge between the two \"end\" nodes. If it has $n$ nodes, we denote it $C_n$. You can make one with `cycle_graph(n)`, but here we'll do it manually.\n",
+ " "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "C5 = nx.Graph(['01', '12', '23', '34', '40'])\n",
+ "nx.draw(C5, **opts) "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "nx.draw(nx.cycle_graph(6))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### Petersen Graph\n",
+ "\n",
+ "The [Petersen Graph](https://en.wikipedia.org/wiki/Petersen_graph)\n",
+ "is a graph on $10$ vertices with $15$ edges.\n",
+ "\n",
+ "It can be constructed \n",
+ "as the complement of the line graph of the complete graph $K_5$,\n",
+ "i.e.,\n",
+ "as the graph with vertex set\n",
+ "$$X = \\binom{\\{0,1,2,3,4\\}}{2}$$ (the edge set of $K_5$) and\n",
+ "with an edge between $x, y \\in X$ whenever $x \\cap y = \\emptyset$."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "nx.draw(K5, **opts)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "lines = K5.edges\n",
+ "print(list(lines))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false
+ },
+ "outputs": [],
+ "source": [
+ "print(list(combinations(lines, 2)))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "edges = [e for e in combinations(lines, 2) \n",
+ " if not set(e[0]) & set(e[1])]\n",
+ "len(edges)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false
+ },
+ "outputs": [],
+ "source": [
+ "P = nx.Graph(edges)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "nx.draw(P, **opts)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "source": [
+ "Even though there is no parameter involved in this example,\n",
+ "it might be worth wrapping the construction up into a `python`\n",
+ "function."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false
+ },
+ "outputs": [],
+ "source": [
+ "def petersen_graph():\n",
+ " nodes = combinations(range(5), 2)\n",
+ " G = nx.Graph()\n",
+ " for e in combinations(nodes, 2):\n",
+ " if not set(e[0]) & set(e[1]):\n",
+ " G.add_edge(*e)\n",
+ " return G"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "nx.draw(petersen_graph(), **opts)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "## Code Corner"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### `python`"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "source": [
+ "* **list unpacking** operator `*e`: if `e` is a list, an\n",
+ "argument `*e` passes the elements of `e` as individual arguments\n",
+ "to a function call."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "source": [
+ "* **dictionary unpacking** operator `**opts`: `python` function calls take **positional** arguments and **keyword** arguments. The keyword arguments can be collected in a dictionary `opts` (with the keywords as keys). This dictionary can then be passed into the function call in its \"unwrapped\" form `**opts`."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "source": [
+ "* **set intersection**: if `a` and `b` are sets then `a & b` represents the intersection of `a` and `b`. In a boolean context, an empty set counts as `False`, and a non-empty set as `True`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false
+ },
+ "outputs": [],
+ "source": [
+ "a = set([1,2,3])\n",
+ "b = set([3,4,5])\n",
+ "a & b"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false
+ },
+ "outputs": [],
+ "source": [
+ "bool({}), bool({3})"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
+ "source": [
+ "* `list` [[doc]](https://docs.python.org/3/library/stdtypes.html#list) turns its argument into a `python` list (if possible)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false
+ },
+ "outputs": [],
+ "source": [
+ "list(\"networks\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false
+ },
+ "source": [
+ "* **list comprehension** [[doc]](https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions) allows the construction of new list from old ones\n",
+ "without explicit `for` loops (or `if` statements)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false
+ },
+ "outputs": [],
+ "source": [
+ "[(x, y) for x in range(4) for y in range(4) if x < y]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "hideCode": false,
+ "hidePrompt": false,
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "## Exercises\n",
+ "\n",
+ "1. For what values of $n$ is $K_n$ bipartite?\n",
+ "2. For what values of $m$ and $n$ is $K_{m,n}$ bipartite?\n",
+ "3. For what values of $n$ is $P_n$ bipartite?\n",
+ "4. For what values of $n$ is $C_n$ bipartite?\n",
+ "5. In this class we looked at a few graph generators in `networkx`. Explore the following: `barbell_graph`, `ladder_graph`, `lollipop_graph`, `star_graph` and `wheel_graph`.\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Finished here Wednesday
"
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Slideshow",
+ "hide_code_all_hidden": false,
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "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.13.1"
+ },
+ "toc": {
+ "base_numbering": 1,
+ "nav_menu": {},
+ "number_sections": true,
+ "sideBar": true,
+ "skip_h1_title": true,
+ "title_cell": "Table of Contents",
+ "title_sidebar": "Contents",
+ "toc_cell": true,
+ "toc_position": {},
+ "toc_section_display": true,
+ "toc_window_display": false
+ },
+ "varInspector": {
+ "cols": {
+ "lenName": 16,
+ "lenType": 16,
+ "lenVar": 40
+ },
+ "kernels_config": {
+ "python": {
+ "delete_cmd_postfix": "",
+ "delete_cmd_prefix": "del ",
+ "library": "var_list.py",
+ "varRefreshCmd": "print(var_dic_list())"
+ },
+ "r": {
+ "delete_cmd_postfix": ") ",
+ "delete_cmd_prefix": "rm(",
+ "library": "var_list.r",
+ "varRefreshCmd": "cat(var_dic_list()) "
+ }
+ },
+ "types_to_exclude": [
+ "module",
+ "function",
+ "builtin_function_or_method",
+ "instance",
+ "_Feature"
+ ],
+ "window_display": false
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}