Conor Lawless

Scientific Computing

data science, dynamic simulation modelling, genomics, interactive visualisation, dashboards, image & video analysis

If you’d like me to work on projects of any size, please get in touch
e: cnr.lwlss@gmail.com
t: @cnrlwlss

About me

Translating maths into code: writing the tattoo function in Python

tattoo00803

In a previous post I discussed a mathematical function which represents a range of different list-comprehensions and how their output can be visualised. Here I’ll describe in detail how we can evaluate this function on a computer, using the Python programming language as an example. Here is the mathematical notation for the tattoo function $t$ again:

$$t(\theta, p) = \left\{ \sum_{k=-1}^{n}e^{\frac{2 \pi i k^p}{\theta}} \mid n \in -1,\dots,\theta \right\}$$

What should we expect from mathematical expressions in code?

Ideally I would like to be able to write computer code representing expressions like the one above in a way that the relationship between the mathematical expression and the code is very obvious. Mathematics is the concise, elegant language of quantitative science. I don’t expect code to look exactly like mathematics, but I prefer the translation to be as intuitive and easy as possible. This makes code easier to think about, makes it easier to notice mistakes and facilitates conversations between people with backgrounds in programming and mathematics.

Mathematics in Python

Here is how I have written the tattoo function $t$ in Python:

import cmath

def tattoo(theta,p=3):
  return( [sum([cmath.exp((2*cmath.pi*1j*k**p)/theta) 
    for k in range(-1,n+1)]) 
    for n in range(-1,theta+1)] )

When you evaluate this function it returns a list as output. You can use the function like this:

# Evaluate function and assign result to variable cps
cps = tattoo(10,3)
# Inspect the resulting list
print(cps)         
# Inspect an element of the list
print(cps[4])
# Generate & store new lists containing 
# real & imaginary parts of cps	 
reals = [cp.real for cp in cps]  
imags = [cp.imag for cp in cps]

When accessing elements of a list, it’s important to note that lists in Python are 0-indexed (e.g. the first element of the list cps has index 0 and is accessed as cps[0]). See enumeration section below.

Complex numbers

In Python, the Imaginary number $\sqrt{-1}$ is represented by the letter j. We typically represent $\sqrt{-1}$ in mathematical notation with an $i$. The convention followed by Python comes from engineering: j is used instead to avoid potential confusion between using $i$ to represent an integer index and $i$ as an Imaginary number. The expression 1j above represents the complex number $\sqrt{-1}=0+j$

Complex number notation is available in Python straight out of the box, however, in order to enable mathematical operations on complex numbers, we need to load the cmath library (first line of code above). Although cmath is not loaded by default, it is a part of standard Python and so no extra installation steps are needed to use it. It’s worth noting here that Python is a general purpose programming language. It is not optimised specifically for maths or for scientific computing. Even if we only wanted to access standard mathematical functions that operate on real numbers, like exp, cos or sin we would still have to import math.

Function definition

On the next line, we define a function called tattoo using Python’s def command which takes two arguments: theta and p. If the user does not specify a value for the optional argument p, it is defined to take a default value of 3. However, as written above, there is no default value provided for theta. To use this function, the user must specify a value for theta.

Code and typesetting

Most programming languages do not support advanced typesetting of the type used to write down the mathematical function definition $t$ above. Python is typical in this regard, and we have to represent the Greek symbol for the variable $\theta$ from the mathematical expression with the word theta.

Similarly, when writing code, we generally do not have access to $\Sigma$ notation to represent summation of mathematical expressions. Instead, we use Python’s sum function which expects one argument: a list. Unsurprisingly, the sum function adds up all the elements in that list. In the Python function definition above the sum function takes the place of the $\Sigma$ operator.

Next, we have the expression cmath.exp((2*cmath.pi*1j*k**p)/theta), which is equivalent to $e^{\frac{2 \pi i k^p}{\theta}}$. Again, lack of typesetting features makes the code version more verbose, but I hope that you can see that cmath.exp(x) is a function representing $e^x$, that cmath.pi is equivalent to $\pi$.

Mathematical operators

The operators *, / and ** represent multiplication, division and exponentiation respectively. It’s worth bearing in mind that operators like these could be replaced by functions. For instance the code 2*cmath.pi could be replaced by an equivalent use of a multiplication function, mul which takes two arguments mul(2,cmath.pi). In Python (but not in all programming languages), to implement this, you would first need to load the operator library, which is installed as standard:

import operator
import cmath

# These two assignments are equivalent
x = cmath.pi*2
x = operator.mul(cmath.pi,2) 

List comprehensions

In the mathematical definition of the function $t$ above, a list (or set) is built by evaluating the expression:

$$\sum_{k=-1}^{n}e^{\frac{2 \pi i k^p}{\theta}}$$

for all integer values of $n$ from $-1$ to $\theta$ inclusive. The mathematical set-building notation used is directly equivalent to the concept of a list comprehension which is common to many programming languages, including Python, Julia, Haskell & Scala.

The Python list-comprehension I used to generate a list containing evaluations of the expression cmath.exp((2*cmath.pi*1j*k**p)/theta) for all values of k from -1, up to (but not including) n+1 looks like this:

[cmath.exp((2*cmath.pi*1j*k**p)/theta) for k in range(-1,n+1)]

In the body of the tattoo function, I add all of the elements of the resulting list by applying the sum function to this expression. The expression above could be written in mathematical notation as follows:

$$\left\{ e^{\frac{2 \pi i k^p}{\theta}} \mid k \in -1,\dots,n \right\}$$

Note that this expression does not actually appear in the function $t$, as it is wrapped up in the $\Sigma$ operator.

Enumeration

In mathematical notation, integer enumeration is usually represented using dot notation: $-1,\dots,n$. Python achieves this using the range function instead. Essentially, range builds an object very much like a list of integers, which you can then iterate through to build the output list. The input list does not have to be a list of integers; it could be a list of images, for example, or text files. In Python range(-1,n+1) enumerates all the integers from -1 up to, but not including n+1. When switching between programming languages and when switching between code and mathematical notation, it is important to check which convention is being used for the end of enumerations like this. Similarly, when referring to elements of an ordered list by index, it is important to check whether your programming language uses the convention that indices start at 0 or 1. Python follows the convention that indices start at 0.

Functional programming tools

In Python, and several other languages that support ideas from functional programming, these list-comprehensions could be replaced by the map function, however the majority of Python programmers find using list-comprehensions clearer than using map: in principle your code should look closer to mathematical notation with list-comprehensions. Similarly, we could write our own version of the sum function by using another functional programming tool: reduce (called fold in many other languages). Support for functional programming style is a great feature of Python, particularly when we come to parallel execution of tasks across multiple CPUs, but these tools are not necessary for this simple example.

Conclusion

The Python code for the tattoo function I’ve written above looks different to the equivalent mathematical notation for $t$. However, I hope that you can see a fairly straightforward corresponding relationship between the two. This correspondance is largely enabled by Python’s support for list-comprehension notation.

Hopefully, in some later posts, we will see that being able to access other built-in functionality as well as the ecosystem of extension packages for Python brings some extra advantages when visualising output and parallelising function evaluation. I will also examine how the code for this function would look written in a few other commonly used scientific computing languages. There are examples where this equivalence is clearer as well as examples where it is less clear.

tattoo00020

Tags: vector graphics art mathematics maths complex numbers visualisation list comprehension code