{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "\n",
    "# Import important modules\n",
    "import scipy # for linear algebra\n",
    "import numpy as np # for linear algebra\n",
    "import networkx as nx # for manipulating graphs\n",
    "import csv # read in csv files\n",
    "import matplotlib  # plotting similar to matlab\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# The documentation of networkx can be found here\n",
    "# https://networkx.github.io/documentation/stable\n",
    "\n",
    "# For a short introduction to networkx see the following \n",
    "# https://networkx.github.io/documentation/stable/tutorial/tutorial.html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise 1 -- TASK 1**\n",
    "\n",
    "Replicate the ’small-world’ experiment numerically. Start with a regular k-nearest neighbors ring-graph, and then keep adding random links to this graph and see how the clustering coefficient and the diameter behaves as a function of the link-probability.\n",
    "\n",
    "Reference:\n",
    "Watts, Duncan J., and Steven H. Strogatz. \"Collective dynamics of'small-world'networks.\" Nature 393.6684 (1998): 440.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "n = 5000\n",
    "k = 5\n",
    "\n",
    "G = nx.newman_watts_strogatz_graph(n,k,0)\n",
    "# compute the (average) clustering coefficient and the average shortest path length\n",
    "# Make use of networkx functions where possible!\n",
    "#C0 = \n",
    "#L0 = \n",
    "\n",
    "for p in np.logspace(-4,-1):\n",
    "    G = nx.newman_watts_strogatz_graph(n,k,p)\n",
    "    # compute the clusterinng coefficient and the average path-length \n",
    "    # [...]\n",
    "    \n",
    "    # and compare it to the value without rewiring\n",
    "    # [...]\n",
    "    \n",
    "\n",
    "# create a plot of your results\n",
    "plt.figure();\n",
    "#[...]\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "** Exercise 1 -- TASK 2 **\n",
    "\n",
    "You have learned about the Barabasi-Albert preferential attachment model in the lectures. Here we will explore it computionally. Use networkx to construct these graph for varying parameters and report on the result you obtain."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# let's set up our parameters\n",
    "n = 10000 # network size\n",
    "m = 30 # intitial network\n",
    "G = nx.barabasi_albert_graph(n,m)\n",
    "\n",
    "# plot the degree distribution \n",
    "\n",
    "# now vary the parameter m and set it to 50, 100, 200 -- do you see a change?\n",
    "# Plot your results\n",
    "\n",
    "# vary the parameter n and change it to 15000, 20000 -- do you observe a change?\n",
    "# Plot your results"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "** Exercise 1 -- TASK 3 **\n",
    "\n",
    "In this exercise you are going to explore another random graph model, the so-called stochastic blockmodel. We will consider a version of the model defined as follows.\n",
    "We consider an undirected network with n nodes, which are divided into 2 equal sized groups which we name class 1 and class 2. The probability of a link between two nodes is now given by p if the nodes are in the same class, and by q if the nodes are in two different classes.\n",
    "\n",
    "Your task is to create graphs with varying parameters and use spectral clustering to see whether you can recover the blocks. \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def permute_array(A,permutation):\n",
    "    A = A[permutation,:]\n",
    "    A = A[:,permutation]\n",
    "    return A\n",
    "\n",
    "def compare_clustering(true_clustering, inferred_clustering):\n",
    "    nr_nodes = float(true_clustering.shape[0])\n",
    "    overlap = np.sum(true_clustering == inferred_clustering)/nr_nodes\n",
    "    overlap = max(overlap,1-overlap)\n",
    "    return overlap\n",
    "\n",
    "# this function allows you to sample from a stochastic blockmodel with varying parameters\n",
    "def create_block_model_graph(nr_nodes=2000, p=0.01, q=0.001):\n",
    "    # build the graph from sparse matrices\n",
    "    A1 =  np.triu(p > np.random.rand(nr_nodes/2,nr_nodes/2),1)\n",
    "    A1 =  A1 + A1.T\n",
    "    \n",
    "    A2 =  np.triu(p > np.random.rand(nr_nodes/2,nr_nodes/2),1)\n",
    "    A2 =  A2 + A2.T\n",
    "    \n",
    "    A3 =  q > np.random.rand(nr_nodes/2,nr_nodes/2)\n",
    "    \n",
    "    # final adjacency matrix\n",
    "    A = 1*np.hstack([np.vstack([A1,A3]),np.vstack([A3.T,A2])])\n",
    "    \n",
    "    # original block variables\n",
    "    true_blocks = np.kron(np.arange(2),np.ones(nr_nodes/2))\n",
    "    \n",
    "    # we disguise the true block labels by shuffling!\n",
    "    permute = np.random.permutation(nr_nodes)\n",
    "    true_blocks = true_blocks[permute]\n",
    "    A = permute_array(A,permute)\n",
    "    \n",
    "    # create the graph\n",
    "    G = nx.from_numpy_matrix(A,create_using=nx.Graph())\n",
    "    \n",
    "    return G, true_blocks\n",
    "\n",
    "#To get a feel for how the blockmodel graph looks like, lets visualize it\n",
    "G, true_blocks = create_block_model_graph()\n",
    "\n",
    "# first lets have a look at the adjacency matrix where the nodes are shuffled\n",
    "# and not ordered according to blocks\n",
    "plt.figure()\n",
    "A = nx.adjacency_matrix(G)\n",
    "plt.spy(A,markersize=0.1);\n",
    "\n",
    "# and now lets look at the re-ordered version -- note that this is the same graph!\n",
    "plt.figure()\n",
    "permute2 = np.argsort(true_blocks)\n",
    "A2 = permute_array(A,permute2)\n",
    "plt.spy(A2,markersize=0.1);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# Let's see if we can recover the blocks using spectral clustering\n",
    "\n",
    "# fix p of probability inside blocks\n",
    "p = 0.01\n",
    "n = 2000\n",
    "nr_samples = 10\n",
    "\n",
    "# vary q\n",
    "for q in np.logspace(-4,-2):\n",
    "    for s in range(nr_samples):\n",
    "        G, true_blocks = create_block_model_graph(nr_nodes=n,p=p,q=q)\n",
    "        # use spectral clustering to infer the blocks -- have a look at your previous \n",
    "        # homework!\n",
    "        # [...]\n",
    "        # record the accuracy of your inference over the specified number of samples\n",
    "        # i.e. compute the absolute error b/w your spectral cluster labelling and the true labels given in true_blocks\n",
    "        # [...]\n",
    "\n",
    "        \n",
    "# plot your results using an errorbar plot with mean +- one standard deviation\n",
    "        "
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.12"
  },
  "latex_envs": {
   "bibliofile": "biblio.bib",
   "cite_by": "apalike",
   "current_citInitial": 1,
   "eqLabelWithNumbers": true,
   "eqNumInitial": 0
  },
  "nav_menu": {},
  "toc": {
   "navigate_menu": true,
   "number_sections": true,
   "sideBar": true,
   "threshold": 6,
   "toc_cell": false,
   "toc_section_display": "block",
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
