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

e: cnr.lwlss@gmail.com

t: @cnrlwlss

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\}$$

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.

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.

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`

.

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`

.

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$.

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)
```

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.

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`

.

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.

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.

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