Linear Algebra and Programming Skills¶

Dr Jon Shiach, Department of Computing and Mathematics, Manchester Metropolitan University


Functions¶

Learning Outcomes¶

On successful completion of this page readers will be able to:

  • define functions in Python and call them to perform calculations;
  • define functions with multiple input and output arguments.

A function is a block of code that is used to perform a single action. These are very useful in programming as they allow us to think of a program as a collection of single individual tasks which helps when writing longer programs. Functions also allow us to repeat tasks easily without having to write out the individual commands again.

You have already been introduced to functions that are in the math library. For example, execute the code cell below

In [1]:
import math

math.exp(1)
Out[1]:
2.718281828459045

Here we have used the exp function from the math library to calculate the value of the constant $e$. Using a function like this is known as calling a function


Defining functions¶

Functions in Python are defined using the def keyword

def function_name(inputs):
    commands
    return outputs

Where

  • function_name is the name which is used to call the function. A function name must begin with a character or underscore and not a number. Also care must be taken not to use the same name as a previously defined function (e.g., if you define a new function called abs then you won't be able to use Python's built-in abs function).
  • inputs are variables which are used as inputs to the function.
  • outputs are variables which are outputted by the function.

Note that the commands within the function are indented. The commands following a function that are not indented are not part of the function so functions are ended with the next non-indented command (similar to loops and if statements).

Example 1¶

The Python code below defines a function that doubles the number that is parsed into it. Enter this into the code cell below and execute it (this will not produce any output but needs to be done in order for us to be able to call the function later).

def double(x):
    y = 2 * x
    return y
In [2]:
def double(x):
    y = 2 * x
    return y

To use (or call) our function we use the function name and parse a number into it. You will need to execute the code block above which defines the function double before it can be used. Enter the following command into the code cell below and execute it.

double(3)
In [3]:
double(3)
Out[3]:
6

Local and global variables¶

Any variables defined within a function are known as local variables because they can only be accessed from commands within the function. It can be useful to think of a function as a black box in that any variables which are defined within a function cannot be used outside of the function unless they have been outputted using the return command.

Variables that have been defined prior to the a function being called are known as global variables and can be access within a function, i.e., all variables defined outside of a function are global variables by default.

Example 2¶

The program below defines a function that demonstrates the difference between local and global variables. Enter it into the code cell below and execute it.

def example_function():
    b = 2
    print("The value of the global variable a is {}.".format(a))
    print("The value of the local variable b is {}.".format(b))

a = 10
example_function()

# try to print the variable b (uncomment the line below to see what happens)
# print(b)
In [4]:
def example_function():
    b = 2
    print("The value of the global variable a is {}.".format(a))
    print("The value of the local variable b is {}.".format(b))

a = 10
example_function()

# try to print the variable b (uncomment the line below to see what happens)
# print(b)
The value of the global variable a is 10.
The value of the local variable b is 2.

Here we defined a global variable a and a function example_function in which we also define a local variable b. The two print commands within the function are used to show that both the local and global variables can be accessed in the function even though the global variable is not an input argument of the function. If we tried to access the value of b outside of the function Python would return an error (uncomment the last line to try it).


Exercise 1 - Defining functions¶

  1. Write a function called f which calculates the value of the function $f(x) = 2x^2 - 3x + 4$ for an input $x$.
In [5]:
def f(x):
    return 2 * x ** 2 - 3 * x + 3

Test your function on the following:

    (a)   $f(1) = 2$

In [6]:
f(1)
Out[6]:
2

    (b)   $f(-3) = 30$

In [7]:
f(-3)
Out[7]:
30

    (c)   $f(1.23) = 2.3358$

In [8]:
f(1.23)
Out[8]:
2.3358
  1. Write a function called my_abs which calculates the absolute value of an inputted number. Test your function on $|-2| = 2$.

    $$ \operatorname{abs}(x) = \begin{cases} x, & x \geq 0, \\ -x, & x < 0. \end{cases}$$
In [9]:
def my_abs(n):
    if n >= 0:
        return n
    else:
        return -n
    
my_abs(-2)
Out[9]:
2
  1. Write a function called odd_or_even which takes in an input of a integer number and returns the character string "even" or "odd" depending on the value of the number.
In [10]:
def odd_or_even(n):
    if n % 2 == 0:
        return "even"
    else:
        return "odd"
    
odd_or_even(3)
Out[10]:
'odd'
  1. Write a function called isprime that takes in an input of an integer number $n$ and returns True or False depending whether it is an integer or not using a for loop to check whether the numbers between 2 and $n-1$ are factors of $n$.
In [11]:
def isprime(n):
    for i in range(2, n):
        if n % i == 0:
            return False
    
    return True

Test your function with the numbers:

    (a)   3217 (prime)

