{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "toc": true
   },
   "source": [
    "<h1>Table des matières<span class=\"tocSkip\"></span></h1>\n",
    "<div class=\"toc\"><ul class=\"toc-item\"><li><span><a href=\"#PageRank\" data-toc-modified-id=\"PageRank-1\"><span class=\"toc-item-num\">1&nbsp;&nbsp;</span>PageRank</a></span><ul class=\"toc-item\"><li><span><a href=\"#Part-1---Worksheet\" data-toc-modified-id=\"Part-1---Worksheet-1.1\"><span class=\"toc-item-num\">1.1&nbsp;&nbsp;</span>Partie 1 - Feuille de travail</a></span><ul class=\"toc-item\"><li><span><a href=\"#Introduction\" data-toc-modified-id=\"Introduction-1.1.1\"><span class=\"toc-item-num\">1.1.1&nbsp;&nbsp;</span>Introduction</a></span></li><li><span><a href=\"#PageRank-as-a-linear-algebra-problem\" data-toc-modified-id=\"PageRank-as-a-linear-algebra-problem-1.1.2\"><span class=\"toc-item-num\">1.1.2&nbsp;&nbsp;</span>PageRank comme problème d'algèbre linéaire</a></span></li><li><span><a href=\"#Damping-Parameter\" data-toc-modified-id=\"Damping-Parameter-1.1.3\"><span class=\"toc-item-num\">1.1.3&nbsp;&nbsp;</span>Paramètre d'amortissement</a></span></li></ul></li><li><span><a href=\"#Part-2---Assessment\" data-toc-modified-id=\"Part-2---Assessment-1.2\"><span class=\"toc-item-num\">1.2&nbsp;&nbsp;</span>Partie 2 - Exercice</a></span><ul class=\"toc-item\"><li><span><a href=\"#How-to-submit\" data-toc-modified-id=\"How-to-submit-1.2.1\"><span class=\"toc-item-num\">1.2.1&nbsp;&nbsp;</span>Comment soumettre</a></span></li></ul></li><li><span><a href=\"#Test-your-code-before-submission\" data-toc-modified-id=\"Test-your-code-before-submission-1.3\"><span class=\"toc-item-num\">1.3&nbsp;&nbsp;</span>Tester votre code avant soumission</a></span></li></ul></li></ul></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Instructions\n",
    "\n",
    "- Plusieurs cellules contiennent volontairement des placeholders `???`, des TODOs et des `NotImplementedError`.\n",
    "- Votre objectif est de compléter ces parties: construire les matrices `L` et `L2`, implémenter `pageRank`, et appliquer l'itération de puissance (avec et sans amortissement).\n",
    "- Utilisez votre fonction `power_iteration` pour calculer le vecteur propre dominant, y compris dans `pageRank` sur la matrice amortie `M`.\n",
    "- Ne décommentez les sections de visualisation qu'après avoir implémenté et testé vos fonctions.\n",
    "- Vérifiez que vos matrices sont colonne-stochastiques (chaque colonne somme à 1) et traitez les nœuds pendants.\n",
    "- Paramètre d'amortissement recommandé: `d ≈ 0.85`.\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "source": [
    "# PageRank\n",
    "Dans ce notebook, vous approfondirez vos connaissances des vecteurs propres et des valeurs propres en explorant l'algorithme PageRank.\n",
    "\n",
    "Le notebook est divisé en deux parties : la première est une feuille de travail pour vous familiariser avec le fonctionnement de l'algorithme - ici, nous examinerons un micro-internet avec moins de 10 sites Web et verrons ce qu'il fait et ce qui peut mal se passer.\n",
    "\n",
    "La deuxième est une évaluation qui testera votre application de la théorie des valeurs propres à ce problème et en calculant le PageRank d'un grand réseau représentant une sous-section de l'internet"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Partie 1\n",
    "### Introduction\n",
    "\n",
    "PageRank (développé par Larry Page et Sergey Brin) a révolutionné la recherche sur le Web en générant une liste classée de pages Web basée sur la connectivité sous-jacente du Web. L'algorithme PageRank est basé sur un surfeur Web idéal qui, lorsqu'il atteint une page, se rend sur la page suivante en cliquant sur un lien. Le surfeur a la même probabilité de cliquer sur n'importe quel lien de la page et, lorsqu'il atteint une page sans lien, a la même probabilité de se rendre sur n'importe quelle autre page en tapant son URL. De plus, le surfeur peut occasionnellement choisir de taper une URL aléatoire au lieu de suivre les liens d'une page. \n",
    "\n",
    "Le PageRank est l'ordre classé des pages, de la page la plus probable à la moins probable que le surfeur visualisera."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Chargement des bibliothèques nécessaires\n",
    "%matplotlib notebook\n",
    "import numpy as np\n",
    "import numpy.linalg as la\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Génération d'un graphe\n",
    "def generate_internet(n) :\n",
    "    c = np.full([n,n], np.arange(n))\n",
    "    c = (abs(np.random.standard_cauchy([n,n])/2) > (np.abs(c - c.T) + 1)) + 0\n",
    "    c = (c+1e-10) / np.sum((c+1e-10), axis=0)\n",
    "    return c\n",
    "    \n",
    "np.set_printoptions(suppress=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### PageRank en tant que problème d'algèbre linéaire\n",
    "Imaginons un micro-internet, avec seulement 6 sites Web (**A**vocado, **B**ullseye, **C**atBabel, **D**romeda, **e**Tings et **F**aceSpace).\n",
    "\n",
    "Chaque site Web est lié à certains des autres, ce qui forme un réseau comme indiqué,\n",
    "\n",
    "![Image du micro-internet](./dOXTNcTHBZIsmBeE.png)\n",
    "\n",
    "Le principe de conception de PageRank est que les sites Web importants seront liés par des sites Web importants.\n",
    "\n",
    "Ce principe quelque peu récursif formera la base de notre réflexion.\n",
    "\n",
    "Imaginez que nous avons 100 *utilisateurs* sur notre micro-internet, chacun visualisant un seul site Web à la fois.\n",
    "\n",
    "Chaque minute, les utilisateurs suivent un lien sur leur site Web vers un autre site sur le micro-internet.\n",
    "\n",
    "Après un certain temps, les sites Web qui sont les plus liés auront plus de utilisateurs qui les visitent, et à long terme, pour chaque utilisateur qui quitte un site Web, un autre entrera, gardant le nombre total de utilisateurs sur chaque site Web constant.\n",
    "\n",
    "Le PageRank est simplement le classement des sites Web en fonction du nombre de utilisateurs qu'ils ont sur eux à la fin de ce processus.\n",
    "\n",
    "Nous représentons le nombre de utilisateurs sur chaque site Web avec le vecteur,\n",
    "$$\\mathbf{r} = \\begin{bmatrix} r_A \\\\ r_B \\\\ r_C \\\\ r_D \\\\ r_E \\\\ r_F \\end{bmatrix}$$\n",
    "\n",
    "Et disons que le nombre de utilisateurs sur chaque site Web à la minute $i+1$ est lié à ceux à la minute $i$ par la transformation matricielle\n",
    "\n",
    "$$ \\mathbf{r}^{(i+1)} = L \\,\\mathbf{r}^{(i)}$$\n",
    "avec la matrice $L$ de la forme suivante,\n",
    "$$ L = \\begin{bmatrix}\n",
    "L_{A→A} & L_{B→A} & L_{C→A} & L_{D→A} & L_{E→A} & L_{F→A} \\\\\n",
    "L_{A→B} & L_{B→B} & L_{C→B} & L_{D→B} & L_{E→B} & L_{F→B} \\\\\n",
    "L_{A→C} & L_{B→C} & L_{C→C} & L_{D→C} & L_{E→C} & L_{F→C} \\\\\n",
    "L_{A→D} & L_{B→D} & L_{C→D} & L_{D→D} & L_{E→D} & L_{F→D} \\\\\n",
    "L_{A→E} & L_{B→E} & L_{C→E} & L_{D→E} & L_{E→E} & L_{F→E} \\\\\n",
    "L_{A→F} & L_{B→F} & L_{C→F} & L_{D→F} & L_{E→F} & L_{F→F} \\\\\n",
    "\\end{bmatrix}\n",
    "$$\n",
    "\n",
    "où les colonnes représentent la probabilité de quitter un site Web pour n'importe quel autre site Web et somment à un.\n",
    "\n",
    "Les lignes déterminent la probabilité que vous avez d'entrer sur un site Web à partir de n'importe quel autre, bien que celles-ci n'aient pas besoin de s'additionner à un.\n",
    "\n",
    "Le comportement à long terme de ce système est donné par $ \\mathbf{r}^{(i+1)} = \\mathbf{r}^{(i)}$. En supprimant les exposants nous pouvons écrire,\n",
    "$$ L \\,\\mathbf{r} = \\mathbf{r}$$\n",
    "\n",
    "qui est une équation de valeur propre pour la matrice $L$, avec la valeur propre 1 (ceci est garanti par la structure probabiliste de la matrice $L$).\n",
    "\n",
    "Complétez la matrice $L$ ci-dessous.\n",
    "\n",
    "N'oubliez pas que c'est la probabilité de cliquer sur un autre site Web à partir de celui-ci, donc chaque colonne doit s'additionner à un (en mettant à l'échelle par le nombre de liens)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "invalid syntax (1927761544.py, line 2)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;36m  Cell \u001b[0;32mIn[15], line 2\u001b[0;36m\u001b[0m\n\u001b[0;31m    L = np.array([[???, ???, ???, ???, ???, ???],\u001b[0m\n\u001b[0m                   ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n"
     ]
    }
   ],
   "source": [
    "# Remplacer les ??? par la probabilité de cliquer sur un lien pour chaque site.\n",
    "# Chaque colonne correspond aux probabilités de quitter un site vers d'autres sites et doit sommer à 1.\n",
    "# Laissez des placeholders (???) et complétez-les lors de l'exercice.\n",
    "L = np.array([[???, ???, ???, ???, ???, ???],\n",
    "              [???, ???, ???, ???, ???, ???],\n",
    "              [???, ???, ???, ???, ???, ???],\n",
    "              [???, ???, ???, ???, ???, ???],\n",
    "              [???, ???, ???, ???, ???, ???],\n",
    "              [???, ???, ???, ???, ???, ???]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En principe, nous pourrions utiliser une bibliothèque d'algèbre linéaire, comme ci-dessous, pour calculer les valeurs propres et les vecteurs.\n",
    "\n",
    "Et cela fonctionnerait pour un petit système. Mais cela devient ingérable pour les grands systèmes.\n",
    "\n",
    "Et puisque nous ne nous soucions que du vecteur propre principal (celui avec la plus grande valeur propre, qui sera 1 dans ce cas), nous pouvons utiliser la *méthode de la puissance itérée* qui évoluera mieux et sera plus rapide pour les grands systèmes.\n",
    "\n",
    "Utilisez le code ci-dessous pour jeter un œil au PageRank de ce micro-internet."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "eVals, eVecs = la.eig(L) # Obtention des valeurs et vecteurs propres\n",
    "order = np.absolute(eVals).argsort()[::-1] # Tri par grandeur des valeurs propres\n",
    "eVals = eVals[order]\n",
    "eVecs = eVecs[:,order]\n",
    "\n",
    "r = eVecs[:, 0] # Vecteur propre principal\n",
    "100 * np.real(r / np.sum(r)) # Normaliser pour sommer à 100"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Nous pouvons voir à partir de cette liste, le nombre de utilisations Procrastinateurs que nous nous attendons à trouver sur chaque site Web après un long temps.\n",
    "\n",
    "En les mettant dans l'ordre de *popularité* (basé sur cette métrique), le PageRank de ce micro-internet est :\n",
    "\n",
    "**C**, **D**, **A**, **F**, **B**, **E**\n",
    "\n",
    "En se référant au diagramme du micro-internet, est-ce ce à quoi vous vous attendiez ?\n",
    "\n",
    "Convaincez-vous que, en fonction des pages qui semblent importantes compte tenu des autres qui y sont liées, il s'agit d'un classement raisonnable.\n",
    "\n",
    "Essayons maintenant d'obtenir le même résultat en utilisant la méthode de la puissance itérée.\n",
    "\n",
    "Cette méthode sera bien meilleure pour gérer les grands systèmes.\n",
    "\n",
    "Tout d'abord, définissons notre vecteur initial, $\\mathbf{r}^{(0)}$, afin que nous ayons les 100 utilisateurs répartis uniformément sur chacun des 6 sites Web."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "r = 100 * np.ones(6) / 6 # Répartition uniforme des utilisateurs\n",
    "r # Affiche la valeur de r"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ensuite, mettons à jour le vecteur à la minute suivante, avec la matrice $L$.\n",
    "Exécutez la cellule suivante plusieurs fois, jusqu'à ce que la réponse se stabilise."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "r = L @ r # Multiplication de la r par la matrice d'itération\n",
    "r # Affiche la valeur de r\n",
    "# Rexécuter cette fonction plusieurs fois"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Nous pouvons automatiser l'application de cette matrice plusieurs fois comme suit,"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 16.        ,   5.33333333,  40.        ,  25.33333333,\n",
       "         0.        ,  13.33333333])"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "r = 100 * np.ones(6) / 6 # Vecteur initial\n",
    "for i in np.arange(100) : # Répéter 100 fois\n",
    "    r = L @ r\n",
    "r"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ou mieux encore, nous pouvons continuer à exécuter jusqu'à ce que nous atteignions la tolérance requise."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "18 iterations to convergence.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([ 16.00149917,   5.33252025,  39.99916911,  25.3324738 ,\n",
       "         0.        ,  13.33433767])"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "r = 100 * np.ones(6) / 6 # Initialise ce vecteur (6 entrées de 1/6 × 100 chacune)\n",
    "lastR = r\n",
    "r = L @ r\n",
    "i = 0\n",
    "while la.norm(lastR - r) > 0.01 :\n",
    "    lastR = r\n",
    "    r = L @ r\n",
    "    i += 1\n",
    "print(str(i) + \" iterations pour converger.\")\n",
    "r"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Paramètre d'amortissement\n",
    "\n",
    "Le système que nous venons d'étudier a convergé assez rapidement vers la bonne réponse.\n",
    "\n",
    "Considérons une extension de notre micro-internet où les choses commencent à mal tourner.\n",
    "Disons qu'un nouveau site Web est ajouté au micro-internet : le site Web de *G*.\n",
    "\n",
    "Ce site Web est lié par *G* et ne relie que lui-même.\n",
    "\n",
    "![Image du micro-internet](https://cloud.univ-grenoble-alpes.fr/s/FBXeq6tDpoQSdT4)\n",
    "\n",
    "Intuitivement, seul *G*, qui se trouve dans la moitié inférieure du classement des pages, est lié à ce site Web parmi les deux autres auxquels il est lié, nous pouvons donc nous attendre à ce que le site de *G* ait un score PageRank correspondant faible.\n",
    "\n",
    "Construisez la nouvelle matrice $L$ pour le micro-internet étendu et utilisez Power-Iteration sur le vecteur utilisations Procrastinateurs.\n",
    "\n",
    "Voyez ce qui se passe..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [],
   "source": [
    "# TODO: Construire la matrice L2 (7x7) pour le micro-internet étendu (incluant G)\n",
    "# Chaque colonne doit sommer à 1 (matrice colonne-stochastique). Remplacez les ???\n",
    "L2 = np.array([\n",
    "    [???, ???, ???, ???, ???, ???, ???],\n",
    "    [???, ???, ???, ???, ???, ???, ???],\n",
    "    [???, ???, ???, ???, ???, ???, ???],\n",
    "    [???, ???, ???, ???, ???, ???, ???],\n",
    "    [???, ???, ???, ???, ???, ???, ???],\n",
    "    [???, ???, ???, ???, ???, ???, ???],\n",
    "    [???, ???, ???, ???, ???, ???, ???],\n",
    "])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "131 iterations to convergence.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([  0.03046998,   0.01064323,   0.07126612,   0.04423198,\n",
       "         0.        ,   0.02489342,  99.81849527])"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# TODO: Utiliser la méthode de la puissance itérée pour estimer le PageRank avec L2 (sans amortissement)\n",
    "# Indice: initialiser r uniformément, puis répéter r = L2 @ r jusqu'à convergence\n",
    "raise NotImplementedError(\"Complétez l'itération de puissance pour L2.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ce n'est pas bon ! *G* semble prendre tout le trafic sur le micro-internet et, d'une manière ou d'une autre, arrive en tête du PageRank.\n",
    "\n",
    "Ce comportement peut être compris, car une fois qu'un Pat arrive sur le site Web de *G*, il ne peut pas partir, car tous les liens retournent vers Geoff.\n",
    "\n",
    "Pour lutter contre cela, nous pouvons ajouter une petite probabilité que les utilisations ne suivent aucun lien sur une page Web, mais visitent plutôt un site Web sur le micro-internet au hasard.\n",
    "\n",
    "Nous dirons que la probabilité qu'ils suivent un lien est $d$ et la probabilité de choisir un site Web aléatoire est donc $1-d$.\n",
    "\n",
    "Nous pouvons utiliser une nouvelle matrice pour déterminer où les utilisations visitent chaque minute.\n",
    "\n",
    "$$ M = d \\, L + \\frac{1-d}{n} \\, J $$\n",
    "où $J$ est une matrice $n\\times n$ où chaque élément est un.\n",
    "\n",
    "Si $d$ est égal à un, nous avons le cas que nous avions précédemment, alors que si $d$ est égal à zéro, nous visiterons toujours une page Web aléatoire et par conséquent toutes les pages Web seront également probables et également classées.\n",
    "\n",
    "Pour que cette extension fonctionne au mieux, $1-d$ doit être quelque peu petit - bien que nous n'entrerons pas dans une discussion sur la taille exacte.\n",
    "\n",
    "Réessayons ce PageRank avec cette extension.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [],
   "source": [
    "# TODO: Construire la matrice amortie M = d * L2 + (1-d)/n * J\n",
    "# Remplacer ??? par le choix de d (par ex. 0.85) et n = 7\n",
    "# J est une matrice de 1 de taille n x n\n",
    "# d = ???\n",
    "# n = 7\n",
    "# M = d * L2 + (1 - d) / n * np.ones((n, n))\n",
    "raise NotImplementedError(\"Construisez M avec amortissement.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "8 iterations to convergence.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([ 13.68217054,  11.20902965,  22.41964343,  16.7593433 ,\n",
       "         7.14285714,  10.87976354,  17.90719239])"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# TODO: Appliquer l'itération de puissance avec la matrice amortie M\n",
    "# Indice: initialiser r uniformément, puis répéter r = M @ r jusqu'à convergence\n",
    "raise NotImplementedError(\"Itération de puissance avec amortissement.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "C'est certainement mieux, le PageRank donne des nombres raisonnables pour les utilisations Procrastinateurs qui finissent sur chaque page Web.\n",
    "\n",
    "Cependant, cette méthode prédit toujours que Geoff a une page Web de haut rang.\n",
    "\n",
    "Cela pourrait être considéré comme une conséquence de l'utilisation d'un petit réseau. Nous pourrions également contourner le problème en ne comptant pas les auto-liens lors de la production de la matrice L (et si un site Web n'a pas de liens sortants, faites-le lier à tous les sites Web de manière égale).\n",
    "\n",
    "Nous n'irons pas plus loin dans cette voie, car cela relève plutôt du domaine des améliorations de PageRank que des problèmes de valeurs propres.\n",
    "\n",
    "Vous êtes maintenant dans une bonne position, ayant acquis une compréhension de PageRank, pour produire votre propre code afin de calculer le PageRank d'un site Web avec des milliers d'entrées.\n",
    "\n",
    "Bonne chance!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Partie 2 - Exercice\n",
    "\n",
    "Il vous est demandé de produire une fonction capable de calculer le PageRank pour une matrice de probabilité arbitrairement grande\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def pageRank(linkMatrix, d=0.85, tol=1e-4, max_iter=10000):\n",
    "    \"\"\"Calcule le PageRank pour une matrice de liens.\n",
    "    TODO: Implémentez cette fonction (damping, gestion des nœuds pendants, itération de puissance).\n",
    "    Utilisez votre fonction power_iteration sur la matrice amortie M.\n",
    "    Paramètres\n",
    "    - linkMatrix: matrice colonne-stochastique (ou presque) des liens\n",
    "    - d: paramètre d'amortissement (0<d<1)\n",
    "    - tol: tolérance de convergence\n",
    "    - max_iter: itérations max\n",
    "    Retourne\n",
    "    - r: vecteur PageRank (somme à 100)\n",
    "    \"\"\"\n",
    "    # TODO: 1) Convertir en np.array et vérifier la matrice carrée\n",
    "    # TODO: 2) Gérer les colonnes pendantes (sommes ~0) en redistribuant uniformément\n",
    "    # TODO: 3) Normaliser les colonnes pour garantir la stochasticité\n",
    "    # TODO: 4) Construire la matrice amortie M = d*L + (1-d)/n * J\n",
    "    # TODO: 5) Appeler power_iteration(M, tol, max_iter) et retourner 100 * r\n",
    "    raise NotImplementedError(\"Complétez pageRank en réutilisant power_iteration.\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tester votre code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0.2, 0.2, 0. , 1. , 0. ],\n",
       "       [0.2, 0.2, 0. , 0. , 0. ],\n",
       "       [0.2, 0.2, 0. , 0. , 0. ],\n",
       "       [0.2, 0.2, 0. , 0. , 1. ],\n",
       "       [0.2, 0.2, 1. , 0. , 0. ]])"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Générer des réseaux de tailles différentes\n",
    "generate_internet(5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Comparer votre algorithme à celui donné par défaut pour des réseaux plus grands\n",
    "L = generate_internet(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m                         Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[27], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m pageRank(L, \u001b[38;5;241m1\u001b[39m)\n",
      "Cell \u001b[0;32mIn[24], line 14\u001b[0m, in \u001b[0;36mpageRank\u001b[0;34m(linkMatrix, d)\u001b[0m\n\u001b[1;32m     11\u001b[0m lastR \u001b[38;5;241m=\u001b[39m r\n\u001b[1;32m     12\u001b[0m r \u001b[38;5;241m=\u001b[39m M \u001b[38;5;241m@\u001b[39m r\n\u001b[0;32m---> 14\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m la\u001b[38;5;241m.\u001b[39mnorm(lastR \u001b[38;5;241m-\u001b[39m r) \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m0.01\u001b[39m:\n\u001b[1;32m     15\u001b[0m     lastR \u001b[38;5;241m=\u001b[39m r\n\u001b[1;32m     16\u001b[0m     r \u001b[38;5;241m=\u001b[39m M \u001b[38;5;241m@\u001b[39m r\n",
      "File \u001b[0;32m/opt/homebrew/Caskroom/miniconda/base/lib/python3.11/site-packages/numpy/linalg/linalg.py:2379\u001b[0m, in \u001b[0;36m_norm_dispatcher\u001b[0;34m(x, ord, axis, keepdims)\u001b[0m\n\u001b[1;32m   2375\u001b[0m     result \u001b[38;5;241m=\u001b[39m op(svd(y, compute_uv\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mFalse\u001b[39;00m), axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m   2376\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m result\n\u001b[0;32m-> 2379\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_norm_dispatcher\u001b[39m(x, \u001b[38;5;28mord\u001b[39m\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, keepdims\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[1;32m   2380\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m (x,)\n\u001b[1;32m   2383\u001b[0m \u001b[38;5;129m@array_function_dispatch\u001b[39m(_norm_dispatcher)\n\u001b[1;32m   2384\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mnorm\u001b[39m(x, \u001b[38;5;28mord\u001b[39m\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, keepdims\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mFalse\u001b[39;00m):\n",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
     ]
    }
   ],
   "source": [
    "pageRank(L, 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# DÉMO GUIDÉE: valeurs propres et vecteur propre principal (sans amortissement)\n",
    "# TODO: 1) Calculer eVals, eVecs = la.eig(L)\n",
    "# TODO: 2) Ordonner par grandeur de valeur propre\n",
    "# TODO: 3) Extraire le vecteur propre principal et le normaliser pour sommer à 100\n",
    "# Indice: utilisez np.absolute(eVals).argsort()[::-1]\n",
    "raise NotImplementedError(\"Complétez la démonstration des valeurs propres.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Représentation graphique du PageRank (gabarit)\n",
    "%matplotlib notebook\n",
    "# TODO: Décommentez après avoir implémenté pageRank et testé sur une matrice valide\n",
    "# r = pageRank(generate_internet(100), 0.9)\n",
    "# plt.bar(np.arange(r.shape[0]), r)\n",
    "# plt.xlabel(\"Nœuds\")\n",
    "# plt.ylabel(\"PageRank (somme = 100)\")\n",
    "# plt.title(\"Distribution du PageRank\")"
   ]
  }
 ],
 "metadata": {
  "coursera": {
   "course_slug": "linear-algebra-machine-learning",
   "graded_item_id": "Sfbnp",
   "launcher_item_id": "aPxf3"
  },
  "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.11.3"
  },
  "latex_envs": {
   "LaTeX_envs_menu_present": true,
   "autoclose": true,
   "autocomplete": false,
   "bibliofile": "biblio.bib",
   "cite_by": "apalike",
   "current_citInitial": 1,
   "eqLabelWithNumbers": true,
   "eqNumInitial": 1,
   "hotkeys": {
    "equation": "Ctrl-E",
    "itemize": "Ctrl-I"
   },
   "labels_anchors": false,
   "latex_user_defs": false,
   "report_style_numbering": true,
   "user_envs_cfg": false
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": true,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
