{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Scalars" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "%matplotlib inline" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Integers" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Binary representation of integers" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'00000000000000000000000000010000'" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "format(16, '032b')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Bit shifting" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'00000000000000000000000000000100'" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "format(16 >> 2, '032b')" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "16 >> 2" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'00000000000000000000000001000000'" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "format(16 << 2, '032b')" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "64" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "16 << 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Overflow\n", "\n", "In general, the computer representation of integers has a limited range, and may overflow. The range depends on whether the integer is signed or unsigned.\n", "\n", "For example, with 8 bits, we can represent at most $2^8 = 256$ integers.\n", "\n", "- 0 to 255 unsigned\n", "- -128 ti 127 signed" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Signed integers" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 125, 126, 127, -128, -127], dtype=int8)" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.arange(130, dtype=np.int8)[-5:]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Unsigned integers" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([125, 126, 127, 128, 129], dtype=uint8)" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.arange(130, dtype=np.uint8)[-5:]" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([255, 0, 1, 2, 3], dtype=uint8)" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.arange(260, dtype=np.uint8)[-5:]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Integer division\n", "\n", "In Python 2 or other languages such as C/C++, be very careful when dividing as the division operator `/` performs integer division when both numerator and denominator are integers. This is rarely what you want. In Python 3 the `/` always performs floating point division, and you use `//` for integer division, removing a common source of bugs in numerical calculations." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0 0 0 0 0 0 0 0 0 0]\n" ] } ], "source": [ "%%python2\n", "\n", "import numpy as np\n", "\n", "x = np.arange(10)\n", "print(x/10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Python 3 does the \"right\" thing." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = np.arange(10)\n", "x/10" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Real numbers\n", "\n", "Real numbers are represented as **floating point** numbers. A floating point number is stored in 3 pieces (sign bit, exponent, mantissa) so that every float is represetned as get +/- mantissa ^ exponent. Because of this, the interval between consecutive numbers is smallest (high precison) for numebrs close to 0 and largest for numbers close to the lower and upper bounds.\n", "\n", "Because exponents have to be singed to represent both small and large numbers, but it is more convenint to use unsigned numbers here, the exponnent has an offset (also knwnn as the exponentn bias). For example, if the expoennt is an unsigned 8-bit number, it can rerpesent the range (0, 255). By using an offset of 128, it will now represent the range (-127, 128).\n", "\n", "![float1](http://www.dspguide.com/graphics/F_4_2.gif)\n", "\n", "**Note**: Intervals between consecutive floating point numbers are not constant. In particular, the precision for small numbers is much larger than for large numbers. In fact, approximately half of all floating point numbers lie between -1 and 1 when using the `double` type in C/C++ (also the default for `numpy`).\n", "\n", "![float2](http://jasss.soc.surrey.ac.uk/9/4/4/fig1.jpg)\n", "\n", "Because of this, if you are adding many numbers, it is more accurate to first add the small numbers before the large numbers." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### IEEE 754 32-bit floating point representation\n", "\n", "![img](https://upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Float_example.svg/590px-Float_example.svg.png)\n", "\n", "See [Wikipedia](https://en.wikipedia.org/wiki/Single-precision_floating-point_format) for how this binary number is evaluated to 0.15625." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from ctypes import c_int, c_float" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": true }, "outputs": [], "source": [ "s = c_int.from_buffer(c_float(0.15625)).value" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'00111110001000000000000000000000'" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s = format(s, '032b')\n", "s" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'exponent': '01111100', 'fraction': '01000000000000000000000', 'sign': '0'}" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rep = {\n", " 'sign': s[:1], \n", " 'exponent' : s[1:9:], \n", " 'fraction' : s[9:]\n", "}\n", "rep" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Most base 10 real numbers are approximations\n", "\n", "This is simply because numbers are stored in finite-precision binary format." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'1.00000000000000022204'" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'%.20f' % (0.1 * 0.1 * 100)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Never check for equality of floating point numbers" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10000.000000171856" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "i = 0\n", "loops = 0\n", "while i != 1:\n", " i += 0.1 * 0.1\n", " loops += 1\n", " if loops == 1000000:\n", " break\n", "i" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.0000000000000007" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "i = 0\n", "loops = 0\n", "while np.abs(1 - i) > 1e-6:\n", " i += 0.1 * 0.1\n", " loops += 1\n", " if loops == 1000000:\n", " break\n", "i" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Associative law does not necessarily hold" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.0" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "6.022e23 - 6.022e23 + 1" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.0" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1 + 6.022e23 - 6.022e23" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Distributive law does not hold" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": true }, "outputs": [], "source": [ "a = np.exp(1)\n", "b = np.pi\n", "c = np.sin(1)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10.82708950985241" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a*(b+c)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10.827089509852408" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a*b + a*c" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Catastrophic cancellation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Consider calculating sample variance\n", "\n", "$$\n", "s^2= \\frac{1}{n(n-1)}\\sum_{i=1}^n x_i^2 - (\\sum_{i=1}^n x_i)^2\n", "$$\n", "\n", "Be careful whenever you calculate the difference of potentially big numbers." ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def var(x):\n", " \"\"\"Returns variance of sample data using sum of squares formula.\"\"\"\n", " \n", " n = len(x)\n", " return (1.0/(n*(n-1))*(n*np.sum(x**2) - (np.sum(x))**2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Numerically stable algorithms" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### What is the sample variance for numbers from a normal distribution with variance 1?" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-1328166901.4739892" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.random.seed(15)\n", "x_ = np.random.normal(0, 1, int(1e6))\n", "x = 1e12 + x_\n", "var(x)" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.001345504504934" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.var(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Underflow" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "collapsed": true }, "outputs": [], "source": [ "np.warnings.filterwarnings('ignore')" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "nan" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.random.seed(4)\n", "xs = np.random.random(1000)\n", "ys = np.random.random(1000)\n", "np.prod(xs)/np.prod(ys)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Prevent underflow by staying in log space" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "696432868222.2549" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = np.sum(np.log(xs))\n", "y = np.sum(np.log(ys))\n", "np.exp(x - y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Overflow\n", "\n", "Let's calculate\n", "\n", "$$\n", "\\log(e^{1000} + e^{1000})\n", "$$\n", "\n", "Using basic algebra, we get the solution $\\log(2) + 1000$." ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "inf" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = np.array([1000, 1000])\n", "np.log(np.sum(np.exp(x)))" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1000.6931471805599" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.logaddexp(*x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**logsumexp**\n", "\n", "This function generalizes `logaddexp` to an arbitrary number of addends and is useful in a variety of statistical contexts." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Suppose we need to calculate a probability distribution $\\pi$ parameterized by a vector $x$\n", "\n", "$$\n", "\\pi_i = \\frac{e^{x_i}}{\\sum_{j=1}^n e^{x_j}}\n", "$$\n", "\n", "Taking logs, we get\n", "\n", "$$\n", "\\log(\\pi_i) = x_i - \\log{\\sum_{j=1}^n e^{x_j}}\n", "$$" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "collapsed": true }, "outputs": [], "source": [ "x = 1e6*np.random.random(100)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "inf" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.log(np.sum(np.exp(x))) " ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from scipy.special import logsumexp" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "989279.6334854055" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "logsumexp(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Other useful numerically stable functions " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**logp1 and expm1**" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "9.999999999177334e-07" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.exp(np.log(1 + 1e-6)) - 1" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1e-06" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.expm1(np.log1p(1e-6))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**sinc**" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "collapsed": true }, "outputs": [], "source": [ "x = 1" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.8414709848078965" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.sin(x)/x" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3.8981718325193755e-17" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.sinc(x)" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "collapsed": true }, "outputs": [], "source": [ "x = np.linspace(0.01, 2*np.pi, 100)" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.plot(x, np.sinc(x), label='Library function')\n", "plt.plot(x, np.sin(x)/x, label='DIY function')\n", "plt.legend()\n", "pass" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.5" } }, "nbformat": 4, "nbformat_minor": 2 }