In [12]:
isprime(3217)
Out[12]:
True

    (b)   4233 (not prime)

In [13]:
isprime(4233)
Out[13]:
False
  1. Define a function called norm that calculates the magnitude of a vector that is inputted into it. Your function should be able to deal with vectors of any length.

    $$|\mathbf{a}| = \sqrt{\sum_{i=1}^n a_i^2}.$$
In [14]:
import numpy as np

def norm(a):
    anorm = 0
    for x in a:
        anorm += x ** 2
    
    return np.sqrt(anorm)

Test your function on $|(1,2,3)| \approx 3.7417$.

In [15]:
norm(np.array([1, 2, 3]))
Out[15]:
3.7416573867739413

Functions with multiple inputs¶

Functions can have multiple inputs.

def function_name(input1, input2, ... ):
    commands
    return output

Example 3¶

The program below defines a function which raises the number x to the power n and then uses it to calculate $2^5$. Enter it into the code cell below and execute it. Try changing the values in the power(2, 5) command to calculate different powers.

def power(x, n):
    y = 1
    for i in range(1, n+1):
        y = x * y

    return y

# Use the function power to calculate 2^5
power(2, 5)
In [16]:
def power(x, n):
    y = 1
    for i in range(1, n+1):
        y = x * y

    return y

# Use the function power to calculate 2^5
power(2, 5)
Out[16]:
32

Exercise 2 - Functions with multiple inputs¶

  1. Write a function called g which calculates the value of the bivariate function $g(x, y) = x^2 + y^3$ for inputs of $x$ and $y$.
In [17]:
def g(x, y):
    return x ** 2 + y ** 3

Test your function on:

    (a)   $g(1, 2) = 9$

In [18]:
g(1, 2)
Out[18]:
9

    (b)   $g(3, -1) = 8$

In [19]:
g(3, -1)
Out[19]:
8

    (c)   $g(4, 6) = 232$

In [20]:
g(4, 6)
Out[20]:
232
  1. Define a function called pythagoras which takes in two inputs of the lengths of the two shortest sides of a right-angled triangle a and b and returns the length of the hypotenuse c calculated using Pythagoras' theorem.
$$ c = \sqrt{a^2 + b^2}.$$
In [21]:
import numpy as np

def pythagoras(a, b):
    return np.sqrt(a ** 2 + b ** 2)

Test your function to find the hypotenuse when the two shortest sides are:

    (a)   5 and 12 giving a hypotenuse of 13

In [22]:
pythagoras(5, 12)
Out[22]:
13.0

    (b)   2 and 3 giving a hypotenuse of 3.6056

In [23]:
pythagoras(2, 3)
Out[23]:
3.605551275463989
  1. Define a function called dot_product that calculates the dot product of two inputted vectors
$$\mathbf{a} \cdot \mathbf{b} = \sum_{i=1}^n a_ib_i.$$
In [24]:
import numpy as np

def dot_product(a, b):
    adotb = 0
    for i in range(len(a)):
        adotb += a[i] * b[i]
    
    return adotb

Test your function on $(1,2,3,4) \cdot (5, 6, 7, 8)=70$

In [25]:
dot_product(np.array([1, 2, 3, 4]), np.array([5, 6, 7, 8]))
Out[25]:
70
  1. Define a function called cross_product that calculates the cross product of two vectors in $\mathbb{R}^3$.

    $$\mathbf{a} \times \mathbf{b} = \begin{vmatrix} \mathbf{i} & \mathbf{j} & \mathbf{k} \\ a_1 & a_2 & a_3 \\ b_1 & b_2 & b_3 \end{vmatrix} = (a_2b_3-a_3b_2,a_3b_1-a_1b_3,a_1b_2-a_2b_1).$$
In [26]:
import numpy

def cross_product(a, b):
    axb = np.zeros(3)
    axb[0] = a[1] * b[2] - a[2] * b[1]
    axb[1] = a[2] * b[0] - a[0] * b[2]
    axb[2] = a[0] * b[1] - a[1] * b[0]
    return axb

Test your function on $(1, 2, 3) \times (-1, 0, 2) = (4, -5, 2)$

In [27]:
print(cross_product(np.array([1, 2, 3]), np.array([-1, 0, 2])))
[ 4. -5.  2.]
  1. Euclid's algorithm for finding the greatest common divisor (gcd) of two numbers is:

    • Divide $a$ by $b$ and find the remainder $r$
    • Set $a := b$ and $b := r$
    • Repeat these steps as long as $b \neq 0$ then $\operatorname{gcd}(a, b)= a$.

For example, consider the gcd of 9 and 15

  • $r = 9 \operatorname{mod} 15 = 6$ so $a = 15$ and $b = 6$
  • $r = 15 \operatorname{mod} 6 = 3$ so $a = 6$ and $b = 3$
  • $r = 6 \operatorname{mod} 3 = 0$ so $a = 3$ and $b = 0$

