diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..763513e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.ipynb_checkpoints diff --git a/DefensiveProgramming.ipynb b/DefensiveProgramming.ipynb new file mode 100644 index 0000000..d3655ec --- /dev/null +++ b/DefensiveProgramming.ipynb @@ -0,0 +1,143 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " # Defensive programming (1)\n", + " \n", + " How much time do you spend writing software? How much time do you spend \n", + " debugging that software? It turns out that it is very easy to spend lots\n", + " of time fixing bugs and less time than you would like writing new software\n", + " to do new science. This is a problem that is fairly well understood by\n", + " the software engineering community, but many scientists don't take advantage \n", + " of this knowledge. This afternoon we will take a brief look at some of the \n", + " tools and technique to make your debugging less painful.\n", + " \n", + " We'll also think a bit about how you may know if your programmes are correct. \n", + " This is a much harder but important problem. Even minor errors in research \n", + " code can lead to the retraction of papers, as happened to Geoffrey Chang \n", + " in 2006 (see http://dx.doi.org/10.1126/science.314.5807.1856). Chang did\n", + " nothing malicious and committed no fraud, but because of a minor software \n", + " error had two retract five papers just before Christmas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__NB: This notebook is designed for teaching about exceptions and error testing. It includes deliberate errors. There are probably accidental errors too.__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Mean cell volume \n", + "First, we will look at how one programme can produce \n", + "the wrong answer, and how we can avoid this happening\n", + "when we use it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def cell_volume(X, Y, Z):\n", + " # Return the volume of a unit cell \n", + " # described by lattice vectors X, Y and Z\n", + " # The volume is given by the determinant of\n", + " # the matrix formed by sticking the three \n", + " # vectors together. i.e.\n", + " #\n", + " # | X[0] Y[0] Z[0] |\n", + " # V = | X[1] Y[1] Z[1] |\n", + " # | X[2] Y[2] Z[2] |\n", + " #\n", + " # V = X[0].Y[1].Z[2] + Y[0].Z[1].X[2] \n", + " # + X[2].Y[0].Z[1] - Z[0].Y[1].X[2]\n", + " # - Y[0].X[1].Z[2] - X[0].Z[1].Y[2]\n", + " \n", + " volume = (X[0]*Y[1]*Z[2] + Y[0]*Z[1]*X[2] + X[2]*Y[0]*Z[1] \n", + " - Z[0]*Y[1]*X[2] - Y[0]*X[1]*Z[2] - X[0]*Z[1]*Y[2])\n", + " \n", + " return volume" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def mean_cell_volume(cell_list):\n", + " # Return the avarage volume of a list \n", + " # of unit cells. Each element of cell_list\n", + " # should be a list of three lattice vectors, \n", + " # each with three components. The volume of\n", + " # each cell is calculated and summed before \n", + " # being devided by the number of cells to give\n", + " # the mean volume.\n", + " \n", + " num_cells = 0\n", + " sum_volume = 0.0\n", + " for cell in cell_list:\n", + " X = cell[0]\n", + " Y = cell[1]\n", + " Z = cell[2]\n", + " sum_volume = sum_volume + cell_volume(X, Y, Z)\n", + " num_cells = num_cells + 1\n", + " \n", + " mean_volume = sum_volume/num_cells\n", + " \n", + " return mean_volume\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/DefensiveProgramming_2.ipynb b/DefensiveProgramming_2.ipynb new file mode 100644 index 0000000..85afd4b --- /dev/null +++ b/DefensiveProgramming_2.ipynb @@ -0,0 +1,82 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " # Defensive programming (2)\n", + " We have seen the basic idea that we can insert\n", + " assert statments into code, to check that the \n", + " results are what we expect, but how can we test\n", + " software more fully? Can doing this help us \n", + " avoid bugs in the first place?\n", + " \n", + " One possible approach is **test driven development**.\n", + " Many people think this reduces the number of bugs in \n", + " software as it is written, but evidence for this in the \n", + " sciences is somewhat limited as it is not always easy \n", + " to say what the right answer should be before writing the\n", + " software. Having said that, the tests involved in test\n", + " driven development are certanly useful even if some of\n", + " them are written after the software.\n", + " \n", + " We will look at a new (and quite difficult) problem, \n", + " finding the overlap between ranges of numbers. For \n", + " example, these could be the dates that different \n", + " sensors were running, and you need to find the \n", + " date ranges where all sensors recorded data before\n", + " running further analysis." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Start off by imagining you have a working function `range_overlap` that takes\n", + "a list of tuples. Write some assert statments that would check if the answer from this\n", + "function is correct. Put these in a function. Think of different cases and \n", + "about edge cases (which may show a subtle bug)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def test_range_overlap():\n", + " " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/notes/DefensiveProgrammingNotes.ipynb b/notes/DefensiveProgrammingNotes.ipynb new file mode 100644 index 0000000..f3fa3c7 --- /dev/null +++ b/notes/DefensiveProgrammingNotes.ipynb @@ -0,0 +1,575 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " # Defensive programming (1)\n", + " \n", + " How much time do you spend writing software? How much time do you spend \n", + " debugging that software? It turns out that it is very easy to spend lots\n", + " of time fixing bugs and less time than you would like writing new software\n", + " to do new science. This is a problem that is fairly well understood by\n", + " the software engineering community, but many scientists don't take advantage \n", + " of this knowledge. This afternoon we will take a brief look at some of the \n", + " tools and technique to make your debugging less painful.\n", + " \n", + " We'll also think a bit about how you may know if your programmes are correct. \n", + " This is a much harder but important problem. Even minor errors in research \n", + " code can lead to the retraction of papers, as happened to Geoffrey Chang \n", + " in 2006 (see http://dx.doi.org/10.1126/science.314.5807.1856). Chang did\n", + " nothing malicious and committed no fraud, but because of a minor software \n", + " error had two retract five papers just before Christmas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__NB: This notebook is designed for teaching about exceptions and error testing. It includes deliberate errors. There are probably accidental errors too.__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Mean cell volume \n", + "First, we will look at how one programme can produce \n", + "the wrong answer, and how we can avoid this happening\n", + "when we use it." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def cell_volume(X, Y, Z):\n", + " # Return the volume of a unit cell \n", + " # described by lattice vectors X, Y and Z\n", + " # The volume is given by the determinant of\n", + " # the matrix formed by sticking the three \n", + " # vectors together. i.e.\n", + " #\n", + " # | X[0] Y[0] Z[0] |\n", + " # V = | X[1] Y[1] Z[1] |\n", + " # | X[2] Y[2] Z[2] |\n", + " #\n", + " # V = X[0].Y[1].Z[2] + Y[0].Z[1].X[2] \n", + " # + X[2].Y[0].Z[1] - Z[0].Y[1].X[2]\n", + " # - Y[0].X[1].Z[2] - X[0].Z[1].Y[2]\n", + " \n", + " volume = (X[0]*Y[1]*Z[2] + Y[0]*Z[1]*X[2] + X[2]*Y[0]*Z[1] \n", + " - Z[0]*Y[1]*X[2] - Y[0]*X[1]*Z[2] - X[0]*Z[1]*Y[2])\n", + " \n", + " return volume" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "240.0" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cell_volume([4.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 6.0])" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def mean_cell_volume(cell_list):\n", + " # Return the avarage volume of a list \n", + " # of unit cells. Each element of cell_list\n", + " # should be a list of three lattice vectors, \n", + " # each with three components. The volume of\n", + " # each cell is calculated and summed before \n", + " # being devided by the number of cells to give\n", + " # the mean volume.\n", + " \n", + " num_cells = 0\n", + " sum_volume = 0.0\n", + " for cell in cell_list:\n", + " X = cell[0]\n", + " Y = cell[1]\n", + " Z = cell[2]\n", + " sum_volume = sum_volume + cell_volume(X, Y, Z)\n", + " num_cells = num_cells + 1\n", + " \n", + " mean_volume = sum_volume/num_cells\n", + " \n", + " return mean_volume\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "240.0" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mean_cell_volume([[[4.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 6.0]],\n", + " [[10.0, 0.0, 0.0], [0.0, 4.0, 0.0], [0.0, 0.0, 6.0]],\n", + " [[6.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 4.0]]])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "###\"Wrong\" input" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "IndexError", + "evalue": "list index out of range", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m mean_cell_volume([[[4.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 6.0]],\n\u001b[1;32m 2\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m4.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m10.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m [[4.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 6.0]]])\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mmean_cell_volume\u001b[0;34m(cell_list)\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0mY\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcell\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0mZ\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcell\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 16\u001b[0;31m \u001b[0msum_volume\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msum_volume\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mcell_volume\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mX\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mY\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mZ\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 17\u001b[0m \u001b[0mnum_cells\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnum_cells\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mcell_volume\u001b[0;34m(X, Y, Z)\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m volume = (X[0]*Y[1]*Z[2] + Y[0]*Z[1]*X[2] + X[2]*Y[0]*Z[1] \n\u001b[0;32m---> 17\u001b[0;31m - Z[0]*Y[1]*X[2] - Y[0]*X[1]*Z[2] - X[0]*Z[1]*Y[2])\n\u001b[0m\u001b[1;32m 18\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mvolume\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mIndexError\u001b[0m: list index out of range" + ] + } + ], + "source": [ + "mean_cell_volume([[[4.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 6.0]],\n", + " [[4.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0]],\n", + " [[4.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 6.0]]])" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "ZeroDivisionError", + "evalue": "float division by zero", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmean_cell_volume\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mmean_cell_volume\u001b[0;34m(cell_list)\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0mnum_cells\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnum_cells\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 19\u001b[0;31m \u001b[0mmean_volume\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msum_volume\u001b[0m\u001b[0;34m/\u001b[0m\u001b[0mnum_cells\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 20\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 21\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mmean_volume\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mZeroDivisionError\u001b[0m: float division by zero" + ] + } + ], + "source": [ + "mean_cell_volume([])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### What is python telling us?\n", + "That something went wrong, where it went wrong, what went\n", + "wrong, and what the programme was doing at the time. This\n", + "is an exception.\n", + "\n", + "* Exception class (e.g `ZeroDivisionError`)\n", + "* Some further information (e.g. `float division by zero`)\n", + "* File (or cell) name and line number of each function in the call stack (e.g. in `mean_cell_volume` at line `---> 19` inside cell `ipython-input-...`)\n", + "\n", + "We can create these ourselves when we run code:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "Exception", + "evalue": "something went wrong", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mException\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"something went wrong\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mException\u001b[0m: something went wrong" + ] + } + ], + "source": [ + "raise Exception(\"something went wrong\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### What if we get the wrong answer?\n", + "This is a more difficult problem to spot - the avarage volume cannot be 0.0!" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.0" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mean_cell_volume([[[4.0, 0.0, 0.0], [0.0, -10.0, 0.0], [0.0, 0.0, 6.0]],\n", + " [[4.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 6.0]],\n", + " [[-4.0, 0.0, 0.0], [0.0, -10.0, 0.0], [0.0, 0.0, 6.0]],\n", + " [[-4.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 6.0]]])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The reason is that there is a bug in `cell_volume`." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-240.0" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cell_volume([4.0, 0.0, 0.0], [0.0, -10.0, 0.0], [0.0, 0.0, 6.0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The volume should always be positive. We can check for this. This kind of check is known as an assertion." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "AssertionError", + "evalue": "The volume must be positive", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mvolume\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcell_volume\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m4.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m10.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m6.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mvolume\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mAssertionError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"The volume must be positive\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0;32mprint\u001b[0m \u001b[0mvolume\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mAssertionError\u001b[0m: The volume must be positive" + ] + } + ], + "source": [ + "volume = cell_volume([4.0, 0.0, 0.0], [0.0, -10.0, 0.0], [0.0, 0.0, 6.0])\n", + "if (volume < 0.0):\n", + " raise AssertionError(\"The volume must be positive\")\n", + "print volume" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can write these more easily with the **assert** statment. It is \n", + "good practice to put these in your code when you write it (and you \n", + "know what it does, and what assumptions you have made). These act \n", + "as a form of documentation as well as a form of protection. " + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "AssertionError", + "evalue": "The volume must be positive", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mvolume\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcell_volume\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m4.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m10.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m6.0\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;32massert\u001b[0m \u001b[0mvolume\u001b[0m \u001b[0;34m>=\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"The volume must be positive\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0;32mprint\u001b[0m \u001b[0mvolume\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mAssertionError\u001b[0m: The volume must be positive" + ] + } + ], + "source": [ + "volume = cell_volume([4.0, 0.0, 0.0], [0.0, -10.0, 0.0], [0.0, 0.0, 6.0])\n", + "assert volume >= 0.0, \"The volume must be positive\"\n", + "print volume" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can think about three types of assert statment:\n", + "\n", + "* **precondition** - something that must be true at \n", + "the start of a function in order for it to work correctly.\n", + "\n", + "* **invariant** - something that is always true at a \n", + "particular point inside a piece of code.\n", + "\n", + "* **postcondition** - something that the function guarantees is true when it finishes.\n", + "\n", + "Lets think of some and add these to the functions above. My collection is inserted below." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def cell_volume(X, Y, Z):\n", + " # Return the volume of a unit cell \n", + " # described by lattice vectors X, Y and Z\n", + " # The volume is given by the determinant of\n", + " # the matrix formed by sticking the three \n", + " # vectors together. i.e.\n", + " #\n", + " # | X[0] Y[0] Z[0] |\n", + " # V = | X[1] Y[1] Z[1] |\n", + " # | X[2] Y[2] Z[2] |\n", + " #\n", + " # V = X[0].Y[1].Z[2] + Y[0].Z[1].X[2] \n", + " # + X[2].Y[0].Z[1] - Z[0].Y[1].X[2]\n", + " # - Y[0].X[1].Z[2] - X[0].Z[1].Y[2]\n", + " \n", + " assert len(X) == 3, \"X must be a three-vector\"\n", + " assert len(Y) == 3, \"Y must be a three-vector\"\n", + " assert len(Z) == 3, \"Z must be a three-vector\"\n", + " \n", + " volume = (X[0]*Y[1]*Z[2] + Y[0]*Z[1]*X[2] + X[2]*Y[0]*Z[1] \n", + " - Z[0]*Y[1]*X[2] - Y[0]*X[1]*Z[2] - X[0]*Z[1]*Y[2])\n", + " \n", + " assert volume >= 0.0, \"The calculated volume must be positive\"\n", + " \n", + " return volume" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def mean_cell_volume(cell_list):\n", + " # Return the avarage volume of a list \n", + " # of unit cells. Each element of cell_list\n", + " # should be a list of three lattice vectors, \n", + " # each with three components. The volume of\n", + " # each cell is calculated and summed before \n", + " # being devided by the number of cells to give\n", + " # the mean volume.\n", + " \n", + " num_cells = 0\n", + " sum_volume = 0.0\n", + " for cell in cell_list:\n", + " X = cell[0]\n", + " Y = cell[1]\n", + " Z = cell[2]\n", + " sum_volume = sum_volume + cell_volume(X, Y, Z)\n", + " num_cells = num_cells + 1\n", + " \n", + " assert num_cells >= 1, \"One or more cells must be provided\"\n", + " mean_volume = sum_volume/num_cells\n", + " \n", + " return mean_volume" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "AssertionError", + "evalue": "Z must be a three-vector", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m mean_cell_volume([[[4.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 6.0]],\n\u001b[1;32m 2\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m4.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m10.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m [[4.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 6.0]]])\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mmean_cell_volume\u001b[0;34m(cell_list)\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0mY\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcell\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0mZ\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcell\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 16\u001b[0;31m \u001b[0msum_volume\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msum_volume\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mcell_volume\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mX\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mY\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mZ\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 17\u001b[0m \u001b[0mnum_cells\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnum_cells\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mcell_volume\u001b[0;34m(X, Y, Z)\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mX\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\"X must be a three-vector\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mY\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\"Y must be a three-vector\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 18\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mZ\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\"Z must be a three-vector\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 19\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m volume = (X[0]*Y[1]*Z[2] + Y[0]*Z[1]*X[2] + X[2]*Y[0]*Z[1] \n", + "\u001b[0;31mAssertionError\u001b[0m: Z must be a three-vector" + ] + } + ], + "source": [ + "mean_cell_volume([[[4.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 6.0]],\n", + " [[4.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0]],\n", + " [[4.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 6.0]]])" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "ZeroDivisionError", + "evalue": "float division by zero", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmean_cell_volume\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mmean_cell_volume\u001b[0;34m(cell_list)\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0mnum_cells\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnum_cells\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 19\u001b[0;31m \u001b[0mmean_volume\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msum_volume\u001b[0m\u001b[0;34m/\u001b[0m\u001b[0mnum_cells\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 20\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 21\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mmean_volume\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mZeroDivisionError\u001b[0m: float division by zero" + ] + } + ], + "source": [ + "mean_cell_volume([])" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "AssertionError", + "evalue": "The calculated volume must be positive", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mcell_volume\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m4.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m10.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m6.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mcell_volume\u001b[0;34m(X, Y, Z)\u001b[0m\n\u001b[1;32m 21\u001b[0m - Z[0]*Y[1]*X[2] - Y[0]*X[1]*Z[2] - X[0]*Z[1]*Y[2])\n\u001b[1;32m 22\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 23\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0mvolume\u001b[0m \u001b[0;34m>=\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"The calculated volume must be positive\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 24\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 25\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mvolume\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mAssertionError\u001b[0m: The calculated volume must be positive" + ] + } + ], + "source": [ + "cell_volume([4.0, 0.0, 0.0], [0.0, -10.0, 0.0], [0.0, 0.0, 6.0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you are interested - the bug is in cell_volume. This should \n", + "return the absolute value of the determinant. The handedness of\n", + "the three vectors should not matter." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/notes/DefensiveProgrammingNotes.pdf b/notes/DefensiveProgrammingNotes.pdf new file mode 100644 index 0000000..68a62c1 Binary files /dev/null and b/notes/DefensiveProgrammingNotes.pdf differ diff --git a/notes/DefensiveProgramming_2_Notes.ipynb b/notes/DefensiveProgramming_2_Notes.ipynb new file mode 100644 index 0000000..955f6b5 --- /dev/null +++ b/notes/DefensiveProgramming_2_Notes.ipynb @@ -0,0 +1,307 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " # Defensive programming (2)\n", + " We have seen the basic idea that we can insert\n", + " assert statments into code, to check that the \n", + " results are what we expect, but how can we test\n", + " software more fully? Can doing this help us \n", + " avoid bugs in the first place?\n", + " \n", + " One possible approach is **test driven development**.\n", + " Many people think this reduces the number of bugs in \n", + " software as it is written, but evidence for this in the \n", + " sciences is somewhat limited as it is not always easy \n", + " to say what the right answer should be before writing the\n", + " software. Having said that, the tests involved in test\n", + " driven development are certanly useful even if some of\n", + " them are written after the software.\n", + " \n", + " We will look at a new (and quite difficult) problem, \n", + " finding the overlap between ranges of numbers. For \n", + " example, these could be the dates that different \n", + " sensors were running, and you need to find the \n", + " date ranges where all sensors recorded data before\n", + " running further analysis." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Start off by imagining you have a working function `range_overlap` that takes\n", + "a list of tuples. Write some assert statments that would check if the answer from this\n", + "function is correct. Put these in a function. Think of different cases and \n", + "about edge cases (which may show a subtle bug)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def test_range_overlap():\n", + " assert range_overlap([(-3.0, 5.0), (0.0, 4.5), (-1.5, 2.0)]) == (0.0, 2.0)\n", + " assert range_overlap([ (2.0, 3.0), (2.0, 4.0) ]) == (2.0, 3.0)\n", + " assert range_overlap([ (0.0, 1.0), (0.0, 2.0), (-1.0, 1.0) ]) == (0.0, 1.0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "But what if there is no overlap? What if they just touch?" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def test_range_overlap_no_overlap():\n", + " assert range_overlap([ (0.0, 1.0), (5.0, 6.0) ]) == None\n", + " assert range_overlap([ (0.0, 1.0), (1.0, 2.0) ]) == None" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What about the case of a single range?" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def test_range_overlap_one_range():\n", + " assert range_overlap([ (0.0, 1.0) ]) == (0.0, 1.0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The write a solution - one possible one is below." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def range_overlap(ranges):\n", + " # Return common overlap among a set of [low, high] ranges.\n", + " lowest = -1000.0\n", + " highest = 1000.0\n", + " for (low, high) in ranges:\n", + " lowest = max(lowest, low)\n", + " highest = min(highest, high)\n", + " return (lowest, highest)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And test it..." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "test_range_overlap()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "AssertionError", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtest_range_overlap_no_overlap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mtest_range_overlap_no_overlap\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mtest_range_overlap_no_overlap\u001b[0m\u001b[0;34m(\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;32massert\u001b[0m \u001b[0mrange_overlap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1.0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m5.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m6.0\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0mrange_overlap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m0.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1.0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m1.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2.0\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mAssertionError\u001b[0m: " + ] + } + ], + "source": [ + "test_range_overlap_no_overlap()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "test_range_overlap_one_range()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Should we add to the tests?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Can you write version with fewer bugs. My attempt is below." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def pairs_overlap(rangeA, rangeB):\n", + " # Check if A starts after B ends and \n", + " # A ends before B starts. If both are \n", + " # false, there is an overlap.\n", + " # We are assuming (0.0 1.0) and\n", + " # (1.0 2.0) do not overlap. If these should\n", + " # overlap swap >= for > and <= for <.\n", + " overlap = not ((rangeA[0] >= rangeB[1]) or\n", + " (rangeA[1] <= rangeB[0]))\n", + " \n", + " return overlap\n", + "\n", + "def find_overlap(rangeA, rangeB):\n", + " # Return the overlap between range \n", + " # A and B\n", + " if pairs_overlap(rangeA, rangeB):\n", + " low = max(rangeA[0], rangeB[0])\n", + " high = min(rangeA[1], rangeB[1])\n", + " return (low, high)\n", + " else:\n", + " return None\n", + "\n", + "def range_overlap(ranges):\n", + " # Return common overlap among a set of \n", + " # [low, high] ranges.\n", + "\n", + " if len(ranges) == 1:\n", + " # Special case of one range - \n", + " # overlaps with itself\n", + " return(ranges[0])\n", + " elif len(ranges) == 2:\n", + " # Just return from find_overlap\n", + " return find_overlap(ranges[0], ranges[1])\n", + " else:\n", + " # Range of A, B, C is the \n", + " # range of range(B,C) with\n", + " # A, etc. Do this by recursion...\n", + " overlap = find_overlap(ranges[-1], ranges[-2]) \n", + " if overlap is not None:\n", + " # Chop off the end of ranges and \n", + " # replace with the overlap\n", + " ranges = ranges[:-2]\n", + " ranges.append(overlap)\n", + " # Now run again, with the smaller list.\n", + " return range_overlap(ranges)\n", + " else:\n", + " return None\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "test_range_overlap()\n", + "test_range_overlap_one_range()\n", + "test_range_overlap_no_overlap()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It is possible to automate the process of running these tests,\n", + "and even plugging the tests into a version control system so \n", + "that you know each version passes the tests (or not). We may have\n", + "time to look at this tomorrow. But just having the tests isvery \n", + "helpful.\n", + "\n", + "But for now, lets talk about some debugging tips: http://swcarpentry.github.io/python-novice-inflammation/09-debugging.html" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/notes/DefensiveProgramming_2_Notes.pdf b/notes/DefensiveProgramming_2_Notes.pdf new file mode 100644 index 0000000..d5ef77d Binary files /dev/null and b/notes/DefensiveProgramming_2_Notes.pdf differ diff --git a/notes/python-overlapping-ranges.svg b/notes/python-overlapping-ranges.svg new file mode 100644 index 0000000..8f99ac2 --- /dev/null +++ b/notes/python-overlapping-ranges.svg @@ -0,0 +1,1418 @@ + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -3.0 + + + + + + + + + + + + + + + + + + + + + 5.0 + + + + + + 0.0 + + + + + + + + + + + + + + + + + + + + + 4.5 + + + + + + -1.5 + + + + + + + + + + + + + + + + + + + + + 2.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0 + + + + + + + + + + + 2.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/python-overlapping-ranges.svg b/python-overlapping-ranges.svg new file mode 100644 index 0000000..8f99ac2 --- /dev/null +++ b/python-overlapping-ranges.svg @@ -0,0 +1,1418 @@ + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -3.0 + + + + + + + + + + + + + + + + + + + + + 5.0 + + + + + + 0.0 + + + + + + + + + + + + + + + + + + + + + 4.5 + + + + + + -1.5 + + + + + + + + + + + + + + + + + + + + + 2.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0 + + + + + + + + + + + 2.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file