{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Functions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$\\newcommand{\\re}{\\mathbb{R}}$\n", "\n", "A function is something which takes some input arguments and returns one or more output values. These are called ```def``` in Python, short for definition. They have following structure\n", "```\n", "def functionname(arg1,arg2,arg3):\n", " DO SOME COMPUTATION\n", " return result\n", "```\n", "The body of the function must be indented.\n", "\n", "Whenever some piece of computation is to be repeated many times, it is best to put it inside a function. This avoids code duplication, which minimizes errors and makes it easy to maintain your code." ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "import numpy as np" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Sum two vectors\n", "\n", "For $x, y \\in \\re^n$, compute $z \\in \\re^n$ by\n", "\n", "$$\n", "z_i = x_i + y_i, \\qquad 0 \\le i \\le n-1\n", "$$" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "def sum1(x,y):\n", " z = np.empty_like(x)\n", " for i in range(len(x)):\n", " z[i] = x[i] + y[i]\n", " return z" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can use this function." ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[3. 3. 3. 3. 3.]\n", "[0. 0. 0. 0. 0.]\n" ] } ], "source": [ "n = 5\n", "x = np.ones(n)\n", "y = 2*x\n", "z = sum1(x,y)\n", "print(z)\n", "print(z - (x + y))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Check your inputs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can add two vectors only if they are of same size. It is always a good practice to check that your inputs are consistent. We will also use [enumerate](https://docs.python.org/library/functions.html#enumerate) and [zip](https://docs.python.org/library/functions.html#zip)." ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "def sum2(x,y):\n", " assert len(x) == len(y)\n", " z = np.empty_like(x)\n", " for i,(a,b) in enumerate(zip(x,y)):\n", " z[i] = a + b\n", " return z" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First test on same inputs as before." ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[3. 3. 3. 3. 3.]\n", "[0. 0. 0. 0. 0.]\n" ] } ], "source": [ "z = sum2(x,y)\n", "print(z)\n", "print(z - (x + y))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you try to run the following code, it will give an error.\n", "\n", "```\n", "n = 5\n", "x = np.ones(n)\n", "y = np.ones(n+1)\n", "z = sum2(x,y)\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Minimum value of an array\n", "\n", "Given $x \\in \\re^n$, compute\n", "\n", "$$\n", "m = \\min_{0 \\le i \\le n-1} x_i\n", "$$" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [], "source": [ "def minval(x):\n", " val = x[0]\n", " for v in x:\n", " if v < val:\n", " val = v\n", " return val" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Test on some random array." ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x = \n", " [0.56952692 0.45413289 0.74771823 0.87510616 0.84367195 0.00983037\n", " 0.86834298 0.23403788 0.14703669 0.53805294]\n", "Minimum = 0.0098303666528603\n", "Numpy = 0.0098303666528603\n" ] } ], "source": [ "x = np.random.rand(10)\n", "mval = minval(x)\n", "print('x = \\n', x)\n", "print('Minimum = ',mval)\n", "print('Numpy = ', x.min())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Min and max of an array\n", "\n", "Given $x \\in \\re^n$, compute\n", "\n", "$$\n", "m = \\min_{0 \\le i \\le n-1} x_i, \\qquad\n", "M = \\max_{0 \\le i \\le n-1} x_i\n", "$$" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [], "source": [ "def minmax(x):\n", " min_val = x[0]\n", " max_val = x[0]\n", " for v in x:\n", " if v < min_val:\n", " min_val = v\n", " if v > max_val:\n", " max_val = v\n", " return min_val, max_val" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The type of returned object is `tuple`." ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0.49440445 0.96844055 0.87774249 0.61157192 0.29238153]\n", "Minimum = 0.29238152818674534 0.29238152818674534\n", "Maximum = 0.9684405476373608 0.9684405476373608\n" ] } ], "source": [ "x = np.random.rand(5)\n", "min_val, max_val = minmax(x)\n", "print(x)\n", "print('Minimum =',min_val,x.min())\n", "print('Maximum =',max_val,x.max())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## lambda: for simple functions\n", "\n", "If the function definition is simple we can define them using lambda\n", "\n", "$$\n", "f(x) = \\sin(2\\pi x)\n", "$$" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Minimum = -0.9998741276738751\n", "Maximum = 0.9998741276738751\n" ] } ], "source": [ "f = lambda x: np.sin(2*np.pi*x)\n", "x = np.linspace(0.0, 1.0, 100)\n", "y = f(x)\n", "min_val, max_val = minmax(y)\n", "print('Minimum =',min_val)\n", "print('Maximum =',max_val)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can define lambda's with more than one argument." ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "-0.2938926261462344\n" ] } ], "source": [ "g = lambda x,y: np.sin(2*np.pi*x) * np.cos(2*np.pi*y)\n", "z = g(1.2, 2.3)\n", "print(z)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Variables inside functions are local" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [], "source": [ "def fun1(x):\n", " a = 1\n", " print(x + a)\n", "\n", "def fun2(x):\n", " a = 2\n", " print(x + a)\n", "\n", "def fun3(x):\n", " print(x + a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The variable `a` inside these functions are local variables and have different values." ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n", "3\n" ] }, { "ename": "NameError", "evalue": "name 'a' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[46], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m fun1(\u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 2\u001b[0m fun2(\u001b[38;5;241m1\u001b[39m)\n\u001b[0;32m----> 3\u001b[0m \u001b[43mfun3\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n", "Cell \u001b[0;32mIn[45], line 10\u001b[0m, in \u001b[0;36mfun3\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mfun3\u001b[39m(x):\n\u001b[0;32m---> 10\u001b[0m \u001b[38;5;28mprint\u001b[39m(x \u001b[38;5;241m+\u001b[39m \u001b[43ma\u001b[49m)\n", "\u001b[0;31mNameError\u001b[0m: name 'a' is not defined" ] } ], "source": [ "fun1(1)\n", "fun2(1)\n", "fun3(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Variable `a` is not visible in `fun3` since it is a local variable in `fun1` and `fun2`.\n", "\n", "## Beware of global variables" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [], "source": [ "p = 1.234 # Global variable, visible inside all functions.\n", "\n", "def fun4(x):\n", " p = 1.0 # Local variable of same name\n", " print(x + p)\n", "\n", "def fun5(x):\n", " print(x + p) # Here the global variable p is used" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The variable `p = 1.234` which is declared outside the functions is a global variable." ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2.0\n", "2.234\n" ] } ], "source": [ "fun4(1.0)\n", "fun5(1.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The variable `p = 1.0` is a local variable in `fun4`; this is ok, you can have a local and global variable of the same name, they have independent existence. But in `fun5`, the global variable `p = 1.234` is used since there is no local variable of the same name. If you wanted to use the global variable, then it is fine, but if you forgot to declare `p` inside `fun5`, then you have a bug, and it can be difficult to detect.\n", "\n", "You have to be very careful with global variables. If you forget to declare some variable inside a function, and a global variable with the same name exists, then your program will run but likely give wrong answers. We should ideally never use global variables, but they are rampant in jupyter notebooks. To avoid global variables, you have to declare ALL variables inside functions and not declare anything in the global scope. It is good practice not to rely on global variables, but instead write [pure functions](https://en.wikipedia.org/wiki/Pure_function)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Pass a function as argument to other functions" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [], "source": [ "def sin(x):\n", " return np.sin(x)\n", " \n", "def cos(x):\n", " return np.cos(x)\n", " \n", "def run(x, f):\n", " print(f(x))" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.0\n", "6.123233995736766e-17\n" ] } ], "source": [ "x = np.pi/2\n", "run(x, sin)\n", "run(x, cos)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.8" }, "vscode": { "interpreter": { "hash": "c6e4e9f98eb68ad3b7c296f83d20e6de614cb42e90992a65aa266555a3137d0d" } } }, "nbformat": 4, "nbformat_minor": 4 }