so $\operatorname{gcd}(9, 15) = 3$. Define a function called gcd that uses Euclid's algorithm to calculate the gcd of two numbers.

In [28]:
def gcd(a, b):
    while b != 0:
        a, b = b, a % b
        
    return a     

Test your function on

    (a)   $\operatorname{gcd}(3, 6)=6$

In [29]:
gcd(15, 9)
Out[29]:
3

    (b)   $\operatorname{gcd}(24, 54)=6$

In [30]:
gcd(24, 54)
Out[30]:
6

Functions with multiple outputs¶

Functions can also output multiple values.

def function_name(input1, input2, ... ):
    commands
    return output1, output2, ...

The outputs of a function can be accessed by listing the values or as a single tuple, e.g.,

output1, output2, ... = function_name(input1, input2, ...)

or

outputs = function_name(input1, input2, ...)

Example 4¶

The program below defines a function that calculates the roots of the quadratic polynomial $0=ax^2 + bx + c$. Enter it into the code cell below and execute it to see the result. Try using the function to calculate roots of different quadratics including ones with one or no real roots.

import numpy as np

def quadratic(a, b, c):    
    discriminant = b ** 2 - 4 * a * c
    if discriminant < 0:
        print("No real roots.")
        return

    elif discriminant == 0:
        root = -b / (2 * a)
        return root

    else:
        root1 = (-b - np.sqrt(discriminant)) / (2 * a)
        root2 = (-b + np.sqrt(discriminant)) / (2 * a)
        return root1, root2


# Use the function quadratic() to compute the roots of 0 = x^2 - 4a + 3
quadratic(1, -4, 3)
In [31]:
import numpy as np

def quadratic(a, b, c):    
    discriminant = b ** 2 - 4 * a * c
    if discriminant < 0:
        print("No real roots.")
        return
    
    elif discriminant == 0:
        root = -b / (2 * a)
        return root
    
    else:
        root1 = (-b - np.sqrt(discriminant)) / (2 * a)
        root2 = (-b + np.sqrt(discriminant)) / (2 * a)
        return root1, root2

    
# Use the function quadratic() to compute the roots of 0 = x^2 - 4a + 3
quadratic(1, -4, 3)
Out[31]:
(1.0, 3.0)

Exercise 3 - Functions with multiple outputs¶

  1. Define a function called cylinder that calculates the volume and surface area of a cylinder given inputs of the radius and height.
In [32]:
import numpy as np

def cylinder(r, h):
    volume = np.pi * r ** 2 * h
    surface_area = 4 * np.pi * r + np.pi * r ** 2 * h
    return volume, surface_area

Test your function on a cylinder with radius 1 and height 2 which gives a volume of 6.28 units cubed and surface area 18.85 units squared.

In [33]:
cylinder(1, 2)
Out[33]:
(6.283185307179586, 18.84955592153876)
  1. The series expansion of $\exp(x)$ is

    $$\exp(x) = \sum_{i=0}^\infty \frac{x^n}{n!} = 1 + x + \frac{x^2}{2!} + \frac{x^2}{3!} + \cdots $$

    Define a function called my_exp that uses a while loop to compute the value of $\exp(x)$ by adding the $n$th term at each iteration. Your while loop should stop when the value of the $n$th term is less than $10^{-6}$. Your function should return the value of $\exp(x)$ and the number of terms used to achieve the required accuracy.

In [34]:
def my_exp(x):
    expx, term, n = 1, 1, 0
    while term > 1e-6:
        n += 1
        term *= x / n
        expx += term
    
    return expx, n + 1

Test your function on $\exp(1) = 2.718281$ which requires 11 terms.

In [35]:
my_exp(1)
Out[35]:
(2.7182818011463845, 11)
  1. A method for calculating the value of $x=\sqrt{s}$ for some real number $s$ uses the iterative scheme

    $$ x_{n+1} = \frac{1}{2}\left(x_n + \dfrac{s}{x_n}\right).$$

    Define a function called my_sqrt which uses a while loop to calculate this scheme whilst $|x_{n+1} - x_n| > 5\times 10^{-5}$ where $x_0=1$ and $x_1=2$. Your function should output the approximation of $\sqrt{s}$ and the number of iterations used.
In [36]:
def my_sqrt(s):
    x0, x1, iterations = 1, 2, 0
    while abs(x1 - x0) > 5e-5:
        x0, x1 = x1, 1 / 2 * (x1 + s / x1)
        iterations += 1
    
    return x1, iterations

Test your function on $\sqrt{2}=1.4142\ldots$.

In [37]:
my_sqrt(2)
Out[37]:
(1.4142135623746899, 4)