├── .gitignore ├── img ├── FF-ANN.png ├── LeNet-5.png ├── perceptron.png ├── inception_module.png └── convolutional_module.png ├── cache └── 03.SimpleDense.MNIST.h5 ├── extras └── Reti Neurali - MFN0824 - Extras.pdf ├── datasets └── iris.csv ├── README.md ├── 05 - Gridworld with Value Iteration.ipynb ├── 06 - Gridworld with Q-learning.ipynb └── A - Python Basics Review.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | #ignore .ipynb_checkpoints folder 2 | /.ipynb_checkpoints/* 3 | -------------------------------------------------------------------------------- /img/FF-ANN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmottes/unito-neural-networks/HEAD/img/FF-ANN.png -------------------------------------------------------------------------------- /img/LeNet-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmottes/unito-neural-networks/HEAD/img/LeNet-5.png -------------------------------------------------------------------------------- /img/perceptron.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmottes/unito-neural-networks/HEAD/img/perceptron.png -------------------------------------------------------------------------------- /img/inception_module.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmottes/unito-neural-networks/HEAD/img/inception_module.png -------------------------------------------------------------------------------- /cache/03.SimpleDense.MNIST.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmottes/unito-neural-networks/HEAD/cache/03.SimpleDense.MNIST.h5 -------------------------------------------------------------------------------- /img/convolutional_module.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmottes/unito-neural-networks/HEAD/img/convolutional_module.png -------------------------------------------------------------------------------- /extras/Reti Neurali - MFN0824 - Extras.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmottes/unito-neural-networks/HEAD/extras/Reti Neurali - MFN0824 - Extras.pdf -------------------------------------------------------------------------------- /datasets/iris.csv: -------------------------------------------------------------------------------- 1 | sepal length (cm), sepal width (cm), petal length (cm), petal width (cm), target 2 | 5.1,3.5,1.4,0.2,0 3 | 4.9,3.0,1.4,0.2,0 4 | 4.7,3.2,1.3,0.2,0 5 | 4.6,3.1,1.5,0.2,0 6 | 5.0,3.6,1.4,0.2,0 7 | 5.4,3.9,1.7,0.4,0 8 | 4.6,3.4,1.4,0.3,0 9 | 5.0,3.4,1.5,0.2,0 10 | 4.4,2.9,1.4,0.2,0 11 | 4.9,3.1,1.5,0.1,0 12 | 5.4,3.7,1.5,0.2,0 13 | 4.8,3.4,1.6,0.2,0 14 | 4.8,3.0,1.4,0.1,0 15 | 4.3,3.0,1.1,0.1,0 16 | 5.8,4.0,1.2,0.2,0 17 | 5.7,4.4,1.5,0.4,0 18 | 5.4,3.9,1.3,0.4,0 19 | 5.1,3.5,1.4,0.3,0 20 | 5.7,3.8,1.7,0.3,0 21 | 5.1,3.8,1.5,0.3,0 22 | 5.4,3.4,1.7,0.2,0 23 | 5.1,3.7,1.5,0.4,0 24 | 4.6,3.6,1.0,0.2,0 25 | 5.1,3.3,1.7,0.5,0 26 | 4.8,3.4,1.9,0.2,0 27 | 5.0,3.0,1.6,0.2,0 28 | 5.0,3.4,1.6,0.4,0 29 | 5.2,3.5,1.5,0.2,0 30 | 5.2,3.4,1.4,0.2,0 31 | 4.7,3.2,1.6,0.2,0 32 | 4.8,3.1,1.6,0.2,0 33 | 5.4,3.4,1.5,0.4,0 34 | 5.2,4.1,1.5,0.1,0 35 | 5.5,4.2,1.4,0.2,0 36 | 4.9,3.1,1.5,0.2,0 37 | 5.0,3.2,1.2,0.2,0 38 | 5.5,3.5,1.3,0.2,0 39 | 4.9,3.6,1.4,0.1,0 40 | 4.4,3.0,1.3,0.2,0 41 | 5.1,3.4,1.5,0.2,0 42 | 5.0,3.5,1.3,0.3,0 43 | 4.5,2.3,1.3,0.3,0 44 | 4.4,3.2,1.3,0.2,0 45 | 5.0,3.5,1.6,0.6,0 46 | 5.1,3.8,1.9,0.4,0 47 | 4.8,3.0,1.4,0.3,0 48 | 5.1,3.8,1.6,0.2,0 49 | 4.6,3.2,1.4,0.2,0 50 | 5.3,3.7,1.5,0.2,0 51 | 5.0,3.3,1.4,0.2,0 52 | 7.0,3.2,4.7,1.4,1 53 | 6.4,3.2,4.5,1.5,1 54 | 6.9,3.1,4.9,1.5,1 55 | 5.5,2.3,4.0,1.3,1 56 | 6.5,2.8,4.6,1.5,1 57 | 5.7,2.8,4.5,1.3,1 58 | 6.3,3.3,4.7,1.6,1 59 | 4.9,2.4,3.3,1.0,1 60 | 6.6,2.9,4.6,1.3,1 61 | 5.2,2.7,3.9,1.4,1 62 | 5.0,2.0,3.5,1.0,1 63 | 5.9,3.0,4.2,1.5,1 64 | 6.0,2.2,4.0,1.0,1 65 | 6.1,2.9,4.7,1.4,1 66 | 5.6,2.9,3.6,1.3,1 67 | 6.7,3.1,4.4,1.4,1 68 | 5.6,3.0,4.5,1.5,1 69 | 5.8,2.7,4.1,1.0,1 70 | 6.2,2.2,4.5,1.5,1 71 | 5.6,2.5,3.9,1.1,1 72 | 5.9,3.2,4.8,1.8,1 73 | 6.1,2.8,4.0,1.3,1 74 | 6.3,2.5,4.9,1.5,1 75 | 6.1,2.8,4.7,1.2,1 76 | 6.4,2.9,4.3,1.3,1 77 | 6.6,3.0,4.4,1.4,1 78 | 6.8,2.8,4.8,1.4,1 79 | 6.7,3.0,5.0,1.7,1 80 | 6.0,2.9,4.5,1.5,1 81 | 5.7,2.6,3.5,1.0,1 82 | 5.5,2.4,3.8,1.1,1 83 | 5.5,2.4,3.7,1.0,1 84 | 5.8,2.7,3.9,1.2,1 85 | 6.0,2.7,5.1,1.6,1 86 | 5.4,3.0,4.5,1.5,1 87 | 6.0,3.4,4.5,1.6,1 88 | 6.7,3.1,4.7,1.5,1 89 | 6.3,2.3,4.4,1.3,1 90 | 5.6,3.0,4.1,1.3,1 91 | 5.5,2.5,4.0,1.3,1 92 | 5.5,2.6,4.4,1.2,1 93 | 6.1,3.0,4.6,1.4,1 94 | 5.8,2.6,4.0,1.2,1 95 | 5.0,2.3,3.3,1.0,1 96 | 5.6,2.7,4.2,1.3,1 97 | 5.7,3.0,4.2,1.2,1 98 | 5.7,2.9,4.2,1.3,1 99 | 6.2,2.9,4.3,1.3,1 100 | 5.1,2.5,3.0,1.1,1 101 | 5.7,2.8,4.1,1.3,1 102 | 6.3,3.3,6.0,2.5,2 103 | 5.8,2.7,5.1,1.9,2 104 | 7.1,3.0,5.9,2.1,2 105 | 6.3,2.9,5.6,1.8,2 106 | 6.5,3.0,5.8,2.2,2 107 | 7.6,3.0,6.6,2.1,2 108 | 4.9,2.5,4.5,1.7,2 109 | 7.3,2.9,6.3,1.8,2 110 | 6.7,2.5,5.8,1.8,2 111 | 7.2,3.6,6.1,2.5,2 112 | 6.5,3.2,5.1,2.0,2 113 | 6.4,2.7,5.3,1.9,2 114 | 6.8,3.0,5.5,2.1,2 115 | 5.7,2.5,5.0,2.0,2 116 | 5.8,2.8,5.1,2.4,2 117 | 6.4,3.2,5.3,2.3,2 118 | 6.5,3.0,5.5,1.8,2 119 | 7.7,3.8,6.7,2.2,2 120 | 7.7,2.6,6.9,2.3,2 121 | 6.0,2.2,5.0,1.5,2 122 | 6.9,3.2,5.7,2.3,2 123 | 5.6,2.8,4.9,2.0,2 124 | 7.7,2.8,6.7,2.0,2 125 | 6.3,2.7,4.9,1.8,2 126 | 6.7,3.3,5.7,2.1,2 127 | 7.2,3.2,6.0,1.8,2 128 | 6.2,2.8,4.8,1.8,2 129 | 6.1,3.0,4.9,1.8,2 130 | 6.4,2.8,5.6,2.1,2 131 | 7.2,3.0,5.8,1.6,2 132 | 7.4,2.8,6.1,1.9,2 133 | 7.9,3.8,6.4,2.0,2 134 | 6.4,2.8,5.6,2.2,2 135 | 6.3,2.8,5.1,1.5,2 136 | 6.1,2.6,5.6,1.4,2 137 | 7.7,3.0,6.1,2.3,2 138 | 6.3,3.4,5.6,2.4,2 139 | 6.4,3.1,5.5,1.8,2 140 | 6.0,3.0,4.8,1.8,2 141 | 6.9,3.1,5.4,2.1,2 142 | 6.7,3.1,5.6,2.4,2 143 | 6.9,3.1,5.1,2.3,2 144 | 5.8,2.7,5.1,1.9,2 145 | 6.8,3.2,5.9,2.3,2 146 | 6.7,3.3,5.7,2.5,2 147 | 6.7,3.0,5.2,2.3,2 148 | 6.3,2.5,5.0,1.9,2 149 | 6.5,3.0,5.2,2.0,2 150 | 6.2,3.4,5.4,2.3,2 151 | 5.9,3.0,5.1,1.8,2 152 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Open In Colab 4 | 5 | 6 | 7 | Open In nbviewer 8 | 9 |

10 | 11 | # Neural Networks Course (MFN0824) 12 | 13 | This repository contains the Jupyter notebooks and other materials used for the Neural Networks course, held at the Physics Department of the University of Turin. You can find more details on the [institutional page of the course](https://fisica.campusnet.unito.it/do/corsi.pl/Show?_id=6e6f). More notebooks and materials will be added along the course. 14 | 15 | **NOTE:** The "appendix" notebooks, beginning with capital letters, are meant to be a review of basic concepts that will be used during the course. They should therefore be completed _before_ looking at the numbered notebooks, which contain the course material instead. 16 | 17 | --- 18 | 19 | ## Looking at the Notebooks 20 | For some reason, github does not render the notebooks in the correct way. If you just want to take a look at the rendered notebooks without running the code, please use the [Jupyter Notebook Viewer](https://nbviewer.jupyter.org/) service. You can access the notebooks in this directory directly by clicking on the badge at the top of the page or here: 21 | 22 | Open In nbviewer 23 | . 24 | 25 | 26 | --- 27 | 28 | ## Running the Notebooks 29 | 30 | The notebooks can be run both locally on your computer or online using [Google Colab](colab.research.google.com). 31 | 32 | * **Colab Notebooks** 33 | 34 | The Colab service can be accessed for free with any personal google account, including your institutional one (@edu.unito.it, @unito.it), and provides an interactive Jupyter notebook environment. Many of the most used python libraries are pre-installed and can be used in a straightforward manner after a simple `import` statement. The Colab [main page](colab.research.google.com) is also a good starting point both if you need to gain a bit of confidence with the basics of Jupyter notebook and if you want to take a look at some nice examples and tutorials. 35 | 36 | You can load a copy of the entire directory in colab by clicking on the following badge: 37 | 38 | Open In Colab 39 | 40 | A similar badge will be present in each notebook and allows you to open a copy of the the notebook directly in Colab. Note that, in order to retain your work, you will need to **save** the notebooks before you close them. 41 | 42 | 43 | * **Jupyter Notebooks on a Local Machine** 44 | 45 | You can also run the notebooks by cloning this github repository on your local machine or by downloading it in the compressed version. In this case, in order to run the notebooks it is advised that you download and install the latest [Anaconda Distribution](https://www.anaconda.com/distribution/) which will provide you with a lot of useful packages, including Jupyter notebooks, Numpy and Matplotlib. You will have to install Tensorflow2 manually after installing Anaconda, in order to do so just open a terminal and type: 46 | 47 | `conda install tensorflow` 48 | -------------------------------------------------------------------------------- /05 - Gridworld with Value Iteration.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Gridworld\n", 8 | "\n", 9 | "*Andrea Mazzolini*, andrea.mazzolini.90@gmail.com.\n", 10 | "\n", 11 | "\n", 12 | "Here we want to find the optimal strategy of a 2d grid-world problem having full information.\n", 13 | "**Value iteration** algorithm will be used." 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": {}, 19 | "source": [ 20 | "## Gridworld as a Markov Decision Process\n", 21 | "\n", 22 | "### States\n", 23 | "The state space corresponds to the physical space of the gridworld. Therefore each state is identified by the two coordinates, and the whole space is composed of $d^2$ states:\n", 24 | "\n", 25 | "$$\n", 26 | "\\mathcal{S} = \\{ 0, 1, \\ldots, d-1 \\} \\times \\{ 0, 1, \\ldots, d-1 \\}\n", 27 | "$$\n", 28 | "\n", 29 | "### Actions\n", 30 | "The actions of the agent are five: he can move to nearest neighbours or stay in the cell without moving:\n", 31 | "\n", 32 | "$$\n", 33 | "\\mathcal{A} = \\{ \\text{up}, \\text{left}, \\text{down}, \\text{right}, \\text{stay} \\} = \\{ (0,1), (-1,0), (0,-1), (1,0), (0,0)\\}\n", 34 | "$$\n", 35 | "\n", 36 | "which can be expressed also translation vectors.\n", 37 | "Actually, these actions are not always possible in each state: the agent cannot cross boundaries. This makes actions state dependent, for example if the agent is located on the left boundary: $\\mathcal{A}(0,y) = \\{ \\text{up}, \\text{down}, \\text{right}, \\text{stay} \\}$, or in a corner: $\\mathcal{A}(d-1,d-1) = \\{ \\text{left}, \\text{down}, \\text{stay} \\}$.\n", 38 | "\n", 39 | "### Transition probabilities\n", 40 | "\n", 41 | "The transition probabilities between states are deterministic: the next state is just the old state plus the translation action chosen by the agent:\n", 42 | "\n", 43 | "$$\n", 44 | "p(s_{t+1} | a_t, s_t) = \\delta (s_{t+1} = a_t + s_t)\n", 45 | "$$\n", 46 | "\n", 47 | "### Rewards\n", 48 | "\n", 49 | "The rewards depends only on the arrival states, $r(s_{t+1})$, and are zero for all the states with the exception of some special ones chosen to contain some resource." 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "## Value iteration algorithm\n", 57 | "\n", 58 | "* Initialise the values of all the states $V^{(0)}(s)$.\n", 59 | "\n", 60 | "* For $t$ in $[1,2,\\ldots]$ until a *convergence condition* is satisfied do:\n", 61 | "> * for each state $s$:\n", 62 | "> > * Compute the new value estimate using the Bellman equation: $ V^{(t)}(s) = \\max_{a} \\sum_{s'} p(s'|s,a)\\big[ r(s',a, s) + \\gamma\\,V^{(t-1)}(s') \\big] $" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "## Implementation\n", 70 | "\n", 71 | "### Environment class: the gridworld\n", 72 | "\n", 73 | "The Gridworld class contains all the information about the environment:\n", 74 | "- The info about the state space (cells and obstacles).\n", 75 | "- The set of possible actions from each state.\n", 76 | "- The reward table: which reward the agent take in each cell (0 if none).\n", 77 | "\n", 78 | "The methods are:\n", 79 | "- `display()`: Plot the gridworld.\n", 80 | "- `display_best_path(values, gamma, start_cell)`: Plot the best path from the `start_cell`." 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": 1, 86 | "metadata": {}, 87 | "outputs": [], 88 | "source": [ 89 | "import numpy as np\n", 90 | "from copy import copy\n", 91 | "import seaborn as sns\n", 92 | "import matplotlib.pyplot as plt\n", 93 | "from matplotlib import cm\n", 94 | "\n", 95 | "\n", 96 | "class Gridworld:\n", 97 | "\n", 98 | " def __init__(self, grid_size, rewards, obstacles=[]):\n", 99 | " \"\"\"\n", 100 | " Training environment for reinforcement learning: gridworld.\n", 101 | " Args:\n", 102 | " - grid_size, (int, int): defining the size of the 2d lattice\n", 103 | " - init_cell, (int, int): coordinates from 0 to size-1 from which the agent starts to play\n", 104 | " - rewards, list((int, int), float): list of the coordinates§ and values of the rewards\n", 105 | " - obstacles, lits((int, int)): list of the coordinates of the obstacles\n", 106 | " \"\"\"\n", 107 | "\n", 108 | " # Define state space\n", 109 | " self.state = None # current state of the game\n", 110 | " self.state_dim = grid_size\n", 111 | " #self.init_state = init_cell\n", 112 | " self.obstacles = obstacles\n", 113 | " # Cells that are not obstacles\n", 114 | " self.states = [(i,j) for i in range(self.state_dim[0]) for j in range(self.state_dim[1]) if (i,j) not in self.obstacles] \n", 115 | "\n", 116 | " # Define action space\n", 117 | " self.action_dim = (5,) # up, right, down, left, stay\n", 118 | " self.action_dict = {\"up\": 0, \"right\": 1, \"down\": 2, \"left\": 3, \"stay\": 4}\n", 119 | " self.action_coords = [(0, 1), (1, 0), (0, -1), (-1, 0), (0, 0)] # translations\n", 120 | " self.actions_allowed = self._build_allowed_actions(obstacles)\n", 121 | "\n", 122 | " # Define rewards table\n", 123 | " self.R = self._build_rewards(rewards)\n", 124 | "\n", 125 | "\n", 126 | " def _build_allowed_actions(self, obstacles):\n", 127 | " actions_allowed = dict()\n", 128 | " Nx, Ny = self.state_dim\n", 129 | " for x in range(Nx):\n", 130 | " for y in range(Ny):\n", 131 | " # Actions not allowed at the boundaries\n", 132 | " actions_allowed[(x,y)] = [self.action_dict[\"stay\"]] # The stay action is always allowed\n", 133 | " if (y > 0): \n", 134 | " actions_allowed[(x,y)].append(self.action_dict[\"down\"])\n", 135 | " if (y < Ny - 1): \n", 136 | " actions_allowed[(x,y)].append(self.action_dict[\"up\"])\n", 137 | " if (x > 0): \n", 138 | " actions_allowed[(x,y)].append(self.action_dict[\"left\"])\n", 139 | " if (x < Nx - 1): \n", 140 | " actions_allowed[(x,y)].append(self.action_dict[\"right\"])\n", 141 | " actions_allowed[(x,y)] = np.array(actions_allowed[(x,y)], dtype=int)\n", 142 | "\n", 143 | " # Actions not allowed because of obstacles\n", 144 | " for o in obstacles:\n", 145 | " if (x+1,y) == o:\n", 146 | " actions_allowed[(x,y)] = actions_allowed[(x,y)][actions_allowed[(x,y)] != self.action_dict[\"right\"]]\n", 147 | " if (x-1,y) == o:\n", 148 | " actions_allowed[(x,y)] = actions_allowed[(x,y)][actions_allowed[(x,y)] != self.action_dict[\"left\"]]\n", 149 | " if (x,y+1) == o:\n", 150 | " actions_allowed[(x,y)] = actions_allowed[(x,y)][actions_allowed[(x,y)] != self.action_dict[\"up\"]]\n", 151 | " if (x,y-1) == o:\n", 152 | " actions_allowed[(x,y)] = actions_allowed[(x,y)][actions_allowed[(x,y)] != self.action_dict[\"down\"]]\n", 153 | " return actions_allowed\n", 154 | "\n", 155 | "\n", 156 | " def _build_rewards(self, rewards):\n", 157 | " R = np.zeros(self.state_dim, dtype=float)\n", 158 | " for rew in rewards:\n", 159 | " R[rew[0]] = rew[1]\n", 160 | " return R\n", 161 | "\n", 162 | " def display(self, values=np.array([]), cmap=sns.dark_palette(\"red\", as_cmap=True), figsize=(7,6)):\n", 163 | " fig = plt.figure(figsize=figsize)\n", 164 | " obstacle_mask = np.zeros(self.state_dim, dtype=bool)\n", 165 | " for obs in self.obstacles:\n", 166 | " obstacle_mask[obs[0], obs[1]] = True\n", 167 | "\n", 168 | " if len(values)==0:\n", 169 | " ax = sns.heatmap(obstacle_mask.T, cmap=cm.get_cmap(\"Greys\"), cbar=False, \n", 170 | " linewidths=0.1, linecolor='#222222')\n", 171 | " else:\n", 172 | " ax = sns.heatmap(values.T, mask=obstacle_mask.T, cmap=cmap, \n", 173 | " linewidths=0.1, linecolor='#222222', vmin=np.min(values[values != 0]))\n", 174 | " ax.collections[0].colorbar.set_label(\"Value\", fontsize=14)\n", 175 | "\n", 176 | " ax.invert_yaxis()\n", 177 | " ax.set_xlabel('x', fontsize=14)\n", 178 | " ax.set_ylabel('y', fontsize=14)\n", 179 | " #ax.scatter([start_cell[0]+0.5],[start_cell[1]+0.5], s=100, c='grey', label='Start')\n", 180 | " \n", 181 | " for rew in rewards:\n", 182 | " ax.scatter([rew[0][0]+0.5],[rew[0][1]+0.5], s=200*rew[1], c='red', label='Reward:{}'.format(rew[1]), marker='*')\n", 183 | " plt.tight_layout()\n", 184 | "\n", 185 | " return fig, ax\n", 186 | "\n", 187 | "\n", 188 | " def display_best_path(self, values, gamma, start_coord, lcolor='black', cmap=sns.dark_palette(\"red\", as_cmap=True), figsize=(7,6)):\n", 189 | " fig, ax = self.display(values, cmap, figsize)\n", 190 | " obstacle_mask = np.zeros(self.state_dim, dtype=bool)\n", 191 | "\n", 192 | " s, count, best_action = start_coord, 0, 0\n", 193 | " while best_action != 4 or count < self.state_dim[0]*self.state_dim[1]:\n", 194 | " count += 1\n", 195 | " qualities = np.zeros(5)\n", 196 | " for a in self.actions_allowed[s]:\n", 197 | " new_s = (self.action_coords[a][0] + s[0], self.action_coords[a][1] + s[1])\n", 198 | " qualities[a] = self.R[new_s] + gamma*values[new_s]\n", 199 | " best_action = np.argmax(qualities)\n", 200 | " new_s = s[0] + self.action_coords[best_action][0], s[1] + self.action_coords[best_action][1]\n", 201 | " ax.plot([s[0]+0.5, new_s[0]+0.5], [s[1]+0.5, new_s[1]+0.5], c='black', lw=3)\n", 202 | " s = new_s\n", 203 | "\n", 204 | " plt.tight_layout()\n", 205 | "\n", 206 | " return fig, ax" 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": 2, 212 | "metadata": {}, 213 | "outputs": [ 214 | { 215 | "data": { 216 | "text/plain": [ 217 | "
" 218 | ] 219 | }, 220 | "metadata": {}, 221 | "output_type": "display_data" 222 | }, 223 | { 224 | "data": { 225 | "image/png": "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\n", 226 | "text/plain": [ 227 | "
" 228 | ] 229 | }, 230 | "metadata": { 231 | "needs_background": "light" 232 | }, 233 | "output_type": "display_data" 234 | } 235 | ], 236 | "source": [ 237 | "world_size = (16,12) # dimension of the gridworld\n", 238 | "\n", 239 | "#start_cell = (1,1)\n", 240 | "\n", 241 | "obstacles = [(2,2), (2,3), (2,4), (2,5), (2,6), (2,7), (2,8), (2,9), \n", 242 | " (3,9), (4,9), (5,9), (6,8), (6,7), (6,6), (5,5), (4,5),\n", 243 | " (4,4), (5,3), (6,2),\n", 244 | " (9,2), (9,3), (9,4), (9,5), (9,6), (9,7), (9,8), (9,9), \n", 245 | " (10,2), (11,2), (12,2), (13,2)]\n", 246 | "\n", 247 | "rewards = [((5, 8), 1), ((14, 10), 2)]\n", 248 | "\n", 249 | "gridworld = Gridworld(world_size, rewards, obstacles) # Building the world\n", 250 | "plt.figure(figsize=(12, 6))\n", 251 | "fig, ax = gridworld.display(figsize=(5.5,4)) # And showing it\n", 252 | "#ax.legend(fontsize=14)" 253 | ] 254 | }, 255 | { 256 | "cell_type": "code", 257 | "execution_count": 3, 258 | "metadata": {}, 259 | "outputs": [], 260 | "source": [ 261 | "def val_iter_gridworld(gridworld, gamma, delta_conv=0.001, max_time=1000):\n", 262 | " \"\"\"\n", 263 | " Value iteration algorithm applied to Gridworld. It returns the computed value \n", 264 | " function as a 2d array.\n", 265 | " \"\"\"\n", 266 | " values = np.zeros(gridworld.state_dim)\n", 267 | " for t in range(max_time):\n", 268 | " old_values = copy(values)\n", 269 | " delta_v = 0\n", 270 | " for s in gridworld.states:\n", 271 | " qualities = np.array([])\n", 272 | " for a in gridworld.actions_allowed[s]:\n", 273 | " new_s = (gridworld.action_coords[a][0] + s[0], gridworld.action_coords[a][1] + s[1])\n", 274 | " qualities = np.append(qualities, gridworld.R[new_s] + gamma * old_values[new_s])\n", 275 | " values[s] = qualities.max()\n", 276 | " delta_v = max(delta_v, abs(values[s] - old_values[s]))\n", 277 | " \n", 278 | " if delta_v < delta_conv:\n", 279 | " break\n", 280 | " \n", 281 | " return values" 282 | ] 283 | }, 284 | { 285 | "cell_type": "code", 286 | "execution_count": 8, 287 | "metadata": {}, 288 | "outputs": [], 289 | "source": [ 290 | "gamma = 0.75\n", 291 | "\n", 292 | "values = val_iter_gridworld(gridworld, gamma)" 293 | ] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "execution_count": 9, 298 | "metadata": {}, 299 | "outputs": [ 300 | { 301 | "data": { 302 | "text/plain": [ 303 | "(
,\n", 304 | " )" 305 | ] 306 | }, 307 | "execution_count": 9, 308 | "metadata": {}, 309 | "output_type": "execute_result" 310 | }, 311 | { 312 | "data": { 313 | "image/png": "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\n", 314 | "text/plain": [ 315 | "
" 316 | ] 317 | }, 318 | "metadata": { 319 | "needs_background": "light" 320 | }, 321 | "output_type": "display_data" 322 | } 323 | ], 324 | "source": [ 325 | "gridworld.display(values, cmap=cm.get_cmap('inferno'), figsize=(5.5,4))" 326 | ] 327 | }, 328 | { 329 | "cell_type": "code", 330 | "execution_count": 10, 331 | "metadata": {}, 332 | "outputs": [ 333 | { 334 | "data": { 335 | "text/plain": [ 336 | "(
,\n", 337 | " )" 338 | ] 339 | }, 340 | "execution_count": 10, 341 | "metadata": {}, 342 | "output_type": "execute_result" 343 | }, 344 | { 345 | "data": { 346 | "image/png": "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\n", 347 | "text/plain": [ 348 | "
" 349 | ] 350 | }, 351 | "metadata": { 352 | "needs_background": "light" 353 | }, 354 | "output_type": "display_data" 355 | } 356 | ], 357 | "source": [ 358 | "gridworld.display_best_path(values, gamma, (4,2), cmap=cm.get_cmap('inferno'), figsize=(5.5,4))" 359 | ] 360 | }, 361 | { 362 | "cell_type": "code", 363 | "execution_count": null, 364 | "metadata": {}, 365 | "outputs": [], 366 | "source": [] 367 | } 368 | ], 369 | "metadata": { 370 | "kernelspec": { 371 | "display_name": "Python 3", 372 | "language": "python", 373 | "name": "python3" 374 | }, 375 | "language_info": { 376 | "codemirror_mode": { 377 | "name": "ipython", 378 | "version": 3 379 | }, 380 | "file_extension": ".py", 381 | "mimetype": "text/x-python", 382 | "name": "python", 383 | "nbconvert_exporter": "python", 384 | "pygments_lexer": "ipython3", 385 | "version": "3.7.7" 386 | } 387 | }, 388 | "nbformat": 4, 389 | "nbformat_minor": 4 390 | } 391 | -------------------------------------------------------------------------------- /06 - Gridworld with Q-learning.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Gridworld\n", 8 | "\n", 9 | "*Andrea Mazzolini*, andrea.mazzolini.90@gmail.com.\n", 10 | "\n", 11 | "\n", 12 | "Here we want to find the optimal strategy of a 2d grid-world problem having no information about the environment. The\n", 13 | "**Q-learning** algorithm will be used." 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": {}, 19 | "source": [ 20 | "## Gridworld as a Markov Decision Process\n", 21 | "\n", 22 | "### States\n", 23 | "The state space corresponds to the physical space of the gridworld. Therefore each state is identified by the two coordinates, and the whole space is composed of $d^2$ states:\n", 24 | "\n", 25 | "$$\n", 26 | "\\mathcal{S} = \\{ 0, 1, \\ldots, d-1 \\} \\times \\{ 0, 1, \\ldots, d-1 \\}\n", 27 | "$$\n", 28 | "\n", 29 | "### Actions\n", 30 | "The actions of the agent are five: he can move to nearest neighbours or stay in the cell without moving:\n", 31 | "\n", 32 | "$$\n", 33 | "\\mathcal{A} = \\{ \\text{up}, \\text{left}, \\text{down}, \\text{right}, \\text{stay} \\} = \\{ (0,1), (-1,0), (0,-1), (1,0), (0,0)\\}\n", 34 | "$$\n", 35 | "\n", 36 | "which can be expressed also translation vectors.\n", 37 | "Actually, these actions are not always possible in each state: the agent cannot cross boundaries. This makes actions state dependent, for example if the agent is located on the left boundary: $\\mathcal{A}(0,y) = \\{ \\text{up}, \\text{down}, \\text{right}, \\text{stay} \\}$, or in a corner: $\\mathcal{A}(d-1,d-1) = \\{ \\text{left}, \\text{down}, \\text{stay} \\}$.\n", 38 | "\n", 39 | "### Transition probabilities\n", 40 | "\n", 41 | "The transition probabilities between states are deterministic: the next state is just the old state plus the translation action chosen by the agent:\n", 42 | "\n", 43 | "$$\n", 44 | "p(s_{t+1} | a_t, s_t) = \\delta (s_{t+1} = a_t + s_t)\n", 45 | "$$\n", 46 | "\n", 47 | "### Rewards\n", 48 | "\n", 49 | "The rewards depends only on the arrival states, $r(s_{t+1})$, and are zero for all the states with the exception of some special ones chosen to contain some resource." 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "## Q-learning\n", 57 | "\n", 58 | "Q-learning is a reinforcement learning algorithm for any finite Markov decision processes (state and action space must be discrete and finite), which can converge to an optimal policy for maximizing an exponentially discounted return.\n", 59 | "It does not require a model (hence the connotation \"model-free\") of the environment.\n", 60 | "For a \"model\" we mean the knowledge of the transition probabilities and reward function of the MDP. Differently value iteration or dynamic programming rely on this information.\n", 61 | "\n", 62 | "### Table for the state-action qualitites\n", 63 | "\n", 64 | "The general idea of the algorithm is to build a table of estimates of \"goodness\" of each state and action pairs. This object is called Quality matrix: ${Q}(s, a)$. It can be proven that, when the algorithm converge, the Qualitiy becomes the best quality function:\n", 65 | "\n", 66 | "$$\n", 67 | "Q(s,a) \\rightarrow Q^*(s,a) = \\max_{\\pi} \\left[ \\mathbb{E}_\\pi\\left[ \\sum_{t=0}^\\infty \\gamma^t\\,r_t \\Big| s_0 = s, a_0 = a \\right] \\right]\n", 68 | "$$\n", 69 | "\n", 70 | "which is the best possible return starting from the state $s$ and taking the action $a$.\n", 71 | "By assuming that $Q$ are good estimates of $Q^*$, the best policy is deterministic and, for each state $s$, consists in choosing the action that leads to the best possible return:\n", 72 | "\n", 73 | "$$\n", 74 | "\\pi^*(s) = \\delta (a - \\text{argmax}_b Q(s,b))\n", 75 | "$$\n", 76 | "\n", 77 | "### Finding the quality matrix\n", 78 | "\n", 79 | "The core of the algorithm is a simple online quality update. At time $t$ the learning agent is in the state $s_t$ and take the action $a_t$ (later we specify how to choose the action). As a consequence it moves to a new state $s_{t+1}$ taking the reward $r_t$. Note that $s_{t+1}$ and $r_t$ are stochastic outcomes of the MDP, that the agent can just sample (it doesn't have a model and then a prediction of what they are).\n", 80 | "The Q-learning update rule for the Quality is:\n", 81 | "\n", 82 | "$$\n", 83 | "Q(s_t, a_t) \\leftarrow Q(s_t, a_t) + \\alpha \\left(r_t + \\gamma \\max_b Q(s_{t+1}, b) - Q(s_t, a_t)\\right)\n", 84 | "$$\n", 85 | "\n", 86 | "$$\n", 87 | "Q(s_t, a_t)^* = \\mathbb{E} \\left[ r_t + \\gamma \\max_b Q^*(s_{t+1}, b) \\right] \n", 88 | "$$\n", 89 | "\n", 90 | "where $\\gamma$ is the discount factor defined in the return that one aims to maximize, and $\\alpha$ is a learning rate.\n", 91 | "Note that the Bellman equation for the quality reads $Q(s_t, a_t)^* = \\mathbb{E} \\left[ r_t + \\gamma \\max_b Q(s_{t+1}, b) \\right] $, and the error appearing in the learning rule above says exactly how much I am far away from satisfying the equation with my sample. \n", 92 | "\n", 93 | "### Pseudocode for Q-learning\n", 94 | "\n", 95 | "Therefore, the algorithm consists simply in starting from an initial configuration fo the Quality table, and then \"play the game\"\n", 96 | "$$\n", 97 | "s_0, a_0 \\rightarrow r_0, s_1, a_1 \\rightarrow r_1, s_2, a_2 \\rightarrow \\ldots\n", 98 | "$$\n", 99 | "and update the Quality as specified above after each transition.\n", 100 | "\n", 101 | "We still have to specify how to choose actions. Here we consider an epsilon-greeedy strategy. Let us define two way of choosing the action:\n", 102 | "- **Exploration** move, where the action is choosen uniformely at random among the possible action from the state in which the aget is.\n", 103 | "- **Exploitation** move, where the action is taken as the one that maximize my current Qualitites, which is the best action that I can take according to my estimates of the returns, $a_t = \\text{argmax}_b Q(s_t, b)$.\n", 104 | "\n", 105 | "An epsilon-greeedy strategy says that the exploration move is chosen with probability $\\epsilon$, and the exploitation one otherwise.\n", 106 | "\n", 107 | "Putting everything together, the pseudocode for an epsilon-greedy Q-learning algorithm is the following:\n", 108 | "\n", 109 | " - Initialize the Q-matrix and choose the algorithm parameters $\\gamma$, $\\alpha$, $\\epsilon$.\n", 110 | " - Set the agent in the starting state $s_0$.\n", 111 | " - For $t = 1, \\ldots$ until convergence:\n", 112 | "> - With probability $\\epsilon$ choose $a_t$ at random from the possible actions, otherwise choose the action that maximizes the Qualities $a_t = \\text{argmax}_b Q(s_t, b)$.\n", 113 | "> - Play a step in the game and get the new state and the reward $s_t, a_t \\rightarrow s_{t+1}, r_t$\n", 114 | "> - Update the quality matrix using the obtained sample\n", 115 | "> $$\n", 116 | "Q(s_t, a_t) \\leftarrow Q(s_t, a_t) + \\alpha \\left(r_t + \\gamma \\max_b Q(s_{t+1}, b) - Q(s_t, a_t)\\right)\n", 117 | "$$\n", 118 | "\n", 119 | "### Episodic game and exploration scheduling\n", 120 | "\n", 121 | "Actually one usually introduces two tricks to speed up the covergence of the quality table.\n", 122 | "\n", 123 | "The first is to restart the game after a given number of steps (the state is forced to be $s_0$ again). Each of these runs is called episode. This is natural if there are terminal states and, at some point, the game finishes. However, it can be useful to rerestart the game when I'm interested in a particular initial condition. In this way, I force the algorithm to explore more and have better estimates around this initial condition.\n", 124 | "\n", 125 | "A second trick is to schedule the exporation parameter $\\epsilon$. Usually, I want the exploration to be large at the beginning, to have an approximate idea of all the possible qualities. L\n", 126 | "ater I want instead to focus on the best moves to have more fine-tuned estimates of them, forgetting about the bad actions.\n", 127 | "\n", 128 | "Rewriting the pseudocode following these two observations we have:\n", 129 | "\n", 130 | " - Initialize the Q-matrix and choose the algorithm parameters $\\gamma$, $\\alpha$, $\\epsilon_0$, $T_{episode}$.\n", 131 | "> For episodes $e = 1, \\ldots$ until convergence:\n", 132 | "> - Set the agent in the starting state $s_0$.\n", 133 | "> - For steps in the episode $t = 1, \\ldots, T_{episode}$:\n", 134 | ">> - With probability $\\epsilon_e$ choose $a_t$ at random from the possible actions, otherwise choose the action that maximizes the Qualities $a_t = \\text{argmax}_b Q(s_t, b)$.\n", 135 | ">> - Play a step in the game and get the new state and the reward $s_t, a_t \\rightarrow s_{t+1}, r_t$\n", 136 | ">> - Update the quality matrix using the obtained sample\n", 137 | ">> $$\n", 138 | "Q(s_t, a_t) \\leftarrow Q(s_t, a_t) + \\alpha \\left(r_t + \\gamma \\max_b Q(s_{t+1}, b) - Q(s_t, a_t)\\right)\n", 139 | "$$\n", 140 | "> - Decrease the exploration rate $\\epsilon_e$." 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "metadata": {}, 146 | "source": [ 147 | "## Implementation\n", 148 | "\n", 149 | "### Environment class: the gridworld\n", 150 | "\n", 151 | "The Gridworld class contains all the information about the environment:\n", 152 | "- The info about the state space, the current state of the game and the initial state.\n", 153 | "- The set of possible actions.\n", 154 | "- The reward table: which reward the agent take in each cell (0 if none).\n", 155 | "\n", 156 | "The methods are:\n", 157 | "- `reset()`: the game is initialized. Here the only initialization is to put the agent in the starting cell. \n", 158 | "- `step(action)`: update the agent state according to the `action` passed and compute reward. Returns the state after the transition (the movement) and the reward." 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": 1, 164 | "metadata": {}, 165 | "outputs": [], 166 | "source": [ 167 | "import numpy as np\n", 168 | "from copy import copy\n", 169 | "import operator\n", 170 | "import sys\n", 171 | "import seaborn as sns\n", 172 | "import matplotlib.pyplot as plt\n", 173 | "from matplotlib import cm\n", 174 | "\n", 175 | "\n", 176 | "class Gridworld:\n", 177 | "\n", 178 | " def __init__(self, grid_size, init_cell, rewards, obstacles=[]):\n", 179 | " \"\"\"\n", 180 | " Training environment for reinforcement learning: gridworld.\n", 181 | " Args:\n", 182 | " - grid_size, (int, int): defining the size of the 2d lattice\n", 183 | " - init_cell, (int, int): coordinates from 0 to size-1 from which the agent starts to play\n", 184 | " - rewards, list((int, int), float): list of the coordinates and values of the rewards\n", 185 | " - obstacles, lits((int, int)): list of the coordinates of the obstacles\n", 186 | " \"\"\"\n", 187 | "\n", 188 | " # Define state space\n", 189 | " self.state = None # current state of the game\n", 190 | " self.state_dim = grid_size\n", 191 | " self.init_state = init_cell\n", 192 | " self.obstacles = obstacles\n", 193 | " # Cells that are not obstacles\n", 194 | " self.states = [(i,j) for i in range(self.state_dim[0]) for j in range(self.state_dim[1]) if (i,j) not in self.obstacles] \n", 195 | "\n", 196 | " # Define action space\n", 197 | " self.action_dim = (5,) # up, right, down, left, stay\n", 198 | " self.action_dict = {\"up\": 0, \"right\": 1, \"down\": 2, \"left\": 3, \"stay\": 4}\n", 199 | " self.action_coords = [(0, 1), (1, 0), (0, -1), (-1, 0), (0, 0)] # translations\n", 200 | " self.actions_allowed = self._build_allowed_actions(obstacles)\n", 201 | "\n", 202 | " # Define rewards table\n", 203 | " self.R = self._build_rewards(rewards)\n", 204 | "\n", 205 | "\n", 206 | " def reset(self):\n", 207 | " \"\"\"Reset agent state to its initial cell\"\"\" \n", 208 | " self.state = self.init_state\n", 209 | " return self.state\n", 210 | "\n", 211 | "\n", 212 | " def step(self, action):\n", 213 | " \"\"\"Update agent state\"\"\"\n", 214 | " state_next = (self.state[0] + self.action_coords[action][0],\n", 215 | " self.state[1] + self.action_coords[action][1])\n", 216 | " # Collect reward\n", 217 | " reward = self.R[state_next]\n", 218 | " # Update state\n", 219 | " self.state = state_next\n", 220 | " return state_next, reward\n", 221 | "\n", 222 | "\n", 223 | " def _build_allowed_actions(self, obstacles):\n", 224 | " actions_allowed = dict()\n", 225 | " Nx, Ny = self.state_dim\n", 226 | " for x in range(Nx):\n", 227 | " for y in range(Ny):\n", 228 | " # Actions not allowed at the boundaries\n", 229 | " actions_allowed[(x,y)] = [self.action_dict[\"stay\"]] # The stay action is always allowed\n", 230 | " if (y > 0): \n", 231 | " actions_allowed[(x,y)].append(self.action_dict[\"down\"])\n", 232 | " if (y < Ny - 1): \n", 233 | " actions_allowed[(x,y)].append(self.action_dict[\"up\"])\n", 234 | " if (x > 0): \n", 235 | " actions_allowed[(x,y)].append(self.action_dict[\"left\"])\n", 236 | " if (x < Nx - 1): \n", 237 | " actions_allowed[(x,y)].append(self.action_dict[\"right\"])\n", 238 | " actions_allowed[(x,y)] = np.array(actions_allowed[(x,y)], dtype=int)\n", 239 | "\n", 240 | " # Actions not allowed because of obstacles\n", 241 | " for o in obstacles:\n", 242 | " if (x+1,y) == o:\n", 243 | " actions_allowed[(x,y)] = actions_allowed[(x,y)][actions_allowed[(x,y)] != self.action_dict[\"right\"]]\n", 244 | " if (x-1,y) == o:\n", 245 | " actions_allowed[(x,y)] = actions_allowed[(x,y)][actions_allowed[(x,y)] != self.action_dict[\"left\"]]\n", 246 | " if (x,y+1) == o:\n", 247 | " actions_allowed[(x,y)] = actions_allowed[(x,y)][actions_allowed[(x,y)] != self.action_dict[\"up\"]]\n", 248 | " if (x,y-1) == o:\n", 249 | " actions_allowed[(x,y)] = actions_allowed[(x,y)][actions_allowed[(x,y)] != self.action_dict[\"down\"]]\n", 250 | " return actions_allowed\n", 251 | "\n", 252 | "\n", 253 | " def _build_rewards(self, rewards):\n", 254 | " R = np.zeros(self.state_dim, dtype=float)\n", 255 | " for rew in rewards:\n", 256 | " R[rew[0]] = rew[1]\n", 257 | " return R\n", 258 | "\n", 259 | " def display(self, values=np.array([]), cmap=sns.dark_palette(\"red\", as_cmap=True), figsize=(7,6)):\n", 260 | " fig = plt.figure(figsize=figsize)\n", 261 | " obstacle_mask = np.zeros(self.state_dim, dtype=bool)\n", 262 | " for obs in obstacles:\n", 263 | " obstacle_mask[obs[0], obs[1]] = True\n", 264 | "\n", 265 | " if len(values)==0:\n", 266 | " ax = sns.heatmap(obstacle_mask.T, cmap=cm.get_cmap(\"Greys\"), cbar=False, \n", 267 | " linewidths=0.1, linecolor='#222222')\n", 268 | " else:\n", 269 | " ax = sns.heatmap(values.T, mask=obstacle_mask.T, cmap=cmap, \n", 270 | " linewidths=0.1, linecolor='#222222', vmin=np.min(values[values != 0]))\n", 271 | " ax.collections[0].colorbar.set_label(\"Value\", fontsize=14)\n", 272 | "\n", 273 | " ax.invert_yaxis()\n", 274 | " ax.set_xlabel('x', fontsize=14)\n", 275 | " ax.set_ylabel('y', fontsize=14)\n", 276 | " ax.scatter([start_cell[0]+0.5],[start_cell[1]+0.5], s=100, c='grey', label='Start')\n", 277 | " \n", 278 | " for rew in rewards:\n", 279 | " ax.scatter([rew[0][0]+0.5],[rew[0][1]+0.5], s=200*rew[1], c='red', label='Reward:{}'.format(rew[1]), marker='*')\n", 280 | " plt.tight_layout()\n", 281 | "\n", 282 | " return fig, ax\n", 283 | "\n", 284 | "\n", 285 | " def display_best_path(self, Q, start_coord, lcolor='black', values=np.array([]), cmap=sns.dark_palette(\"red\", as_cmap=True), figsize=(7,6)):\n", 286 | " fig, ax = self.display(values, cmap, figsize)\n", 287 | " obstacle_mask = np.zeros(self.state_dim, dtype=bool)\n", 288 | "\n", 289 | " s, count, best_action = start_coord, 0, 0\n", 290 | " while best_action != 4 or count < self.state_dim[0]*self.state_dim[1]:\n", 291 | " count += 1\n", 292 | " best_action = np.argmax(Q[s[0], s[1], :])\n", 293 | " new_s = s[0] + self.action_coords[best_action][0], s[1] + self.action_coords[best_action][1]\n", 294 | " ax.plot([s[0]+0.5, new_s[0]+0.5], [s[1]+0.5, new_s[1]+0.5], c='black', lw=3)\n", 295 | " s = new_s\n", 296 | "\n", 297 | " plt.tight_layout()\n", 298 | "\n", 299 | " return fig, ax" 300 | ] 301 | }, 302 | { 303 | "cell_type": "markdown", 304 | "metadata": {}, 305 | "source": [ 306 | "### Agent class: the Q-learning algorithm\n", 307 | "\n", 308 | "This class defines how the agent chooses the action from each state and how improve its strategy while playing.\n", 309 | "It emplys a Q-learning algorithm with epsilon-greedy policy.\n", 310 | "In particular:\n", 311 | "- `get_action()` returns an action using the epsilon greedy rule.\n", 312 | "- `train()` performs one step of the Q-learning update." 313 | ] 314 | }, 315 | { 316 | "cell_type": "code", 317 | "execution_count": 2, 318 | "metadata": {}, 319 | "outputs": [], 320 | "source": [ 321 | "class QL_agent:\n", 322 | "\n", 323 | " def __init__(self, env, gamma, learning_rate=0.1, eps_decay=0.995):\n", 324 | "\n", 325 | " # Store the environment\n", 326 | " self.env = env\n", 327 | "\n", 328 | " # Agent learning parameters\n", 329 | " self.epsilon = 1.0 # initial exploration probability\n", 330 | " self.epsilon_decay = eps_decay # epsilon decay after each episode\n", 331 | " self.alpha = learning_rate\n", 332 | " self.gamma = gamma # reward discount factor\n", 333 | "\n", 334 | " # Initialize Quality matrix\n", 335 | " self.Q = np.zeros(env.state_dim + env.action_dim, dtype=float)\n", 336 | "\n", 337 | "\n", 338 | " def get_action(self):\n", 339 | " \"\"\"\n", 340 | " Choose an action using an epsilon greedy policy: random with probability\n", 341 | " epsilon, greedy otherwise.\n", 342 | " \"\"\"\n", 343 | " state = self.env.state\n", 344 | " actions_allowed = self.env.actions_allowed[state]\n", 345 | " if np.random.rand() < self.epsilon: # explore\n", 346 | " return np.random.choice(actions_allowed)\n", 347 | " else: # exploit\n", 348 | " Q_s = self.Q[state[0], state[1], actions_allowed]\n", 349 | " actions_greedy = actions_allowed[np.flatnonzero(Q_s == np.max(Q_s))]\n", 350 | " return np.random.choice(actions_greedy)\n", 351 | "\n", 352 | "\n", 353 | " def train(self, transition):\n", 354 | " \"\"\"\n", 355 | " Q-learning update\n", 356 | " \"\"\"\n", 357 | " (state, action, state_next, reward) = transition\n", 358 | " sa = state + (action,)\n", 359 | " td_error = reward + gamma * np.max(self.Q[state_next]) - self.Q[sa]\n", 360 | " self.Q[sa] += self.alpha * td_error\n", 361 | "\n", 362 | " @property\n", 363 | " def values(self):\n", 364 | " vals = np.zeros(self.env.state_dim)\n", 365 | " for i in range(len(self.Q)):\n", 366 | " for j in range(len(self.Q[0])):\n", 367 | " vals[i,j] = np.max(self.Q[i,j])\n", 368 | " return vals" 369 | ] 370 | }, 371 | { 372 | "cell_type": "code", 373 | "execution_count": 8, 374 | "metadata": {}, 375 | "outputs": [ 376 | { 377 | "data": { 378 | "text/plain": [ 379 | "(
,\n", 380 | " )" 381 | ] 382 | }, 383 | "execution_count": 8, 384 | "metadata": {}, 385 | "output_type": "execute_result" 386 | }, 387 | { 388 | "data": { 389 | "image/png": "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\n", 390 | "text/plain": [ 391 | "
" 392 | ] 393 | }, 394 | "metadata": { 395 | "needs_background": "light" 396 | }, 397 | "output_type": "display_data" 398 | } 399 | ], 400 | "source": [ 401 | "world_size = (10,12) # dimension of the gridworld\n", 402 | "\n", 403 | "start_cell = (2,3)\n", 404 | "obstacles = [(2,5), (3,5), (4,5), (5,5), (5,4), (5,3), (5,2), (5,1), (5,0),\n", 405 | " (7,4), (7,5), (7,6), (7,7)]\n", 406 | "rewards = [((8,6), 2), ((3,9), 0.5)]\n", 407 | "\n", 408 | "gridworld = Gridworld(world_size, start_cell, rewards, obstacles) # Building the world\n", 409 | "gridworld.display(figsize=(5,5)) # And showing it" 410 | ] 411 | }, 412 | { 413 | "cell_type": "markdown", 414 | "metadata": {}, 415 | "source": [ 416 | "### Main learning cycle\n", 417 | "\n", 418 | "It follows the second pseudocode written before." 419 | ] 420 | }, 421 | { 422 | "cell_type": "code", 423 | "execution_count": 9, 424 | "metadata": {}, 425 | "outputs": [ 426 | { 427 | "name": "stdout", 428 | "output_type": "stream", 429 | "text": [ 430 | "Episode: 2000/2000 epsilon: 0.02044857891939702" 431 | ] 432 | } 433 | ], 434 | "source": [ 435 | "n_episodes = 2000\n", 436 | "n_steps_in_episode = 200 \n", 437 | "eps_decay = 0.995 # Multiplicative decay factor for the exploration rate\n", 438 | "gamma = 0.95\n", 439 | "learning_rate=0.1\n", 440 | "\n", 441 | "agent = QL_agent(gridworld, gamma, learning_rate, eps_decay)\n", 442 | "\n", 443 | "# Iteration over all the episodes\n", 444 | "for episode in range(n_episodes):\n", 445 | "\n", 446 | " state = gridworld.reset() # Setting the agent in the initial cell\n", 447 | "\n", 448 | " for _ in range(n_steps_in_episode):\n", 449 | " action = agent.get_action() # get action\n", 450 | " state_next, reward = gridworld.step(action) # evolve state by action\n", 451 | " agent.train((state, action, state_next, reward)) # train agent\n", 452 | " state = state_next # transition to next state\n", 453 | "\n", 454 | " agent.epsilon = max(agent.epsilon * agent.epsilon_decay, 0.02) # Decrease the exploration\n", 455 | " # Show training info\n", 456 | " sys.stdout.write(\"\\rEpisode: \" + str(episode+1) + \"/\" + str(n_episodes) + \" epsilon: \" + str(agent.epsilon))\n", 457 | " sys.stdout.flush()\n", 458 | " " 459 | ] 460 | }, 461 | { 462 | "cell_type": "code", 463 | "execution_count": 10, 464 | "metadata": {}, 465 | "outputs": [ 466 | { 467 | "data": { 468 | "text/plain": [ 469 | "(
,\n", 470 | " )" 471 | ] 472 | }, 473 | "execution_count": 10, 474 | "metadata": {}, 475 | "output_type": "execute_result" 476 | }, 477 | { 478 | "data": { 479 | "image/png": "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\n", 480 | "text/plain": [ 481 | "
" 482 | ] 483 | }, 484 | "metadata": { 485 | "needs_background": "light" 486 | }, 487 | "output_type": "display_data" 488 | } 489 | ], 490 | "source": [ 491 | "gridworld.display(agent.values, cmap=cm.get_cmap('inferno'), figsize=(5,5))" 492 | ] 493 | }, 494 | { 495 | "cell_type": "code", 496 | "execution_count": 11, 497 | "metadata": {}, 498 | "outputs": [ 499 | { 500 | "data": { 501 | "text/plain": [ 502 | "(
,\n", 503 | " )" 504 | ] 505 | }, 506 | "execution_count": 11, 507 | "metadata": {}, 508 | "output_type": "execute_result" 509 | }, 510 | { 511 | "data": { 512 | "image/png": "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\n", 513 | "text/plain": [ 514 | "
" 515 | ] 516 | }, 517 | "metadata": { 518 | "needs_background": "light" 519 | }, 520 | "output_type": "display_data" 521 | } 522 | ], 523 | "source": [ 524 | "gridworld.display_best_path(agent.Q, start_cell, cmap=cm.get_cmap('inferno'), figsize=(5,5))" 525 | ] 526 | }, 527 | { 528 | "cell_type": "code", 529 | "execution_count": null, 530 | "metadata": {}, 531 | "outputs": [], 532 | "source": [] 533 | } 534 | ], 535 | "metadata": { 536 | "kernelspec": { 537 | "display_name": "Python 3", 538 | "language": "python", 539 | "name": "python3" 540 | }, 541 | "language_info": { 542 | "codemirror_mode": { 543 | "name": "ipython", 544 | "version": 3 545 | }, 546 | "file_extension": ".py", 547 | "mimetype": "text/x-python", 548 | "name": "python", 549 | "nbconvert_exporter": "python", 550 | "pygments_lexer": "ipython3", 551 | "version": "3.7.7" 552 | } 553 | }, 554 | "nbformat": 4, 555 | "nbformat_minor": 4 556 | } 557 | -------------------------------------------------------------------------------- /A - Python Basics Review.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\n", 8 | " \n", 13 | "
\n", 9 | " \n", 10 | " \"Open\n", 11 | " \n", 12 | "
" 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": {}, 19 | "source": [ 20 | "#
**Neural Networks** (MFN0824)
" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": {}, 26 | "source": [ 27 | "### Lectures: _Dr. Matteo Osella_\n", 28 | "### Notebooks: _Francesco Mottes_\n", 29 | "
\n", 30 | "\n", 31 | "---" 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": {}, 37 | "source": [ 38 | "The material presented in the following is not meant to be a complete introduction to the Python programming language or to programming in general. The purpose of this notebook is to review some basic functionalities of the Python3 language, as well as some of the libraries that are most often used in Machine Learning and scientific applications in general. In particular, we will try to show many of the functionalities that will be needed for the practicals of the Neural Networks course.\n", 39 | "\n", 40 | "Further in-depth information, as well as more comprehensive introductions, can be found online quite easily. Below is a (short and incomplete) list of references that can guide further research into the presented topics. Other relevant references will be given when needed." 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "### Python references:\n", 48 | "- Python programming introductory book: Think Python (FREE: https://greenteapress.com/wp/think-python-2e/)\n", 49 | "- Python3 Docs: https://docs.python.org/3/\n", 50 | "- Python stilistic guide: https://www.python.org/dev/peps/pep-0008/\n", 51 | "\n", 52 | "### Python modules references:\n", 53 | "- Numpy Docs: https://numpy.org/\n", 54 | "- Matplotlib Docs: https://matplotlib.org/\n", 55 | "- Scikit-learn Docs: https://scikit-learn.org/" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "#
**A - Python Basics Review**
" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "From Wikipedia:\n", 70 | "\n", 71 | ">

Python is an interpreted, high-level, general-purpose programming language. Created by Guido van Rossum and first released in 1991, Python's design philosophy emphasizes code readability with its notable use of significant whitespace. Its language constructs and object-oriented approach aim to help programmers write clear, logical code for small and large-scale projects.[28]

\n", 72 | ">\n", 73 | ">

Python is dynamically typed and garbage-collected. It supports multiple programming paradigms, including structured (particularly, procedural,) object-oriented, and functional programming. Python is often described as a \"batteries included\" language due to its comprehensive standard library.[29]

\n", 74 | "> \n", 75 | ">

Python was conceived in the late 1980s as a successor to the ABC language. Python 2.0, released in 2000, introduced features like list comprehensions and a garbage collection system capable of collecting reference cycles. Python 3.0, released in 2008, was a major revision of the language that is not completely backward-compatible, and much Python 2 code does not run unmodified on Python 3.

\n", 76 | ">\n", 77 | ">

The Python 2 language, i.e. Python 2.7.x, was officially discontinued on 1 January 2020 (first planned for 2015) after which security patches and other improvements will not be released for it.[30][31] With Python 2's end-of-life, only Python 3.5.x[32] and later are supported.

" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": {}, 83 | "source": [ 84 | "The first important thing to know about Python is that it is an **interpreted** language. This means that Python code does not need to be compiled into machine code in order to run, but there is another pre-compiled program (called, without much surprise, the **interpreter**) that directly executes the Python code on the machine, line by line. Such a feature, as always, has pros and cons: absence of the compiling step allows python to be an **interactive** language, but also makes it much slower to run with respect to compiled languages (e.g. C/C++). Another consequence of the interpreted nature of Python code is that many errors that in other languages are caught by the compiler, such as undefined variables, get thrown out by python directly at runtime, when met. Thus debugging in Python is made easier by the interactive neature of the language (we can run the code line-by-line to see what's wrong), but slower (errors show up one at a time and we must re-run the whole code each time we fix them, in order to see if another error pops up).\n", 85 | "\n", 86 | "The first bit of Python syntax to be known is that the interpreter will ignore all the lines in the code that start with the `#` symbol. We can than use it to insert **comments** in our code which improve human readability and makes debugging easier, especially in complex projects." 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "## **Variables and Fundamental Types**" 94 | ] 95 | }, 96 | { 97 | "cell_type": "markdown", 98 | "metadata": {}, 99 | "source": [ 100 | "Variables are, quite like in algebra, placeholders. Like algebraic variables, a pyhton variable can take any of the permitted values values (whatever they may be), but unlike in algebra variables are assigned one specific value at any moment in time. They can be of different types and, unlike in other lower-level languages such as C/C++, they must not be declared before usage. The type of the variable is decded by the Python interpreter based on the value it takes.\n", 101 | "\n", 102 | "For more information on Python built-in types: https://docs.python.org/3/library/stdtypes.html.\n", 103 | "\n", 104 | "Let's see some examples." 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 5, 110 | "metadata": {}, 111 | "outputs": [ 112 | { 113 | "data": { 114 | "text/plain": [ 115 | "1" 116 | ] 117 | }, 118 | "execution_count": 5, 119 | "metadata": {}, 120 | "output_type": "execute_result" 121 | } 122 | ], 123 | "source": [ 124 | "# declare a variable named \"var\" and assign the value 1 to it\n", 125 | "# NOTE: the = operator always works right to left!\n", 126 | "# That is, takes whatever value is on the right and assigns it to whatever is on the left!\n", 127 | "var = 1\n", 128 | "var" 129 | ] 130 | }, 131 | { 132 | "cell_type": "markdown", 133 | "metadata": {}, 134 | "source": [ 135 | "Jupyter automatically prints variables found without any operations on the last line of the cell. Things can be explicitely printed on screen also using the ``` print()``` function." 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": 6, 141 | "metadata": {}, 142 | "outputs": [ 143 | { 144 | "name": "stdout", 145 | "output_type": "stream", 146 | "text": [ 147 | "1\n" 148 | ] 149 | } 150 | ], 151 | "source": [ 152 | "print(var)" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": 2, 158 | "metadata": {}, 159 | "outputs": [ 160 | { 161 | "data": { 162 | "text/plain": [ 163 | "int" 164 | ] 165 | }, 166 | "execution_count": 2, 167 | "metadata": {}, 168 | "output_type": "execute_result" 169 | } 170 | ], 171 | "source": [ 172 | "# check type of variable \"var\":\n", 173 | "type(var)" 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": 4, 179 | "metadata": {}, 180 | "outputs": [ 181 | { 182 | "name": "stdout", 183 | "output_type": "stream", 184 | "text": [ 185 | "2.6\n" 186 | ] 187 | }, 188 | { 189 | "data": { 190 | "text/plain": [ 191 | "float" 192 | ] 193 | }, 194 | "execution_count": 4, 195 | "metadata": {}, 196 | "output_type": "execute_result" 197 | } 198 | ], 199 | "source": [ 200 | "#now assign a real value to the same variable and check value and type\n", 201 | "var = 2.6\n", 202 | "print(var)\n", 203 | "type(var)" 204 | ] 205 | }, 206 | { 207 | "cell_type": "markdown", 208 | "metadata": {}, 209 | "source": [ 210 | "### int type" 211 | ] 212 | }, 213 | { 214 | "cell_type": "code", 215 | "execution_count": 23, 216 | "metadata": {}, 217 | "outputs": [ 218 | { 219 | "data": { 220 | "text/plain": [ 221 | "int" 222 | ] 223 | }, 224 | "execution_count": 23, 225 | "metadata": {}, 226 | "output_type": "execute_result" 227 | } 228 | ], 229 | "source": [ 230 | "var = -2\n", 231 | "type(var)" 232 | ] 233 | }, 234 | { 235 | "cell_type": "markdown", 236 | "metadata": {}, 237 | "source": [ 238 | "### float type" 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": 15, 244 | "metadata": {}, 245 | "outputs": [ 246 | { 247 | "data": { 248 | "text/plain": [ 249 | "float" 250 | ] 251 | }, 252 | "execution_count": 15, 253 | "metadata": {}, 254 | "output_type": "execute_result" 255 | } 256 | ], 257 | "source": [ 258 | "var = 2.1\n", 259 | "type(var)" 260 | ] 261 | }, 262 | { 263 | "cell_type": "markdown", 264 | "metadata": {}, 265 | "source": [ 266 | "### complex type" 267 | ] 268 | }, 269 | { 270 | "cell_type": "code", 271 | "execution_count": 16, 272 | "metadata": {}, 273 | "outputs": [ 274 | { 275 | "data": { 276 | "text/plain": [ 277 | "complex" 278 | ] 279 | }, 280 | "execution_count": 16, 281 | "metadata": {}, 282 | "output_type": "execute_result" 283 | } 284 | ], 285 | "source": [ 286 | "# YOU WILL NOT USE THIS TYPE IN THE COURSE\n", 287 | "var = 2 + 3j\n", 288 | "type(var)" 289 | ] 290 | }, 291 | { 292 | "cell_type": "markdown", 293 | "metadata": {}, 294 | "source": [ 295 | "### str type\n", 296 | "\n", 297 | "string is actually a somewhat more refined data type, with specific possible operations \"attached\" to it. Will be presented in more detail in the following section." 298 | ] 299 | }, 300 | { 301 | "cell_type": "code", 302 | "execution_count": 17, 303 | "metadata": {}, 304 | "outputs": [ 305 | { 306 | "data": { 307 | "text/plain": [ 308 | "str" 309 | ] 310 | }, 311 | "execution_count": 17, 312 | "metadata": {}, 313 | "output_type": "execute_result" 314 | } 315 | ], 316 | "source": [ 317 | "var = 'hello'\n", 318 | "type(var)" 319 | ] 320 | }, 321 | { 322 | "cell_type": "markdown", 323 | "metadata": {}, 324 | "source": [ 325 | "### bool type" 326 | ] 327 | }, 328 | { 329 | "cell_type": "code", 330 | "execution_count": 13, 331 | "metadata": {}, 332 | "outputs": [ 333 | { 334 | "data": { 335 | "text/plain": [ 336 | "bool" 337 | ] 338 | }, 339 | "execution_count": 13, 340 | "metadata": {}, 341 | "output_type": "execute_result" 342 | } 343 | ], 344 | "source": [ 345 | "var = True # or False\n", 346 | "type(var)" 347 | ] 348 | }, 349 | { 350 | "cell_type": "markdown", 351 | "metadata": {}, 352 | "source": [ 353 | "### type casting" 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": 61, 359 | "metadata": {}, 360 | "outputs": [ 361 | { 362 | "data": { 363 | "text/plain": [ 364 | "5" 365 | ] 366 | }, 367 | "execution_count": 61, 368 | "metadata": {}, 369 | "output_type": "execute_result" 370 | } 371 | ], 372 | "source": [ 373 | "var = 5.7\n", 374 | "int(var)" 375 | ] 376 | }, 377 | { 378 | "cell_type": "code", 379 | "execution_count": 62, 380 | "metadata": {}, 381 | "outputs": [ 382 | { 383 | "data": { 384 | "text/plain": [ 385 | "'5.7'" 386 | ] 387 | }, 388 | "execution_count": 62, 389 | "metadata": {}, 390 | "output_type": "execute_result" 391 | } 392 | ], 393 | "source": [ 394 | "str(var)" 395 | ] 396 | }, 397 | { 398 | "cell_type": "code", 399 | "execution_count": 66, 400 | "metadata": {}, 401 | "outputs": [ 402 | { 403 | "data": { 404 | "text/plain": [ 405 | "(True, True, False)" 406 | ] 407 | }, 408 | "execution_count": 66, 409 | "metadata": {}, 410 | "output_type": "execute_result" 411 | } 412 | ], 413 | "source": [ 414 | "# NOTE: all values different from 0 are assigned to the True boolean value\n", 415 | "bool(var), bool(-3), bool(0)" 416 | ] 417 | }, 418 | { 419 | "cell_type": "markdown", 420 | "metadata": {}, 421 | "source": [ 422 | "## **Operators**" 423 | ] 424 | }, 425 | { 426 | "cell_type": "markdown", 427 | "metadata": {}, 428 | "source": [ 429 | "Operators take the values that are given to them (either \"raw\" values or assigned to a named variable) and return the result of the operation." 430 | ] 431 | }, 432 | { 433 | "cell_type": "markdown", 434 | "metadata": {}, 435 | "source": [ 436 | "* Arithmetic operators: `+`, `-`, `*`, `/`, `//` (integer division), `**` (power)" 437 | ] 438 | }, 439 | { 440 | "cell_type": "code", 441 | "execution_count": 28, 442 | "metadata": {}, 443 | "outputs": [ 444 | { 445 | "data": { 446 | "text/plain": [ 447 | "10" 448 | ] 449 | }, 450 | "execution_count": 28, 451 | "metadata": {}, 452 | "output_type": "execute_result" 453 | } 454 | ], 455 | "source": [ 456 | "var = 2\n", 457 | "5*var" 458 | ] 459 | }, 460 | { 461 | "cell_type": "code", 462 | "execution_count": 29, 463 | "metadata": {}, 464 | "outputs": [ 465 | { 466 | "data": { 467 | "text/plain": [ 468 | "13.2" 469 | ] 470 | }, 471 | "execution_count": 29, 472 | "metadata": {}, 473 | "output_type": "execute_result" 474 | } 475 | ], 476 | "source": [ 477 | "var2 = 7.2\n", 478 | "3*var + var2" 479 | ] 480 | }, 481 | { 482 | "cell_type": "markdown", 483 | "metadata": {}, 484 | "source": [ 485 | "The result of arithmetic operations can be assigned to a (new or old) variable:" 486 | ] 487 | }, 488 | { 489 | "cell_type": "code", 490 | "execution_count": 36, 491 | "metadata": {}, 492 | "outputs": [ 493 | { 494 | "data": { 495 | "text/plain": [ 496 | "-6.4" 497 | ] 498 | }, 499 | "execution_count": 36, 500 | "metadata": {}, 501 | "output_type": "execute_result" 502 | } 503 | ], 504 | "source": [ 505 | "var3 = var2/2-5*var\n", 506 | "var3" 507 | ] 508 | }, 509 | { 510 | "cell_type": "code", 511 | "execution_count": 37, 512 | "metadata": {}, 513 | "outputs": [ 514 | { 515 | "data": { 516 | "text/plain": [ 517 | "-4.4" 518 | ] 519 | }, 520 | "execution_count": 37, 521 | "metadata": {}, 522 | "output_type": "execute_result" 523 | } 524 | ], 525 | "source": [ 526 | "# increase var3 by 2\n", 527 | "var3 = var3 + 2\n", 528 | "var3" 529 | ] 530 | }, 531 | { 532 | "cell_type": "code", 533 | "execution_count": 38, 534 | "metadata": {}, 535 | "outputs": [ 536 | { 537 | "data": { 538 | "text/plain": [ 539 | "-6.4" 540 | ] 541 | }, 542 | "execution_count": 38, 543 | "metadata": {}, 544 | "output_type": "execute_result" 545 | } 546 | ], 547 | "source": [ 548 | "#decrease var3 by 2\n", 549 | "#NOTE THE USE OF THE MORE COINCISE SYNTAX: this can be done with all operators\n", 550 | "var3 -= 2\n", 551 | "var3" 552 | ] 553 | }, 554 | { 555 | "cell_type": "code", 556 | "execution_count": 43, 557 | "metadata": {}, 558 | "outputs": [ 559 | { 560 | "data": { 561 | "text/plain": [ 562 | "1.35" 563 | ] 564 | }, 565 | "execution_count": 43, 566 | "metadata": {}, 567 | "output_type": "execute_result" 568 | } 569 | ], 570 | "source": [ 571 | "var = 2.7\n", 572 | "var/2" 573 | ] 574 | }, 575 | { 576 | "cell_type": "code", 577 | "execution_count": 44, 578 | "metadata": {}, 579 | "outputs": [ 580 | { 581 | "data": { 582 | "text/plain": [ 583 | "1.0" 584 | ] 585 | }, 586 | "execution_count": 44, 587 | "metadata": {}, 588 | "output_type": "execute_result" 589 | } 590 | ], 591 | "source": [ 592 | "#INTEGER DIVISION\n", 593 | "var//2" 594 | ] 595 | }, 596 | { 597 | "cell_type": "code", 598 | "execution_count": 47, 599 | "metadata": {}, 600 | "outputs": [ 601 | { 602 | "data": { 603 | "text/plain": [ 604 | "0.7000000000000002" 605 | ] 606 | }, 607 | "execution_count": 47, 608 | "metadata": {}, 609 | "output_type": "execute_result" 610 | } 611 | ], 612 | "source": [ 613 | "# MODULO operator finds the remainder of the integer division\n", 614 | "var % 2" 615 | ] 616 | }, 617 | { 618 | "cell_type": "code", 619 | "execution_count": 48, 620 | "metadata": {}, 621 | "outputs": [ 622 | { 623 | "data": { 624 | "text/plain": [ 625 | "9" 626 | ] 627 | }, 628 | "execution_count": 48, 629 | "metadata": {}, 630 | "output_type": "execute_result" 631 | } 632 | ], 633 | "source": [ 634 | "#POWER operator\n", 635 | "3**2" 636 | ] 637 | }, 638 | { 639 | "cell_type": "code", 640 | "execution_count": 50, 641 | "metadata": {}, 642 | "outputs": [ 643 | { 644 | "data": { 645 | "text/plain": [ 646 | "3.0" 647 | ] 648 | }, 649 | "execution_count": 50, 650 | "metadata": {}, 651 | "output_type": "execute_result" 652 | } 653 | ], 654 | "source": [ 655 | "#can be use also to find roots:\n", 656 | "9**.5\n", 657 | "\n", 658 | "#NOTE: there is no out-of-the box implementation of the sqrt function in plain python other than this one!!\n", 659 | "# (you would need to import the \"math\" module to have one)" 660 | ] 661 | }, 662 | { 663 | "cell_type": "markdown", 664 | "metadata": {}, 665 | "source": [ 666 | "* Boolean operators: `and`, `not`, `or`" 667 | ] 668 | }, 669 | { 670 | "cell_type": "code", 671 | "execution_count": 51, 672 | "metadata": {}, 673 | "outputs": [ 674 | { 675 | "data": { 676 | "text/plain": [ 677 | "False" 678 | ] 679 | }, 680 | "execution_count": 51, 681 | "metadata": {}, 682 | "output_type": "execute_result" 683 | } 684 | ], 685 | "source": [ 686 | "True and False" 687 | ] 688 | }, 689 | { 690 | "cell_type": "code", 691 | "execution_count": 52, 692 | "metadata": {}, 693 | "outputs": [ 694 | { 695 | "data": { 696 | "text/plain": [ 697 | "True" 698 | ] 699 | }, 700 | "execution_count": 52, 701 | "metadata": {}, 702 | "output_type": "execute_result" 703 | } 704 | ], 705 | "source": [ 706 | "True or False" 707 | ] 708 | }, 709 | { 710 | "cell_type": "code", 711 | "execution_count": 53, 712 | "metadata": {}, 713 | "outputs": [ 714 | { 715 | "data": { 716 | "text/plain": [ 717 | "False" 718 | ] 719 | }, 720 | "execution_count": 53, 721 | "metadata": {}, 722 | "output_type": "execute_result" 723 | } 724 | ], 725 | "source": [ 726 | "not True" 727 | ] 728 | }, 729 | { 730 | "cell_type": "code", 731 | "execution_count": 55, 732 | "metadata": {}, 733 | "outputs": [ 734 | { 735 | "data": { 736 | "text/plain": [ 737 | "(True, True)" 738 | ] 739 | }, 740 | "execution_count": 55, 741 | "metadata": {}, 742 | "output_type": "execute_result" 743 | } 744 | ], 745 | "source": [ 746 | "var = False\n", 747 | "not var, var or True" 748 | ] 749 | }, 750 | { 751 | "cell_type": "markdown", 752 | "metadata": {}, 753 | "source": [ 754 | "* Comparison operators: `>`, `<`, `>=` (greater or equal), `<=` (less or equal), `==` equality, `!=` inequality" 755 | ] 756 | }, 757 | { 758 | "cell_type": "code", 759 | "execution_count": 56, 760 | "metadata": {}, 761 | "outputs": [ 762 | { 763 | "data": { 764 | "text/plain": [ 765 | "(False, True)" 766 | ] 767 | }, 768 | "execution_count": 56, 769 | "metadata": {}, 770 | "output_type": "execute_result" 771 | } 772 | ], 773 | "source": [ 774 | "2 > 2.0, 2 > 1" 775 | ] 776 | }, 777 | { 778 | "cell_type": "code", 779 | "execution_count": 57, 780 | "metadata": {}, 781 | "outputs": [ 782 | { 783 | "data": { 784 | "text/plain": [ 785 | "(True, False, True)" 786 | ] 787 | }, 788 | "execution_count": 57, 789 | "metadata": {}, 790 | "output_type": "execute_result" 791 | } 792 | ], 793 | "source": [ 794 | "var = 3\n", 795 | "\n", 796 | "3 == var, var < 3, var <= 3" 797 | ] 798 | }, 799 | { 800 | "cell_type": "code", 801 | "execution_count": 59, 802 | "metadata": {}, 803 | "outputs": [ 804 | { 805 | "data": { 806 | "text/plain": [ 807 | "True" 808 | ] 809 | }, 810 | "execution_count": 59, 811 | "metadata": {}, 812 | "output_type": "execute_result" 813 | } 814 | ], 815 | "source": [ 816 | "var = 3\n", 817 | "var2 = 3.0\n", 818 | "\n", 819 | "var == var2" 820 | ] 821 | }, 822 | { 823 | "cell_type": "code", 824 | "execution_count": 67, 825 | "metadata": {}, 826 | "outputs": [ 827 | { 828 | "data": { 829 | "text/plain": [ 830 | "(True, True)" 831 | ] 832 | }, 833 | "execution_count": 67, 834 | "metadata": {}, 835 | "output_type": "execute_result" 836 | } 837 | ], 838 | "source": [ 839 | "var != 5, var != 3" 840 | ] 841 | }, 842 | { 843 | "cell_type": "markdown", 844 | "metadata": {}, 845 | "source": [ 846 | "## **Compound Types**" 847 | ] 848 | }, 849 | { 850 | "cell_type": "markdown", 851 | "metadata": {}, 852 | "source": [ 853 | "### Strings" 854 | ] 855 | }, 856 | { 857 | "cell_type": "markdown", 858 | "metadata": {}, 859 | "source": [ 860 | "Python does not have a `char` data type to store single characters, all text data in python is represented using strings. Strings (as everything in python, to be precise) are actually objects. The meaning of this statement will become clearer later on, for now it is sufficient to know that objects have special functions (*methods*) attached to them. Such functions act on what is \"inside\" the object in manners that are specific to the object in consideration. To give an example, strings have a method that transforms all the letteres in the string to uppercase, but it wouldn't make sense if also an object representing an integer number had the same method (what is an UPPERCASE INTEGER?).\n", 861 | "\n", 862 | "Methods are usually not needed for simple data types, but become more and more important as the ojects to which they are attached grow more and more complex. We will now make some examples of operations that can be done with strings, some of which will use string-specific methods." 863 | ] 864 | }, 865 | { 866 | "cell_type": "code", 867 | "execution_count": 115, 868 | "metadata": {}, 869 | "outputs": [ 870 | { 871 | "data": { 872 | "text/plain": [ 873 | "'Hello everyone, I am a string.'" 874 | ] 875 | }, 876 | "execution_count": 115, 877 | "metadata": {}, 878 | "output_type": "execute_result" 879 | } 880 | ], 881 | "source": [ 882 | "text = 'Hello everyone, I am a string.' # equivalently: \"Hello everyone, I am a string\"\n", 883 | "# 'text' is now a string object!\n", 884 | "\n", 885 | "text" 886 | ] 887 | }, 888 | { 889 | "cell_type": "code", 890 | "execution_count": 116, 891 | "metadata": {}, 892 | "outputs": [ 893 | { 894 | "data": { 895 | "text/plain": [ 896 | "30" 897 | ] 898 | }, 899 | "execution_count": 116, 900 | "metadata": {}, 901 | "output_type": "execute_result" 902 | } 903 | ], 904 | "source": [ 905 | "#number of chars in the text string\n", 906 | "len(text)" 907 | ] 908 | }, 909 | { 910 | "cell_type": "code", 911 | "execution_count": 106, 912 | "metadata": {}, 913 | "outputs": [ 914 | { 915 | "data": { 916 | "text/plain": [ 917 | "'Hello everyone, I am a string. Nice to meet you!'" 918 | ] 919 | }, 920 | "execution_count": 106, 921 | "metadata": {}, 922 | "output_type": "execute_result" 923 | } 924 | ], 925 | "source": [ 926 | "#strings can be added:\n", 927 | "text = text + \" Nice to meet you!\"\n", 928 | "\n", 929 | "text" 930 | ] 931 | }, 932 | { 933 | "cell_type": "code", 934 | "execution_count": 80, 935 | "metadata": {}, 936 | "outputs": [ 937 | { 938 | "name": "stdout", 939 | "output_type": "stream", 940 | "text": [ 941 | "HELLO EVERYONE, I AM A STRING. NICE TO MEET YOU!\n", 942 | "hello everyone, i am a string. nice to meet you!\n" 943 | ] 944 | } 945 | ], 946 | "source": [ 947 | "# .upper() and .lower() string methods\n", 948 | "\n", 949 | "#methods are called by appending a . after the object name\n", 950 | "print(text.upper())\n", 951 | "print(text.lower())" 952 | ] 953 | }, 954 | { 955 | "cell_type": "code", 956 | "execution_count": 94, 957 | "metadata": {}, 958 | "outputs": [ 959 | { 960 | "data": { 961 | "text/plain": [ 962 | "['Hello', 'everyone,', 'I', 'am', 'a', 'string.', 'Nice', 'to', 'meet', 'you!']" 963 | ] 964 | }, 965 | "execution_count": 94, 966 | "metadata": {}, 967 | "output_type": "execute_result" 968 | } 969 | ], 970 | "source": [ 971 | "#split strings when a space character is found\n", 972 | "text.split()\n", 973 | "#NOTE: this method returns an object of type list (we will see it next)" 974 | ] 975 | }, 976 | { 977 | "cell_type": "code", 978 | "execution_count": 95, 979 | "metadata": {}, 980 | "outputs": [ 981 | { 982 | "data": { 983 | "text/plain": [ 984 | "['H', 'llo ', 'v', 'ryon', ', I am a string. Nic', ' to m', '', 't you!']" 985 | ] 986 | }, 987 | "execution_count": 95, 988 | "metadata": {}, 989 | "output_type": "execute_result" 990 | } 991 | ], 992 | "source": [ 993 | "#split strings when a \"e\" character is found\n", 994 | "text.split('e')" 995 | ] 996 | }, 997 | { 998 | "cell_type": "markdown", 999 | "metadata": {}, 1000 | "source": [ 1001 | "* INDEXING:" 1002 | ] 1003 | }, 1004 | { 1005 | "cell_type": "code", 1006 | "execution_count": 82, 1007 | "metadata": {}, 1008 | "outputs": [ 1009 | { 1010 | "data": { 1011 | "text/plain": [ 1012 | "'H'" 1013 | ] 1014 | }, 1015 | "execution_count": 82, 1016 | "metadata": {}, 1017 | "output_type": "execute_result" 1018 | } 1019 | ], 1020 | "source": [ 1021 | "#string elements can be accessed by INDEXING:\n", 1022 | "\n", 1023 | "text[0]\n", 1024 | "\n", 1025 | "# NOTE: INDEXING IN PYTHON STARTS FROM 0!" 1026 | ] 1027 | }, 1028 | { 1029 | "cell_type": "code", 1030 | "execution_count": 84, 1031 | "metadata": {}, 1032 | "outputs": [ 1033 | { 1034 | "data": { 1035 | "text/plain": [ 1036 | "'Hel'" 1037 | ] 1038 | }, 1039 | "execution_count": 84, 1040 | "metadata": {}, 1041 | "output_type": "execute_result" 1042 | } 1043 | ], 1044 | "source": [ 1045 | "# SLICING\n", 1046 | "\n", 1047 | "text[:3]\n", 1048 | "#access all the letters in the string up to index of value 3 (excluded)" 1049 | ] 1050 | }, 1051 | { 1052 | "cell_type": "code", 1053 | "execution_count": 86, 1054 | "metadata": {}, 1055 | "outputs": [ 1056 | { 1057 | "name": "stdout", 1058 | "output_type": "stream", 1059 | "text": [ 1060 | "llo \n", 1061 | "yone, I am a string. Nice to meet you!\n" 1062 | ] 1063 | } 1064 | ], 1065 | "source": [ 1066 | "# in a similar fashion:\n", 1067 | "print(text[2:6])\n", 1068 | "print(text[10:])" 1069 | ] 1070 | }, 1071 | { 1072 | "cell_type": "code", 1073 | "execution_count": 90, 1074 | "metadata": {}, 1075 | "outputs": [ 1076 | { 1077 | "name": "stdout", 1078 | "output_type": "stream", 1079 | "text": [ 1080 | "to meet you\n" 1081 | ] 1082 | } 1083 | ], 1084 | "source": [ 1085 | "# inexing can also be used backwards:\n", 1086 | "print(text[-12:-1])" 1087 | ] 1088 | }, 1089 | { 1090 | "cell_type": "code", 1091 | "execution_count": 92, 1092 | "metadata": {}, 1093 | "outputs": [ 1094 | { 1095 | "name": "stdout", 1096 | "output_type": "stream", 1097 | "text": [ 1098 | "Hloeeyn,Ia tig iet etyu\n", 1099 | "l eo,\n" 1100 | ] 1101 | } 1102 | ], 1103 | "source": [ 1104 | "# indexing step size:\n", 1105 | "\n", 1106 | "#print from index 0 till the end with step size = 2\n", 1107 | "print(text[::2])\n", 1108 | "\n", 1109 | "#print from index 2 to index 17, with step size = 3\n", 1110 | "print(text[2:17:3])" 1111 | ] 1112 | }, 1113 | { 1114 | "cell_type": "markdown", 1115 | "metadata": {}, 1116 | "source": [ 1117 | "* FORMATTING:" 1118 | ] 1119 | }, 1120 | { 1121 | "cell_type": "code", 1122 | "execution_count": 97, 1123 | "metadata": {}, 1124 | "outputs": [ 1125 | { 1126 | "data": { 1127 | "text/plain": [ 1128 | "'Hello, I am Paul.'" 1129 | ] 1130 | }, 1131 | "execution_count": 97, 1132 | "metadata": {}, 1133 | "output_type": "execute_result" 1134 | } 1135 | ], 1136 | "source": [ 1137 | "name = 'Paul'\n", 1138 | "\n", 1139 | "'Hello, I am {}.'.format(name)" 1140 | ] 1141 | }, 1142 | { 1143 | "cell_type": "code", 1144 | "execution_count": 99, 1145 | "metadata": {}, 1146 | "outputs": [ 1147 | { 1148 | "data": { 1149 | "text/plain": [ 1150 | "'I am Paul, this is Jane'" 1151 | ] 1152 | }, 1153 | "execution_count": 99, 1154 | "metadata": {}, 1155 | "output_type": "execute_result" 1156 | } 1157 | ], 1158 | "source": [ 1159 | "name2 = 'Jane'\n", 1160 | "text = 'I am {}, this is {}'\n", 1161 | "\n", 1162 | "text.format(name,name2)" 1163 | ] 1164 | }, 1165 | { 1166 | "cell_type": "code", 1167 | "execution_count": 101, 1168 | "metadata": {}, 1169 | "outputs": [ 1170 | { 1171 | "data": { 1172 | "text/plain": [ 1173 | "'I am Jane, this is Paul'" 1174 | ] 1175 | }, 1176 | "execution_count": 101, 1177 | "metadata": {}, 1178 | "output_type": "execute_result" 1179 | } 1180 | ], 1181 | "source": [ 1182 | "text = 'I am {1}, this is {0}'\n", 1183 | "\n", 1184 | "text.format(name,name2)" 1185 | ] 1186 | }, 1187 | { 1188 | "cell_type": "code", 1189 | "execution_count": 104, 1190 | "metadata": {}, 1191 | "outputs": [ 1192 | { 1193 | "data": { 1194 | "text/plain": [ 1195 | "'I am Paul, this is Jane and she is 20.57 years old.'" 1196 | ] 1197 | }, 1198 | "execution_count": 104, 1199 | "metadata": {}, 1200 | "output_type": "execute_result" 1201 | } 1202 | ], 1203 | "source": [ 1204 | "age = 20.5674009\n", 1205 | "\n", 1206 | "text = 'I am {}, this is {} and she is {:.2f} years old.'\n", 1207 | "\n", 1208 | "text.format(name,name2,age)" 1209 | ] 1210 | }, 1211 | { 1212 | "cell_type": "markdown", 1213 | "metadata": {}, 1214 | "source": [ 1215 | "### Lists" 1216 | ] 1217 | }, 1218 | { 1219 | "cell_type": "markdown", 1220 | "metadata": {}, 1221 | "source": [ 1222 | "Lists are collections of Python objects, pretty much as strings are collectiong of single text characters. They work in the same way for what indexing is concerned, but they have some extra features. Objects inside lists can be of any kind, not just characters, and each object can be of a different kind. Ojects inside the list can also be inserted, removed and changed at will, once the list has been created. Lists can also be nested, meaning that one list can contain one or more other lists, with no limits." 1223 | ] 1224 | }, 1225 | { 1226 | "cell_type": "code", 1227 | "execution_count": 118, 1228 | "metadata": {}, 1229 | "outputs": [ 1230 | { 1231 | "data": { 1232 | "text/plain": [ 1233 | "[1, 'hey', 3.5, (3-9j)]" 1234 | ] 1235 | }, 1236 | "execution_count": 118, 1237 | "metadata": {}, 1238 | "output_type": "execute_result" 1239 | } 1240 | ], 1241 | "source": [ 1242 | "# create a list object\n", 1243 | "l = [1, 'hey', 3.5, 3-9j]\n", 1244 | "l" 1245 | ] 1246 | }, 1247 | { 1248 | "cell_type": "code", 1249 | "execution_count": 119, 1250 | "metadata": {}, 1251 | "outputs": [ 1252 | { 1253 | "data": { 1254 | "text/plain": [ 1255 | "4" 1256 | ] 1257 | }, 1258 | "execution_count": 119, 1259 | "metadata": {}, 1260 | "output_type": "execute_result" 1261 | } 1262 | ], 1263 | "source": [ 1264 | "len(l)" 1265 | ] 1266 | }, 1267 | { 1268 | "cell_type": "code", 1269 | "execution_count": 120, 1270 | "metadata": {}, 1271 | "outputs": [ 1272 | { 1273 | "data": { 1274 | "text/plain": [ 1275 | "'hey'" 1276 | ] 1277 | }, 1278 | "execution_count": 120, 1279 | "metadata": {}, 1280 | "output_type": "execute_result" 1281 | } 1282 | ], 1283 | "source": [ 1284 | "l[1]" 1285 | ] 1286 | }, 1287 | { 1288 | "cell_type": "code", 1289 | "execution_count": 121, 1290 | "metadata": {}, 1291 | "outputs": [ 1292 | { 1293 | "data": { 1294 | "text/plain": [ 1295 | "[1, 'hey', 3.5, 9.21]" 1296 | ] 1297 | }, 1298 | "execution_count": 121, 1299 | "metadata": {}, 1300 | "output_type": "execute_result" 1301 | } 1302 | ], 1303 | "source": [ 1304 | "#change last element of the list\n", 1305 | "l[-1] = 9.21\n", 1306 | "l" 1307 | ] 1308 | }, 1309 | { 1310 | "cell_type": "code", 1311 | "execution_count": 122, 1312 | "metadata": {}, 1313 | "outputs": [ 1314 | { 1315 | "data": { 1316 | "text/plain": [ 1317 | "[1, 'hey', 3.5, 9.21, 'hello']" 1318 | ] 1319 | }, 1320 | "execution_count": 122, 1321 | "metadata": {}, 1322 | "output_type": "execute_result" 1323 | } 1324 | ], 1325 | "source": [ 1326 | "#add an element at the end of the list\n", 1327 | "l.append('hello')\n", 1328 | "l" 1329 | ] 1330 | }, 1331 | { 1332 | "cell_type": "code", 1333 | "execution_count": 123, 1334 | "metadata": {}, 1335 | "outputs": [ 1336 | { 1337 | "data": { 1338 | "text/plain": [ 1339 | "[1, 'hey', 3.5, 9.21, 'hello', 'a', 7]" 1340 | ] 1341 | }, 1342 | "execution_count": 123, 1343 | "metadata": {}, 1344 | "output_type": "execute_result" 1345 | } 1346 | ], 1347 | "source": [ 1348 | "#extend a list with elements of another list\n", 1349 | "l.extend(['a',7])\n", 1350 | "l" 1351 | ] 1352 | }, 1353 | { 1354 | "cell_type": "code", 1355 | "execution_count": 124, 1356 | "metadata": {}, 1357 | "outputs": [ 1358 | { 1359 | "data": { 1360 | "text/plain": [ 1361 | "[1, 'hey', 3.5, 9.21, 'hello', 'a', 7, ['a', 7]]" 1362 | ] 1363 | }, 1364 | "execution_count": 124, 1365 | "metadata": {}, 1366 | "output_type": "execute_result" 1367 | } 1368 | ], 1369 | "source": [ 1370 | "#What happens if I use .append() method instead?\n", 1371 | "l.append(['a',7])\n", 1372 | "l" 1373 | ] 1374 | }, 1375 | { 1376 | "cell_type": "code", 1377 | "execution_count": 125, 1378 | "metadata": {}, 1379 | "outputs": [ 1380 | { 1381 | "data": { 1382 | "text/plain": [ 1383 | "'a'" 1384 | ] 1385 | }, 1386 | "execution_count": 125, 1387 | "metadata": {}, 1388 | "output_type": "execute_result" 1389 | } 1390 | ], 1391 | "source": [ 1392 | "#access first element of the nested list:\n", 1393 | "l[-1][0]" 1394 | ] 1395 | }, 1396 | { 1397 | "cell_type": "markdown", 1398 | "metadata": {}, 1399 | "source": [ 1400 | "### Tuples" 1401 | ] 1402 | }, 1403 | { 1404 | "cell_type": "markdown", 1405 | "metadata": {}, 1406 | "source": [ 1407 | "Tuples, for all our purposes, can be regarded pretty much as lists that CANNOT BE CHANGED. Once a tuple has been created you cannot add, eliminate or change the ojects inside it." 1408 | ] 1409 | }, 1410 | { 1411 | "cell_type": "code", 1412 | "execution_count": 126, 1413 | "metadata": {}, 1414 | "outputs": [ 1415 | { 1416 | "data": { 1417 | "text/plain": [ 1418 | "(1, 'hey', 3.5, (3-9j))" 1419 | ] 1420 | }, 1421 | "execution_count": 126, 1422 | "metadata": {}, 1423 | "output_type": "execute_result" 1424 | } 1425 | ], 1426 | "source": [ 1427 | "#create a tuple\n", 1428 | "t = (1, 'hey', 3.5, 3-9j)\n", 1429 | "t" 1430 | ] 1431 | }, 1432 | { 1433 | "cell_type": "code", 1434 | "execution_count": 127, 1435 | "metadata": {}, 1436 | "outputs": [ 1437 | { 1438 | "data": { 1439 | "text/plain": [ 1440 | "4" 1441 | ] 1442 | }, 1443 | "execution_count": 127, 1444 | "metadata": {}, 1445 | "output_type": "execute_result" 1446 | } 1447 | ], 1448 | "source": [ 1449 | "len(t)" 1450 | ] 1451 | }, 1452 | { 1453 | "cell_type": "code", 1454 | "execution_count": 129, 1455 | "metadata": {}, 1456 | "outputs": [ 1457 | { 1458 | "ename": "TypeError", 1459 | "evalue": "'tuple' object does not support item assignment", 1460 | "output_type": "error", 1461 | "traceback": [ 1462 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 1463 | "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", 1464 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m#If we tru to assing a value to a member of a tuple, python throws an error (EXCEPTION)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mt\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 1465 | "\u001b[0;31mTypeError\u001b[0m: 'tuple' object does not support item assignment" 1466 | ] 1467 | } 1468 | ], 1469 | "source": [ 1470 | "#If we tru to assing a value to a member of a tuple, python throws an error (EXCEPTION)\n", 1471 | "t[2] = 3" 1472 | ] 1473 | }, 1474 | { 1475 | "cell_type": "markdown", 1476 | "metadata": {}, 1477 | "source": [ 1478 | "**NOTE**: tuples can be **UNPACKED**, meaning that the object they contain can be assigned quickly to other variables, and it can be done all at once!" 1479 | ] 1480 | }, 1481 | { 1482 | "cell_type": "code", 1483 | "execution_count": 128, 1484 | "metadata": {}, 1485 | "outputs": [ 1486 | { 1487 | "name": "stdout", 1488 | "output_type": "stream", 1489 | "text": [ 1490 | "(1, 'hey', 3.5, (3-9j))\n", 1491 | "1 hey 3.5 (3-9j)\n" 1492 | ] 1493 | } 1494 | ], 1495 | "source": [ 1496 | "#tuple unpacking:\n", 1497 | "v1, v2, v3, v4 = t\n", 1498 | "\n", 1499 | "#equivalent to:\n", 1500 | "#v1 = t[0]\n", 1501 | "#v2 = t[1]\n", 1502 | "#...\n", 1503 | "\n", 1504 | "print(t)\n", 1505 | "print(v1, v2, v3, v4)" 1506 | ] 1507 | }, 1508 | { 1509 | "cell_type": "markdown", 1510 | "metadata": {}, 1511 | "source": [ 1512 | "### Dictionaries" 1513 | ] 1514 | }, 1515 | { 1516 | "cell_type": "markdown", 1517 | "metadata": {}, 1518 | "source": [ 1519 | "Dictionaries are like lists (the are MUTABLE), except for the fact that the index of an object is not just an integer number anymore. You can store an object (**value**) into a dictionary and specify another (IMMUTABLE) object (**key**) that represents the location where you stored your value. In essence, a dictionary is a collection of key-value pairs, where the values can be accessed specifying their attached key.\n", 1520 | "\n", 1521 | "Dictionaries have pro and cons: they take up more memory to be stored with respect to a list, but accessing an element in a dictionary is faster than accessing a list member by index. Moreover, dictionaries are usually more human-readable if the keys have been chosen in a sensible manner." 1522 | ] 1523 | }, 1524 | { 1525 | "cell_type": "code", 1526 | "execution_count": 132, 1527 | "metadata": {}, 1528 | "outputs": [ 1529 | { 1530 | "data": { 1531 | "text/plain": [ 1532 | "{'a': 1, 'b': 2, 7.5: ['c', 'd'], (1, 5): 'hello'}" 1533 | ] 1534 | }, 1535 | "execution_count": 132, 1536 | "metadata": {}, 1537 | "output_type": "execute_result" 1538 | } 1539 | ], 1540 | "source": [ 1541 | "#create a dictionary\n", 1542 | "d = {'a':1, 'b':2, 7.5:['c','d'], (1,5):'hello'}\n", 1543 | "d" 1544 | ] 1545 | }, 1546 | { 1547 | "cell_type": "code", 1548 | "execution_count": 143, 1549 | "metadata": {}, 1550 | "outputs": [ 1551 | { 1552 | "data": { 1553 | "text/plain": [ 1554 | "{'a': 1, 'b': 2, 7.5: ['c', 'd'], (1, 5): 'hello'}" 1555 | ] 1556 | }, 1557 | "execution_count": 143, 1558 | "metadata": {}, 1559 | "output_type": "execute_result" 1560 | } 1561 | ], 1562 | "source": [ 1563 | "#create a dictionary - other way\n", 1564 | "d = dict([['a',1], ['b',2], [7.5,['c','d']], [(1,5),'hello']])\n", 1565 | "d" 1566 | ] 1567 | }, 1568 | { 1569 | "cell_type": "code", 1570 | "execution_count": 144, 1571 | "metadata": {}, 1572 | "outputs": [ 1573 | { 1574 | "name": "stdout", 1575 | "output_type": "stream", 1576 | "text": [ 1577 | "1\n", 1578 | "['c', 'd']\n", 1579 | "hello\n" 1580 | ] 1581 | } 1582 | ], 1583 | "source": [ 1584 | "#access dict elements\n", 1585 | "print(d['a'])\n", 1586 | "print(d[7.5])\n", 1587 | "print(d[(1,5)])" 1588 | ] 1589 | }, 1590 | { 1591 | "cell_type": "code", 1592 | "execution_count": 145, 1593 | "metadata": {}, 1594 | "outputs": [ 1595 | { 1596 | "data": { 1597 | "text/plain": [ 1598 | "{'a': 1, 'b': 2, 7.5: ['c', 'd'], (1, 5): 'hello', 5: 3}" 1599 | ] 1600 | }, 1601 | "execution_count": 145, 1602 | "metadata": {}, 1603 | "output_type": "execute_result" 1604 | } 1605 | ], 1606 | "source": [ 1607 | "#add dict element\n", 1608 | "d[5] = 3\n", 1609 | "d" 1610 | ] 1611 | }, 1612 | { 1613 | "cell_type": "code", 1614 | "execution_count": 146, 1615 | "metadata": {}, 1616 | "outputs": [ 1617 | { 1618 | "data": { 1619 | "text/plain": [ 1620 | "['a', 'b', 7.5, (1, 5), 5]" 1621 | ] 1622 | }, 1623 | "execution_count": 146, 1624 | "metadata": {}, 1625 | "output_type": "execute_result" 1626 | } 1627 | ], 1628 | "source": [ 1629 | "#list of dict keys\n", 1630 | "list(d.keys())" 1631 | ] 1632 | }, 1633 | { 1634 | "cell_type": "code", 1635 | "execution_count": 147, 1636 | "metadata": {}, 1637 | "outputs": [ 1638 | { 1639 | "data": { 1640 | "text/plain": [ 1641 | "[1, 2, ['c', 'd'], 'hello', 3]" 1642 | ] 1643 | }, 1644 | "execution_count": 147, 1645 | "metadata": {}, 1646 | "output_type": "execute_result" 1647 | } 1648 | ], 1649 | "source": [ 1650 | "#list of dict values\n", 1651 | "list(d.values())" 1652 | ] 1653 | }, 1654 | { 1655 | "cell_type": "code", 1656 | "execution_count": 148, 1657 | "metadata": {}, 1658 | "outputs": [ 1659 | { 1660 | "data": { 1661 | "text/plain": [ 1662 | "[('a', 1), ('b', 2), (7.5, ['c', 'd']), ((1, 5), 'hello'), (5, 3)]" 1663 | ] 1664 | }, 1665 | "execution_count": 148, 1666 | "metadata": {}, 1667 | "output_type": "execute_result" 1668 | } 1669 | ], 1670 | "source": [ 1671 | "#list of dict (key,value) pairs\n", 1672 | "list(d.items())" 1673 | ] 1674 | }, 1675 | { 1676 | "cell_type": "code", 1677 | "execution_count": 149, 1678 | "metadata": {}, 1679 | "outputs": [ 1680 | { 1681 | "data": { 1682 | "text/plain": [ 1683 | "{'a': 1, 'b': 2, 7.5: ['c', 'd'], (1, 5): 'hello', 5: 3, 's': 3, 'ggg': 9}" 1684 | ] 1685 | }, 1686 | "execution_count": 149, 1687 | "metadata": {}, 1688 | "output_type": "execute_result" 1689 | } 1690 | ], 1691 | "source": [ 1692 | "#add elements of one dict to another\n", 1693 | "d1 = dict([('s',3),('ggg',9)])\n", 1694 | "\n", 1695 | "d.update(d1)\n", 1696 | "\n", 1697 | "d" 1698 | ] 1699 | }, 1700 | { 1701 | "cell_type": "markdown", 1702 | "metadata": {}, 1703 | "source": [ 1704 | "## **Flow Control**" 1705 | ] 1706 | }, 1707 | { 1708 | "cell_type": "markdown", 1709 | "metadata": {}, 1710 | "source": [ 1711 | "### if ... elif ... else" 1712 | ] 1713 | }, 1714 | { 1715 | "cell_type": "code", 1716 | "execution_count": 150, 1717 | "metadata": {}, 1718 | "outputs": [ 1719 | { 1720 | "name": "stdout", 1721 | "output_type": "stream", 1722 | "text": [ 1723 | "statement1 and statement2 are False\n" 1724 | ] 1725 | } 1726 | ], 1727 | "source": [ 1728 | "statement1 = False\n", 1729 | "statement2 = False\n", 1730 | "\n", 1731 | "if statement1:\n", 1732 | " print(\"statement1 is True\")\n", 1733 | " \n", 1734 | "elif statement2:\n", 1735 | " print(\"statement2 is True\")\n", 1736 | " \n", 1737 | "else:\n", 1738 | " print(\"statement1 and statement2 are False\")" 1739 | ] 1740 | }, 1741 | { 1742 | "cell_type": "markdown", 1743 | "metadata": {}, 1744 | "source": [ 1745 | "**NOTE**: program blocks are defined based on their **indentation level**! This is not only for visualization purposes, in Python it is **compulsory** to indent different blocks of code by multiples of the same amount of white spaces (usually one *tab* or *4 blanks*)." 1746 | ] 1747 | }, 1748 | { 1749 | "cell_type": "code", 1750 | "execution_count": 155, 1751 | "metadata": {}, 1752 | "outputs": [ 1753 | { 1754 | "data": { 1755 | "text/plain": [ 1756 | "(False, True)" 1757 | ] 1758 | }, 1759 | "execution_count": 155, 1760 | "metadata": {}, 1761 | "output_type": "execute_result" 1762 | } 1763 | ], 1764 | "source": [ 1765 | "#remember that comparison operator will return a boolean value:\n", 1766 | "2 == 5, 3 > 2" 1767 | ] 1768 | }, 1769 | { 1770 | "cell_type": "code", 1771 | "execution_count": 156, 1772 | "metadata": {}, 1773 | "outputs": [ 1774 | { 1775 | "name": "stdout", 1776 | "output_type": "stream", 1777 | "text": [ 1778 | "var = 5 > 0\n" 1779 | ] 1780 | } 1781 | ], 1782 | "source": [ 1783 | "#check if var is >, < or = 0\n", 1784 | "var = 5\n", 1785 | "\n", 1786 | "if var < 0:\n", 1787 | " print('var = {} < 0'.format(var))\n", 1788 | "\n", 1789 | "elif 0 == var: #good practice, in order to avoid bugs given by writing = instead of ==\n", 1790 | " print('var = 0')\n", 1791 | " \n", 1792 | "else: # var > 0\n", 1793 | " print('var = {} > 0'.format(var))" 1794 | ] 1795 | }, 1796 | { 1797 | "cell_type": "markdown", 1798 | "metadata": {}, 1799 | "source": [ 1800 | "Try different values of var!" 1801 | ] 1802 | }, 1803 | { 1804 | "cell_type": "markdown", 1805 | "metadata": {}, 1806 | "source": [ 1807 | "## **Looping**" 1808 | ] 1809 | }, 1810 | { 1811 | "cell_type": "markdown", 1812 | "metadata": {}, 1813 | "source": [ 1814 | "Loops allow us to run the same portion of code for a number of times (that can be known *a priori* or specified at runtime)." 1815 | ] 1816 | }, 1817 | { 1818 | "cell_type": "markdown", 1819 | "metadata": {}, 1820 | "source": [ 1821 | "### `for` loops\n", 1822 | "\n", 1823 | "In Python, looping is allowed with any type of *iterable* object. This means we can loop not only on the value of an index, but also on the elements of a list, of a tupe, of a dictionary or even characters in a string." 1824 | ] 1825 | }, 1826 | { 1827 | "cell_type": "code", 1828 | "execution_count": 161, 1829 | "metadata": {}, 1830 | "outputs": [ 1831 | { 1832 | "name": "stdout", 1833 | "output_type": "stream", 1834 | "text": [ 1835 | "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n", 1836 | "[5, 6, 7, 8, 9]\n", 1837 | "[2, 5, 8]\n" 1838 | ] 1839 | } 1840 | ], 1841 | "source": [ 1842 | "#range creates an iterator (NOTE: it is different from an iterable, but it's a technical subtlety) on the given indices\n", 1843 | "print(list(range(10)))\n", 1844 | "print(list(range(5,10)))\n", 1845 | "print(list(range(2,10,3)))" 1846 | ] 1847 | }, 1848 | { 1849 | "cell_type": "code", 1850 | "execution_count": 162, 1851 | "metadata": {}, 1852 | "outputs": [ 1853 | { 1854 | "name": "stdout", 1855 | "output_type": "stream", 1856 | "text": [ 1857 | "Hello, I am number 0\n", 1858 | "Hello, I am number 1\n", 1859 | "Hello, I am number 2\n", 1860 | "Hello, I am number 3\n" 1861 | ] 1862 | } 1863 | ], 1864 | "source": [ 1865 | "#looping in indices:\n", 1866 | "for i in range(4):\n", 1867 | " print('Hello, I am number {}'.format(i))" 1868 | ] 1869 | }, 1870 | { 1871 | "cell_type": "code", 1872 | "execution_count": 163, 1873 | "metadata": {}, 1874 | "outputs": [ 1875 | { 1876 | "name": "stdout", 1877 | "output_type": "stream", 1878 | "text": [ 1879 | "hey\n", 1880 | "6\n", 1881 | "[1, 2]\n" 1882 | ] 1883 | } 1884 | ], 1885 | "source": [ 1886 | "#looping on a list (or tuple)\n", 1887 | "l = ['hey', 6, [1,2]]\n", 1888 | "\n", 1889 | "for item in l:\n", 1890 | " print(item)" 1891 | ] 1892 | }, 1893 | { 1894 | "cell_type": "code", 1895 | "execution_count": 165, 1896 | "metadata": {}, 1897 | "outputs": [ 1898 | { 1899 | "name": "stdout", 1900 | "output_type": "stream", 1901 | "text": [ 1902 | "a 1\n", 1903 | "b 2\n", 1904 | "c 3\n" 1905 | ] 1906 | } 1907 | ], 1908 | "source": [ 1909 | "#looping on a dictionary\n", 1910 | "d = dict([('a',1),('b',2),('c',3)])\n", 1911 | "\n", 1912 | "for key in d:\n", 1913 | " print(key, d[key])" 1914 | ] 1915 | }, 1916 | { 1917 | "cell_type": "code", 1918 | "execution_count": 166, 1919 | "metadata": {}, 1920 | "outputs": [ 1921 | { 1922 | "name": "stdout", 1923 | "output_type": "stream", 1924 | "text": [ 1925 | "0 hey\n", 1926 | "1 6\n", 1927 | "2 [1, 2]\n" 1928 | ] 1929 | } 1930 | ], 1931 | "source": [ 1932 | "#adding the indexing to an iterable\n", 1933 | "l = ['hey', 6, [1,2]]\n", 1934 | "\n", 1935 | "for i, item in enumerate(l):\n", 1936 | " print(i, item)" 1937 | ] 1938 | }, 1939 | { 1940 | "cell_type": "markdown", 1941 | "metadata": {}, 1942 | "source": [ 1943 | "* LIST COMPREHENSION:\n", 1944 | " \n", 1945 | "The for loop syntax can also be used to create a list in a faster and more coincise way:" 1946 | ] 1947 | }, 1948 | { 1949 | "cell_type": "code", 1950 | "execution_count": 167, 1951 | "metadata": {}, 1952 | "outputs": [ 1953 | { 1954 | "data": { 1955 | "text/plain": [ 1956 | "['a0', 'a1', 'a2', 'a3', 'a4']" 1957 | ] 1958 | }, 1959 | "execution_count": 167, 1960 | "metadata": {}, 1961 | "output_type": "execute_result" 1962 | } 1963 | ], 1964 | "source": [ 1965 | "l = ['a'+str(i) for i in range(5)]\n", 1966 | "l" 1967 | ] 1968 | }, 1969 | { 1970 | "cell_type": "code", 1971 | "execution_count": 168, 1972 | "metadata": {}, 1973 | "outputs": [ 1974 | { 1975 | "data": { 1976 | "text/plain": [ 1977 | "['a0', 'a1', 'a2', 'a3', 'a4']" 1978 | ] 1979 | }, 1980 | "execution_count": 168, 1981 | "metadata": {}, 1982 | "output_type": "execute_result" 1983 | } 1984 | ], 1985 | "source": [ 1986 | "# same result as:\n", 1987 | "l = []\n", 1988 | "for i in range(5):\n", 1989 | " l.append('a'+str(i))\n", 1990 | " \n", 1991 | "l" 1992 | ] 1993 | }, 1994 | { 1995 | "cell_type": "code", 1996 | "execution_count": 169, 1997 | "metadata": {}, 1998 | "outputs": [ 1999 | { 2000 | "data": { 2001 | "text/plain": [ 2002 | "[0, 1, 4, 9, 16]" 2003 | ] 2004 | }, 2005 | "execution_count": 169, 2006 | "metadata": {}, 2007 | "output_type": "execute_result" 2008 | } 2009 | ], 2010 | "source": [ 2011 | "#anothe example:\n", 2012 | "l = [x**2 for x in range(5)]\n", 2013 | "l" 2014 | ] 2015 | }, 2016 | { 2017 | "cell_type": "markdown", 2018 | "metadata": {}, 2019 | "source": [ 2020 | "### `while` loops\n", 2021 | "\n", 2022 | "Differently from for loops, while loops will continue running until the specified condition becomes False." 2023 | ] 2024 | }, 2025 | { 2026 | "cell_type": "code", 2027 | "execution_count": 171, 2028 | "metadata": {}, 2029 | "outputs": [ 2030 | { 2031 | "name": "stdout", 2032 | "output_type": "stream", 2033 | "text": [ 2034 | "Another loop!\n", 2035 | "Another loop!\n", 2036 | "Another loop!\n", 2037 | "Loop ended finally!\n" 2038 | ] 2039 | } 2040 | ], 2041 | "source": [ 2042 | "a = 0\n", 2043 | "\n", 2044 | "while a < 3:\n", 2045 | " print('Another loop!')\n", 2046 | " a += 1\n", 2047 | " \n", 2048 | "print('Loop ended finally!')" 2049 | ] 2050 | }, 2051 | { 2052 | "cell_type": "markdown", 2053 | "metadata": {}, 2054 | "source": [ 2055 | "## **Exceptions**" 2056 | ] 2057 | }, 2058 | { 2059 | "cell_type": "markdown", 2060 | "metadata": {}, 2061 | "source": [ 2062 | "Exceptions are Python error. They are thrown by the python interpreter at runtime when something goes wrong and they stop the running of the program. Exceptions can be **caught** (that is, handled and managed) with a special construct, if we don't want our code to stop unexpectedly. Exceptions can also be **raised** intentionally, if our code ends up unexpectedly in the wrong section of the program." 2063 | ] 2064 | }, 2065 | { 2066 | "cell_type": "code", 2067 | "execution_count": 4, 2068 | "metadata": {}, 2069 | "outputs": [ 2070 | { 2071 | "ename": "NameError", 2072 | "evalue": "name 'hello' is not defined", 2073 | "output_type": "error", 2074 | "traceback": [ 2075 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 2076 | "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", 2077 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# exception gets thrown if we try to use an undefined variable\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mhello\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 2078 | "\u001b[0;31mNameError\u001b[0m: name 'hello' is not defined" 2079 | ] 2080 | } 2081 | ], 2082 | "source": [ 2083 | "# an exception gets thrown if we try to use an undefined variable\n", 2084 | "hello*10" 2085 | ] 2086 | }, 2087 | { 2088 | "cell_type": "code", 2089 | "execution_count": 5, 2090 | "metadata": {}, 2091 | "outputs": [ 2092 | { 2093 | "name": "stdout", 2094 | "output_type": "stream", 2095 | "text": [ 2096 | "Exception caught!\n" 2097 | ] 2098 | } 2099 | ], 2100 | "source": [ 2101 | "# we can catch any exception thrown by the code\n", 2102 | "try:\n", 2103 | " hello*10\n", 2104 | "except:\n", 2105 | " print('Exception caught!')" 2106 | ] 2107 | }, 2108 | { 2109 | "cell_type": "code", 2110 | "execution_count": 6, 2111 | "metadata": {}, 2112 | "outputs": [ 2113 | { 2114 | "name": "stdout", 2115 | "output_type": "stream", 2116 | "text": [ 2117 | "Exception caught!\n", 2118 | "name 'hello' is not defined\n" 2119 | ] 2120 | } 2121 | ], 2122 | "source": [ 2123 | "#we can catch only a specific exception, all the others will block the program\n", 2124 | "#we can also get the message associated with the error with the 'as' clause\n", 2125 | "try:\n", 2126 | " hello*10\n", 2127 | "except NameError as err:\n", 2128 | " print('Exception caught!')\n", 2129 | " print(err)" 2130 | ] 2131 | }, 2132 | { 2133 | "cell_type": "code", 2134 | "execution_count": 8, 2135 | "metadata": {}, 2136 | "outputs": [ 2137 | { 2138 | "ename": "ValueError", 2139 | "evalue": "Wrong value!", 2140 | "output_type": "error", 2141 | "traceback": [ 2142 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 2143 | "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", 2144 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m#we can also raise a pre-existing exception type or a custom one\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Wrong value!'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 2145 | "\u001b[0;31mValueError\u001b[0m: Wrong value!" 2146 | ] 2147 | } 2148 | ], 2149 | "source": [ 2150 | "#we can also raise a pre-existing exception type or a custom one\n", 2151 | "raise ValueError('Wrong value!')" 2152 | ] 2153 | }, 2154 | { 2155 | "cell_type": "code", 2156 | "execution_count": 10, 2157 | "metadata": {}, 2158 | "outputs": [ 2159 | { 2160 | "ename": "Exception", 2161 | "evalue": "Wrong anything!", 2162 | "output_type": "error", 2163 | "traceback": [ 2164 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 2165 | "\u001b[0;31mException\u001b[0m Traceback (most recent call last)", 2166 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m#we can also raise a pre-existing exception type or a custom one\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Wrong anything!'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 2167 | "\u001b[0;31mException\u001b[0m: Wrong anything!" 2168 | ] 2169 | } 2170 | ], 2171 | "source": [ 2172 | "#we can also raise a pre-existing exception type or a custom one\n", 2173 | "raise Exception('Wrong anything!')" 2174 | ] 2175 | }, 2176 | { 2177 | "cell_type": "markdown", 2178 | "metadata": {}, 2179 | "source": [ 2180 | "## **Functions**" 2181 | ] 2182 | }, 2183 | { 2184 | "cell_type": "markdown", 2185 | "metadata": {}, 2186 | "source": [ 2187 | "A function in Python is defined using the keyword `def`, followed by a function name, a signature within parentheses `()`, and a colon `:`. The code that follows, with one additional level of indentation, is the function body. Once defined, functions can be called simply by their name and passing the right arguments." 2188 | ] 2189 | }, 2190 | { 2191 | "cell_type": "code", 2192 | "execution_count": 11, 2193 | "metadata": {}, 2194 | "outputs": [ 2195 | { 2196 | "name": "stdout", 2197 | "output_type": "stream", 2198 | "text": [ 2199 | "Hello!\n" 2200 | ] 2201 | } 2202 | ], 2203 | "source": [ 2204 | "#define a function that prints some text\n", 2205 | "\n", 2206 | "def my_function():\n", 2207 | " \n", 2208 | " print('Hello!')\n", 2209 | " \n", 2210 | " \n", 2211 | "my_function()" 2212 | ] 2213 | }, 2214 | { 2215 | "cell_type": "code", 2216 | "execution_count": 14, 2217 | "metadata": {}, 2218 | "outputs": [ 2219 | { 2220 | "name": "stdout", 2221 | "output_type": "stream", 2222 | "text": [ 2223 | "Hello! I am Jane.\n", 2224 | "Hello! I am Paul.\n", 2225 | "Hello! I am 3.\n" 2226 | ] 2227 | } 2228 | ], 2229 | "source": [ 2230 | "#define a function that prints some text\n", 2231 | "#with one argument\n", 2232 | "\n", 2233 | "def my_function(name):\n", 2234 | " \n", 2235 | " print('Hello! I am {}.'.format(str(name)))\n", 2236 | " \n", 2237 | " \n", 2238 | "my_function('Jane')\n", 2239 | "my_function('Paul')\n", 2240 | "my_function(3)" 2241 | ] 2242 | }, 2243 | { 2244 | "cell_type": "code", 2245 | "execution_count": 16, 2246 | "metadata": {}, 2247 | "outputs": [ 2248 | { 2249 | "name": "stdout", 2250 | "output_type": "stream", 2251 | "text": [ 2252 | "3.0\n" 2253 | ] 2254 | } 2255 | ], 2256 | "source": [ 2257 | "# functions can also give back the result of some internal computation\n", 2258 | "\n", 2259 | "def sqrt(number):\n", 2260 | " \n", 2261 | " return number**.5\n", 2262 | "\n", 2263 | "var = sqrt(9)\n", 2264 | "print(var)" 2265 | ] 2266 | }, 2267 | { 2268 | "cell_type": "markdown", 2269 | "metadata": {}, 2270 | "source": [ 2271 | "It is good practice to endow functions with a docstring that explain what they do, the meaning of the arguments and what they return." 2272 | ] 2273 | }, 2274 | { 2275 | "cell_type": "code", 2276 | "execution_count": 17, 2277 | "metadata": {}, 2278 | "outputs": [], 2279 | "source": [ 2280 | "def sqrt(number):\n", 2281 | " '''\n", 2282 | " Calculates square root of a number.\n", 2283 | " \n", 2284 | " Paramaters:\n", 2285 | " -----------\n", 2286 | " \n", 2287 | " name : int or float\n", 2288 | " Number the sqrt of which will be calculated.\n", 2289 | " \n", 2290 | " \n", 2291 | " Returns:\n", 2292 | " ---------\n", 2293 | " float : square root of the argument\n", 2294 | " \n", 2295 | " '''\n", 2296 | " \n", 2297 | " return number**.5" 2298 | ] 2299 | }, 2300 | { 2301 | "cell_type": "code", 2302 | "execution_count": 18, 2303 | "metadata": {}, 2304 | "outputs": [ 2305 | { 2306 | "name": "stdout", 2307 | "output_type": "stream", 2308 | "text": [ 2309 | "Help on function sqrt in module __main__:\n", 2310 | "\n", 2311 | "sqrt(number)\n", 2312 | " Calculates square root of a number.\n", 2313 | " \n", 2314 | " Paramaters:\n", 2315 | " -----------\n", 2316 | " \n", 2317 | " name : int or float\n", 2318 | " Number the sqrt of which will be calculated.\n", 2319 | " \n", 2320 | " \n", 2321 | " Returns:\n", 2322 | " ---------\n", 2323 | " float : square root of the argument\n", 2324 | "\n" 2325 | ] 2326 | } 2327 | ], 2328 | "source": [ 2329 | "#show docstring\n", 2330 | "help(sqrt)" 2331 | ] 2332 | }, 2333 | { 2334 | "cell_type": "code", 2335 | "execution_count": 19, 2336 | "metadata": {}, 2337 | "outputs": [ 2338 | { 2339 | "name": "stdout", 2340 | "output_type": "stream", 2341 | "text": [ 2342 | "\n", 2343 | "(3, [2], [9])\n", 2344 | "(3, [2, 3], [9, 27])\n" 2345 | ] 2346 | } 2347 | ], 2348 | "source": [ 2349 | "# default arguments and multiple return values\n", 2350 | "\n", 2351 | "def power(number, exponents=[2]):\n", 2352 | " '''\n", 2353 | " Calculates the specified powers of the given number.\n", 2354 | " '''\n", 2355 | " \n", 2356 | " powers = [number**e for e in exponents]\n", 2357 | " \n", 2358 | " return number, exponents, powers\n", 2359 | "\n", 2360 | "\n", 2361 | "print(type(power(3)))\n", 2362 | "print(power(3))\n", 2363 | "print(power(3,[2,3]))" 2364 | ] 2365 | }, 2366 | { 2367 | "cell_type": "code", 2368 | "execution_count": 20, 2369 | "metadata": {}, 2370 | "outputs": [ 2371 | { 2372 | "name": "stdout", 2373 | "output_type": "stream", 2374 | "text": [ 2375 | "3\n", 2376 | "[2, 3, 4]\n", 2377 | "[9, 27, 81]\n" 2378 | ] 2379 | } 2380 | ], 2381 | "source": [ 2382 | "#function results can be unpacked!\n", 2383 | "num, exponents, results = power(3,[2,3,4])\n", 2384 | "\n", 2385 | "print(num)\n", 2386 | "print(exponents)\n", 2387 | "print(results)" 2388 | ] 2389 | }, 2390 | { 2391 | "cell_type": "markdown", 2392 | "metadata": {}, 2393 | "source": [ 2394 | "## **Modules**" 2395 | ] 2396 | }, 2397 | { 2398 | "cell_type": "markdown", 2399 | "metadata": {}, 2400 | "source": [ 2401 | "Modules are external python files that can be imported and used in the code, without having to explicitely re-write all the code that they contain. Python modules usually contain the definition of useful functions or classes (see next section) that we want to use in the present code. Python comes with many pre-installed libraries that do a lot of useful things, if you installed Python with Anaconda the pre-installed libraries are even more." 2402 | ] 2403 | }, 2404 | { 2405 | "cell_type": "code", 2406 | "execution_count": 21, 2407 | "metadata": {}, 2408 | "outputs": [], 2409 | "source": [ 2410 | "# import the math module that contain some pre-implemented functions\n", 2411 | "import math" 2412 | ] 2413 | }, 2414 | { 2415 | "cell_type": "code", 2416 | "execution_count": 22, 2417 | "metadata": {}, 2418 | "outputs": [ 2419 | { 2420 | "name": "stdout", 2421 | "output_type": "stream", 2422 | "text": [ 2423 | "120\n", 2424 | "4.0\n", 2425 | "148.4131591025766\n" 2426 | ] 2427 | } 2428 | ], 2429 | "source": [ 2430 | "#now math functionalities are available for direct usage\n", 2431 | "\n", 2432 | "print(math.factorial(5))\n", 2433 | "print(math.sqrt(16))\n", 2434 | "print(math.exp(5))" 2435 | ] 2436 | }, 2437 | { 2438 | "cell_type": "code", 2439 | "execution_count": 23, 2440 | "metadata": {}, 2441 | "outputs": [ 2442 | { 2443 | "data": { 2444 | "text/plain": [ 2445 | "120" 2446 | ] 2447 | }, 2448 | "execution_count": 23, 2449 | "metadata": {}, 2450 | "output_type": "execute_result" 2451 | } 2452 | ], 2453 | "source": [ 2454 | "# modules can also be assigned a new name in the current environment, upon import\n", 2455 | "import math as m\n", 2456 | "\n", 2457 | "m.factorial(5)" 2458 | ] 2459 | }, 2460 | { 2461 | "cell_type": "code", 2462 | "execution_count": 24, 2463 | "metadata": {}, 2464 | "outputs": [ 2465 | { 2466 | "data": { 2467 | "text/plain": [ 2468 | "120" 2469 | ] 2470 | }, 2471 | "execution_count": 24, 2472 | "metadata": {}, 2473 | "output_type": "execute_result" 2474 | } 2475 | ], 2476 | "source": [ 2477 | "# we can also import specific functions\n", 2478 | "#this allows us to use the functions as if they were hardcoded in the notebook\n", 2479 | "\n", 2480 | "from math import factorial\n", 2481 | "\n", 2482 | "factorial(5)" 2483 | ] 2484 | }, 2485 | { 2486 | "cell_type": "code", 2487 | "execution_count": 25, 2488 | "metadata": {}, 2489 | "outputs": [ 2490 | { 2491 | "ename": "NameError", 2492 | "evalue": "name 'exp' is not defined", 2493 | "output_type": "error", 2494 | "traceback": [ 2495 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 2496 | "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", 2497 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# what happens if we try to use a function that was not imported?\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mexp\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 2498 | "\u001b[0;31mNameError\u001b[0m: name 'exp' is not defined" 2499 | ] 2500 | } 2501 | ], 2502 | "source": [ 2503 | "# what happens if we try to use a function that was not imported?\n", 2504 | "exp(5)" 2505 | ] 2506 | }, 2507 | { 2508 | "cell_type": "code", 2509 | "execution_count": 26, 2510 | "metadata": {}, 2511 | "outputs": [ 2512 | { 2513 | "data": { 2514 | "text/plain": [ 2515 | "148.4131591025766" 2516 | ] 2517 | }, 2518 | "execution_count": 26, 2519 | "metadata": {}, 2520 | "output_type": "execute_result" 2521 | } 2522 | ], 2523 | "source": [ 2524 | "# we can also import all what's inside a module at once\n", 2525 | "#this allows us to use the functions as if they were hardcoded in the notebook\n", 2526 | "\n", 2527 | "from math import *\n", 2528 | "\n", 2529 | "exp(5)" 2530 | ] 2531 | }, 2532 | { 2533 | "cell_type": "markdown", 2534 | "metadata": {}, 2535 | "source": [ 2536 | "You can also create your own modules in order to make the code cleaner and make it easier to re-use the code. See, for example, [this link](https://stackoverflow.com/questions/37072773/how-to-create-and-import-a-custom-module-in-python/37074372)." 2537 | ] 2538 | }, 2539 | { 2540 | "cell_type": "markdown", 2541 | "metadata": {}, 2542 | "source": [ 2543 | "## **Classes and Object-Oriented Programming**" 2544 | ] 2545 | }, 2546 | { 2547 | "cell_type": "markdown", 2548 | "metadata": {}, 2549 | "source": [ 2550 | "We will pretty much use ojects created by other people more than creating our own objects, but having a look at how they work is insightful, at least in order to know how to manipulate them correctly.\n", 2551 | "\n", 2552 | "Objects, roughly speaking, are entities that are endowed with variables that describe their internal state (**attributes**) and functions that act on that internal state (**methods**). Classes are the blueprints from which you can create many objects of the same kind, they describe which attributes they have and the way in which they can interact with their attributes and, often, with the outside environment.\n", 2553 | "\n", 2554 | "Objects behave pretty much as one would expect them to act in the real world. Let's see some examples." 2555 | ] 2556 | }, 2557 | { 2558 | "cell_type": "code", 2559 | "execution_count": 38, 2560 | "metadata": {}, 2561 | "outputs": [], 2562 | "source": [ 2563 | "#create the blueprint for all objects of type \"bicycle\"\n", 2564 | "#we build a simplified bycicle with only one attribute, the gear\n", 2565 | "\n", 2566 | "class Bicycle():\n", 2567 | " \n", 2568 | " #this function must always be present!\n", 2569 | " #gets called everytime we create (INSTANTIATE) a new object of type Bicycle\n", 2570 | " def __init__(self, name=None):\n", 2571 | " \n", 2572 | " self.name = name #give a name to our bike\n", 2573 | " self.gear = 1 #start always at the lowest gear\n", 2574 | " \n", 2575 | " def gear_up(self):\n", 2576 | " '''\n", 2577 | " Increment gear by 1.\n", 2578 | " '''\n", 2579 | " \n", 2580 | " self.gear += 1\n", 2581 | " \n", 2582 | " def gear_down(self):\n", 2583 | " '''\n", 2584 | " Decrement gear by 1.\n", 2585 | " '''\n", 2586 | " \n", 2587 | " self.gear -= 1\n", 2588 | " \n", 2589 | " def get_gear(self):\n", 2590 | " '''\n", 2591 | " Tells us which is the current gear.\n", 2592 | " '''\n", 2593 | " \n", 2594 | " return self.gear\n", 2595 | " \n", 2596 | " def get_name(self):\n", 2597 | " '''\n", 2598 | " Tells us the name of the bicycle.\n", 2599 | " '''\n", 2600 | " \n", 2601 | " return self.name" 2602 | ] 2603 | }, 2604 | { 2605 | "cell_type": "code", 2606 | "execution_count": 39, 2607 | "metadata": {}, 2608 | "outputs": [ 2609 | { 2610 | "name": "stdout", 2611 | "output_type": "stream", 2612 | "text": [ 2613 | "Larry\n", 2614 | "1\n" 2615 | ] 2616 | } 2617 | ], 2618 | "source": [ 2619 | "# now we can create a specific bicycle, named Larry\n", 2620 | "\n", 2621 | "larry_bike = Bicycle('Larry')\n", 2622 | "\n", 2623 | "#check we got Larry\n", 2624 | "print(larry_bike.get_name())\n", 2625 | "\n", 2626 | "#check we are in first gear\n", 2627 | "print(larry_bike.get_gear())" 2628 | ] 2629 | }, 2630 | { 2631 | "cell_type": "code", 2632 | "execution_count": 40, 2633 | "metadata": {}, 2634 | "outputs": [ 2635 | { 2636 | "name": "stdout", 2637 | "output_type": "stream", 2638 | "text": [ 2639 | "Larry\n", 2640 | "Paul\n" 2641 | ] 2642 | } 2643 | ], 2644 | "source": [ 2645 | "#There are no PRIVATE variables in Python, everything is always PUBLIC\n", 2646 | "#technically we could also do:\n", 2647 | "print(larry_bike.name)\n", 2648 | "\n", 2649 | "#and change name of bike from here\n", 2650 | "larry_bike.name = 'Paul'\n", 2651 | "print(larry_bike.get_name())" 2652 | ] 2653 | }, 2654 | { 2655 | "cell_type": "markdown", 2656 | "metadata": {}, 2657 | "source": [ 2658 | "It is highly recommended that you do not \"touch\" directly one object's attributes, but only through its implemented methods. Methods usually implement also a number of internal checks and logics that cannot be seen from the outside and messing with attributes, although always technically possible, may leave an object in an inconsistent state and cause malfunctions.\n", 2659 | "\n", 2660 | "For example, we could modify our bike to have gears only from 1 to 7, which is pretty sensible:" 2661 | ] 2662 | }, 2663 | { 2664 | "cell_type": "code", 2665 | "execution_count": 41, 2666 | "metadata": {}, 2667 | "outputs": [], 2668 | "source": [ 2669 | "#create the blueprint for all objects of type \"bicycle\"\n", 2670 | "#we build a simplified bycicle with only one attribute, the gear\n", 2671 | "\n", 2672 | "class Bicycle():\n", 2673 | " \n", 2674 | " #this function must always be present!\n", 2675 | " #gets called everytime we create (INSTANTIATE) a new object of type Bicycle\n", 2676 | " def __init__(self, name=None):\n", 2677 | " \n", 2678 | " self.name = name #give a name to our bike\n", 2679 | " self.gear = 1 #start always at the lowest gear\n", 2680 | "\n", 2681 | " \n", 2682 | " def gear_up(self):\n", 2683 | " '''\n", 2684 | " Increment gear by 1.\n", 2685 | " '''\n", 2686 | " \n", 2687 | " self.gear += 1\n", 2688 | " \n", 2689 | " if not (self.gear > 0 and self.gear <= 7):\n", 2690 | " self.gear -= 1\n", 2691 | " print('Gear out of bounds!')\n", 2692 | " \n", 2693 | " def gear_down(self):\n", 2694 | " '''\n", 2695 | " Decrement gear by 1.\n", 2696 | " '''\n", 2697 | " \n", 2698 | " self.gear -= 1\n", 2699 | " \n", 2700 | " if not (self.gear > 0 and self.gear <= 7):\n", 2701 | " self.gear += 1\n", 2702 | " print('Gear out of bounds!')\n", 2703 | " \n", 2704 | " def get_gear(self):\n", 2705 | " '''\n", 2706 | " Tells us which is the current gear.\n", 2707 | " '''\n", 2708 | " \n", 2709 | " return self.gear\n", 2710 | " \n", 2711 | " def get_name(self):\n", 2712 | " '''\n", 2713 | " Tells us the name of the bicycle.\n", 2714 | " '''\n", 2715 | " \n", 2716 | " return self.name" 2717 | ] 2718 | }, 2719 | { 2720 | "cell_type": "code", 2721 | "execution_count": 43, 2722 | "metadata": {}, 2723 | "outputs": [ 2724 | { 2725 | "name": "stdout", 2726 | "output_type": "stream", 2727 | "text": [ 2728 | "None\n", 2729 | "1\n" 2730 | ] 2731 | } 2732 | ], 2733 | "source": [ 2734 | "#create a new bike with no name\n", 2735 | "anon_bike = Bicycle()\n", 2736 | "\n", 2737 | "print(anon_bike.get_name())\n", 2738 | "print(anon_bike.get_gear())" 2739 | ] 2740 | }, 2741 | { 2742 | "cell_type": "code", 2743 | "execution_count": 44, 2744 | "metadata": {}, 2745 | "outputs": [ 2746 | { 2747 | "name": "stdout", 2748 | "output_type": "stream", 2749 | "text": [ 2750 | "Gear out of bounds!\n" 2751 | ] 2752 | } 2753 | ], 2754 | "source": [ 2755 | "#try to decrease gear\n", 2756 | "anon_bike.gear_down()" 2757 | ] 2758 | }, 2759 | { 2760 | "cell_type": "code", 2761 | "execution_count": 45, 2762 | "metadata": {}, 2763 | "outputs": [ 2764 | { 2765 | "data": { 2766 | "text/plain": [ 2767 | "-2" 2768 | ] 2769 | }, 2770 | "execution_count": 45, 2771 | "metadata": {}, 2772 | "output_type": "execute_result" 2773 | } 2774 | ], 2775 | "source": [ 2776 | "#everything works as expected as long as the object stays in the state in which it was build to stay\n", 2777 | "#if we mess up the attributes directly we can \"break\" the oject intended functionality\n", 2778 | "\n", 2779 | "anon_bike.gear = -2\n", 2780 | "\n", 2781 | "anon_bike.get_gear()" 2782 | ] 2783 | }, 2784 | { 2785 | "cell_type": "code", 2786 | "execution_count": 48, 2787 | "metadata": {}, 2788 | "outputs": [ 2789 | { 2790 | "name": "stdout", 2791 | "output_type": "stream", 2792 | "text": [ 2793 | "Gear out of bounds!\n", 2794 | "Gear out of bounds!\n", 2795 | "-2\n" 2796 | ] 2797 | } 2798 | ], 2799 | "source": [ 2800 | "#gear has now a meaningless value!\n", 2801 | "#even worse we now cannot change it anymore!!!\n", 2802 | "\n", 2803 | "anon_bike.gear_down()\n", 2804 | "anon_bike.gear_up()\n", 2805 | "\n", 2806 | "print(anon_bike.get_gear())" 2807 | ] 2808 | }, 2809 | { 2810 | "cell_type": "markdown", 2811 | "metadata": {}, 2812 | "source": [ 2813 | "## **Exercises**" 2814 | ] 2815 | }, 2816 | { 2817 | "cell_type": "markdown", 2818 | "metadata": {}, 2819 | "source": [ 2820 | "### Palindrome detector " 2821 | ] 2822 | }, 2823 | { 2824 | "cell_type": "code", 2825 | "execution_count": 50, 2826 | "metadata": {}, 2827 | "outputs": [], 2828 | "source": [ 2829 | "#write a function that detects if a string is a palindrome\n", 2830 | "#throw a ValueError exception if the argument is not a string\n", 2831 | "\n", 2832 | "def is_palindrome(string):\n", 2833 | " \n", 2834 | " #check argument type\n", 2835 | " if type(string) != str:\n", 2836 | " raise ValueError('Input is not a string!')\n", 2837 | " else:\n", 2838 | " is_pal = False\n", 2839 | " \n", 2840 | " # YOUR CODE\n", 2841 | " #check the string, change the value of is_pal on need\n", 2842 | " \n", 2843 | " return is_pal\n", 2844 | " " 2845 | ] 2846 | }, 2847 | { 2848 | "cell_type": "code", 2849 | "execution_count": null, 2850 | "metadata": {}, 2851 | "outputs": [], 2852 | "source": [ 2853 | "test_strings = ['aijija', 'ghihg', 'kfk sdfg']\n", 2854 | "\n", 2855 | "for string in test_strings:\n", 2856 | " print(string, is_palindrome(string))" 2857 | ] 2858 | }, 2859 | { 2860 | "cell_type": "markdown", 2861 | "metadata": {}, 2862 | "source": [ 2863 | "### Sorting" 2864 | ] 2865 | }, 2866 | { 2867 | "cell_type": "code", 2868 | "execution_count": null, 2869 | "metadata": {}, 2870 | "outputs": [], 2871 | "source": [ 2872 | "#write a function that takes as an input a list of numbers and returns a sorted version of the list\n", 2873 | "# try to figure out your own algorithm for sorting, but if you need help choose one of the (many) standard ways:\n", 2874 | "#https://en.wikipedia.org/wiki/Sorting_algorithm\n", 2875 | "\n", 2876 | "def sort(arg):\n", 2877 | " \n", 2878 | " #YOUR CODE\n", 2879 | " \n", 2880 | " \n", 2881 | " \n", 2882 | " return sorted_list\n", 2883 | "\n", 2884 | "#if you invented your own algorithm, chances are you re-invented an existing one\n", 2885 | "#check out the wikipedia page and try to figure out which category your algorithm falls into!" 2886 | ] 2887 | }, 2888 | { 2889 | "cell_type": "code", 2890 | "execution_count": 52, 2891 | "metadata": {}, 2892 | "outputs": [ 2893 | { 2894 | "data": { 2895 | "text/plain": [ 2896 | "[-3, 0, 2.5, 3, 7, 12]" 2897 | ] 2898 | }, 2899 | "execution_count": 52, 2900 | "metadata": {}, 2901 | "output_type": "execute_result" 2902 | } 2903 | ], 2904 | "source": [ 2905 | "#your function should return the same ordering of the python function sorted()\n", 2906 | "test_list = [0, 7, 3, 2.5, 12, -3]\n", 2907 | "\n", 2908 | "print(sorted(test_list))\n", 2909 | "print(sort(test_list))" 2910 | ] 2911 | }, 2912 | { 2913 | "cell_type": "markdown", 2914 | "metadata": {}, 2915 | "source": [ 2916 | "### Matrix transpose" 2917 | ] 2918 | }, 2919 | { 2920 | "cell_type": "code", 2921 | "execution_count": 56, 2922 | "metadata": {}, 2923 | "outputs": [], 2924 | "source": [ 2925 | "#you can represent a matrix as a list of lists\n", 2926 | "#write a function that takes a matrix as input and outputs its transpose\n", 2927 | "#check that the input is a valid matrix (only numbers, right dimensions)\n", 2928 | "\n", 2929 | "def transpose(A):\n", 2930 | " \n", 2931 | " # YOUR CODE (checks)\n", 2932 | " \n", 2933 | " A_transpose = []\n", 2934 | " \n", 2935 | " # YOUR CODE\n", 2936 | "\n", 2937 | " \n", 2938 | " return A_transpose" 2939 | ] 2940 | }, 2941 | { 2942 | "cell_type": "code", 2943 | "execution_count": 58, 2944 | "metadata": {}, 2945 | "outputs": [ 2946 | { 2947 | "name": "stdout", 2948 | "output_type": "stream", 2949 | "text": [ 2950 | "[1, 2, 3, 9]\n", 2951 | "[4, 5, 6, 9]\n", 2952 | "[7, 8, 9, 9]\n" 2953 | ] 2954 | } 2955 | ], 2956 | "source": [ 2957 | "def print_matrix(A):\n", 2958 | " \n", 2959 | " for row in A:\n", 2960 | " print(row)\n", 2961 | " \n", 2962 | "\n", 2963 | "test_matrix = [[1,2,3,9],[4,5,6,9],[7,8,9,9]]\n", 2964 | "\n", 2965 | "print_matrix(test_matrix)" 2966 | ] 2967 | }, 2968 | { 2969 | "cell_type": "code", 2970 | "execution_count": null, 2971 | "metadata": {}, 2972 | "outputs": [], 2973 | "source": [ 2974 | "print_matrix(transpose(test_matrix))" 2975 | ] 2976 | }, 2977 | { 2978 | "cell_type": "markdown", 2979 | "metadata": {}, 2980 | "source": [ 2981 | "### Matrix multiplication" 2982 | ] 2983 | }, 2984 | { 2985 | "cell_type": "code", 2986 | "execution_count": null, 2987 | "metadata": {}, 2988 | "outputs": [], 2989 | "source": [ 2990 | "#you can represent a matrix as a list of lists\n", 2991 | "#write a function that takes two matrices as input and outputs the product of the two\n", 2992 | "#check that the inputs are valid matrices (only numbers, right dimensions) and their dimensions are compatible\n", 2993 | "\n", 2994 | "def matmul(A,B):\n", 2995 | " \n", 2996 | " # YOUR CODE (checks)\n", 2997 | " \n", 2998 | " C = [] # C = A dot B\n", 2999 | " \n", 3000 | " # YOUR CODE\n", 3001 | "\n", 3002 | " \n", 3003 | " return C" 3004 | ] 3005 | }, 3006 | { 3007 | "cell_type": "code", 3008 | "execution_count": 59, 3009 | "metadata": {}, 3010 | "outputs": [ 3011 | { 3012 | "name": "stdout", 3013 | "output_type": "stream", 3014 | "text": [ 3015 | "[1, 2, 3, 9]\n", 3016 | "[4, 5, 6, 9]\n", 3017 | "[7, 8, 9, 9]\n", 3018 | "\n", 3019 | "[1, 2, 3, 4]\n", 3020 | "[5, 6, 7, 8]\n" 3021 | ] 3022 | } 3023 | ], 3024 | "source": [ 3025 | "A_test = [[1,2,3,9],[4,5,6,9],[7,8,9,9]]\n", 3026 | "B_test = [[1,2,3,4],[5,6,7,8]]\n", 3027 | "\n", 3028 | "print_matrix(A_test)\n", 3029 | "print()\n", 3030 | "print_matrix(B_test)" 3031 | ] 3032 | }, 3033 | { 3034 | "cell_type": "code", 3035 | "execution_count": null, 3036 | "metadata": {}, 3037 | "outputs": [], 3038 | "source": [ 3039 | "#transpose is the function you defined in the previous exercise\n", 3040 | "C = matmul(A_test,transpose(B_test))\n", 3041 | "\n", 3042 | "print_matrix(C)" 3043 | ] 3044 | } 3045 | ], 3046 | "metadata": { 3047 | "kernelspec": { 3048 | "display_name": "Python 3", 3049 | "language": "python", 3050 | "name": "python3" 3051 | }, 3052 | "language_info": { 3053 | "codemirror_mode": { 3054 | "name": "ipython", 3055 | "version": 3 3056 | }, 3057 | "file_extension": ".py", 3058 | "mimetype": "text/x-python", 3059 | "name": "python", 3060 | "nbconvert_exporter": "python", 3061 | "pygments_lexer": "ipython3", 3062 | "version": "3.6.10" 3063 | } 3064 | }, 3065 | "nbformat": 4, 3066 | "nbformat_minor": 4 3067 | } 3068 | --------------------------------------------------------------------------------