Python for Quantum Mechanics:#

Line Plots#

from IPython.display import YouTubeVideo
YouTubeVideo('D50f3tPYUiM',width=700, height=400)

matplotlib.pyplot is a collection of functions like those in MATLAB for plotting in Python. Used in conjuction with numpy, useful visualisations of data can easily be created. We will import both libraries in the next cell.

import matplotlib.pyplot as plt
import numpy as np

Lets create two NumPy arrays each containing the \(x\)- and \(y\)-coordinates respectively of a two points, \((0,1)\) and \((3,5)\). The plt.plot() function in Matplotlib can be used to plot the line between these two points.

x_points = np.array([0,3])
y_points = np.array([1,5])

plt.plot(x_points, y_points)
plt.show()
../../_images/3d21955d1949263b55ecf7a2f67e2a4c50700be1fbe2b90e4489ec41067e6075.png

The plt.show() isn’t stricktly necessary for jupyter notebooks but is a good habit to be in going forward.

We can also plot longer arrays or coordinates and we get a line connecting each point succesively.

x_points = np.array([-9.5,0,2.0,3,10.2])
y_points = np.array([1,5,3.3,1,-2.3])

plt.plot(x_points, y_points)
plt.show()
../../_images/3170ff51e65214f787c0dbc89d2f87b98d9f8369f5c0a21c7add4e8f3c889524.png

Now we can define a function that returns a polinomial and plot that using np.arange() to sample the \(x\)-axis and plug this sample into a function to get a graph of that function.

def f(x):
    '''Docstring: This returns 5x^3+6x^2-6x-2'''
    return 5*x**3 + 6*x**2 - 6*x -2

x_points_rough = np.arange(-2,1.5,.5)
y_points_rough = f(x_points_rough) # all of the operations are in f(x) are overloaded for NumPy arrays

plt.plot(x_points_rough, y_points_rough)
plt.show()
../../_images/97cac826a89c77fb3480949c62a8a5c3a370400de99eab6f73cdfee5dbc465ba.png

Here we have sampled the \(x\)-axis between \(-2\) and \(1.5\) (exluding \(1.5\)) in steps of \(0.5\). This approximates the shape of a cubic, however if we sample more frequently (for example stelps of \(0.1\) as seen below), we can achive something approximating a smooth curve.

x_points_fine = np.arange(-2,1.5,.1)
y_points_fine = f(x_points_fine)

plt.plot(x_points_fine, y_points_fine)
plt.show()
../../_images/6858da3cd3a9bee88d9b78f1b33c6d210c71fc63094cfec0877a18bc1fa2c23c.png

We can plot both lines on the same graph by calling plt.plot() twice and have two plots on the graph which will automatically be set to differnt colours.

plt.plot(x_points_rough, y_points_rough)
plt.plot(x_points_fine, y_points_fine)
plt.show()
../../_images/fd94fe8a41c72febda82158243a64264a40893d38e41fa04c6247c21ae97402b.png

Or we can graph multiple plots in the same plt.plot() call:

plt.plot(x_points_rough, y_points_rough, x_points_fine, y_points_fine)
plt.show()
../../_images/fd94fe8a41c72febda82158243a64264a40893d38e41fa04c6247c21ae97402b.png

or prehaps more elegantly

plt.plot(
    x_points_rough, y_points_rough, 
    x_points_fine, y_points_fine)
plt.show()
../../_images/fd94fe8a41c72febda82158243a64264a40893d38e41fa04c6247c21ae97402b.png

Lines#

In the following section we will look at ways of formatting lines in our plots. We will use the following during the demonstartion.

x = np.arange(0,1,.05)

ex = np.exp(x)
e2x = np.exp(2*x)
e3x = np.exp(3*x)

Colour#

To specify the colour of a line we can use the color or c, keyword when calling plt.plot(). We can use the following named colours (sourced here) or specify a Hex Color Code.

Base Colors

Tableau Palette

CSS Colors

x = np.arange(0,1,.05)

ex = np.exp(x)
e2x = np.exp(2*x)
e3x = np.exp(3*x)

plt.plot(x, ex, c = 'hotpink')
plt.plot(x, e2x, color = 'tab:brown')
plt.plot(x, e3x, '#31AF3F')
plt.show()
../../_images/be9a46e8248685fa5142de714c5420fd0d638ed7c53a2dc9ef28fa394513afa5.png

If no colour is specified the ‘Tableau Palette’ will be cycled through as seen below

plt.plot(
    x, x, 
    x, 2*x, 
    x, 3*x,
    x, 4*x, 
    x, 5*x, 
    x, 6*x, 
    x, 7*x, 
    x, 8*x, 
    x, 9*x, 
    x, 10*x, 
    x, 11*x, 
    x, 12*x, 
    x, 13*x, 
    x, 14*x, 
    x, 15*x, 
    x, 16*x)
plt.show()
../../_images/038df6cda27440781f57fbc917790ade10f2e2e15d09f7dd66540f5edd394892.png

Width#

The line width can be specified with the keyword linewidth or lw and has default value of 1.

plt.plot(x, ex, lw = '1.5')
plt.plot(x, e2x, linewidth = '2.8')
plt.plot(x, e3x, lw = '10')
plt.show()
../../_images/dd159117de4f9a83a1510ef4c0bbaf2506b1b50775c978e9a00bc33fe919753b.png

Line style#

The line’s style can be specified with the keyword linestyle or ls and is defaulted to 'solid'. The other options are

Line

Name

Shortcut

solid

'solid'

'-'

dashed

'dashed'

'--'

dotted

'dotted'

':'

dashdot

'dashdot'

'-.'

'None'

''

plt.plot(x, ex, linestyle = ':')
plt.plot(x, e2x, ls = 'dashed')
plt.plot(x, e3x, '-.')
plt.show()
../../_images/20e9198c4ebd97079fce7f7cf3415bdf889ffabc8a728f0a5851ed567051ddff.png

The 'None' line style might seem pointless but it can be used when only the point markers are needed which we will seen next. A more complete description of the line style formatting options can be found here.

Marker Types#

A marker type can be specified in a similar way to line styles with the keyword 'marker'. A list of the marker types can be found here. When the marker type specified without a keyword the the line style defaults to 'None' showing only the data points without the connecting lines.

x = np.arange(0,1,.05)

plt.plot(x, ex, '.') # dots
plt.plot(x, e2x, marker = '*') # stars
plt.plot(x, e3x, marker = '$hi$', ls = 'None')
plt.show()
../../_images/c24e1c43416bf14fa93cb5a7d6c1eb025c18a26548e1daffda1a515f2a780c1c.png

I should note that the keyword markerfacecolor and markersize exist and they do as you might expect. I will further note that we can assign colour and marker in a single compact form as seen below. This is useful for quick assignment but might get confusing and/or limit customisability.

plt.plot(x, ex, 'rx', x, e2x, 'bo', x, e3x, 'g--x')
plt.show()
../../_images/b8eaa35eb84fc1c4606b5d4aab53151cbcf3b966a4d4a70de108e14c65e71e28.png

Axes#

Limits#

We have seen above that the size of our graph changes and rescales. You can set the \(x\)- and \(y\)-axis limit with plt.xlim() and plt.ylim() as shown.

qbits = [0,1,2,3,4,5,6,7]
probs = [.02,.23,.32,.03,.02,.13,.2,.05]

plt.plot(qbits,probs,'_',markersize = 20)
plt.ylim([0,.4])
plt.show()
../../_images/c3787ae54a7c0615cb3c1d504254ccc9a21ca1d842ef8b6e8ef70e0b2a2951db.png

Ticks#

We can reformat the \(x\) and \(y\) ticks in the following way.

x_ticks = [0,1,2,3,4,5,6,7]
y_ticks = np.arange(0,1,.02)
x_labels = ['000','001','010','011','100','101','110','111']

plt.plot(qbits, probs, '_', markersize = 20)
plt.xticks(ticks=x_ticks, labels=x_labels)
plt.yticks(ticks=y_ticks)
plt.ylim([0,.4])
plt.show()
../../_images/029474b1c7e11820f19cad3a3f750f0550ee0adf4bfd6fd6c8900e83a22d4671.png

This effect is however better achieved by a histagram or bar chart that we will look at later.

Grid#

Often the clarity of demonstrating the values of the points can be assisted by adding a grid of horizontal and verticle lines stretching from the ticks. This can be toggled on/off using plt.grid().

plt.plot(qbits,probs,'o')
plt.grid()
plt.show()
../../_images/a441068a9312a6902c7d245156b9569b5f148b37298b3345517a356798780181.png

Lables#

Lables are a great way to explain what it is your are plotting. In the strings used to label a lot of \(\LaTeX\) is supported which can be very useful.

Title#

A title string can be added using plt.title().

plt.plot(x, ex, ':')
plt.plot(x, e2x, '--')
plt.plot(x, e3x, '-.')

plt.title('Exponentials')

plt.xlim([0,1])
plt.ylim([0,18])

plt.grid()

plt.show()
../../_images/dee18db35c5c12a195e5b73f37a6aec2de6e36a30d4e7dc56a737c4ba1bf1bf7.png

Axis Lables#

Similarly, plt.xlabel() and plt.ylabel() can be used to add labels to the \(x\)- and \(y\)-axes respectively.

plt.plot(x, ex, ':')
plt.plot(x, e2x, '--')
plt.plot(x, e3x, '-.')

plt.title('Exponentials')
plt.xlabel('this is the $x$-axis') 
plt.ylabel('this is the $y$-axis')

plt.xlim([0,1])
plt.ylim([0,18])

plt.grid()

plt.show()
../../_images/55b37336d2df03ba7a3781864a54ab0a7796d1634e195e32d015b9ca68f2fde7.png

Legend#

In plt.plot() we can assign a name to what is being plotted using the label keyword. A legend displaying this can then be toggled on/off using plt.legend() with keyword loc to set a location.

plt.plot(x, ex, ':', label = '$e^x$')
plt.plot(x, e2x, '--', label = '$e^{2x}$')
plt.plot(x, e3x, '-.', label = '$e^{3x}$')

plt.title('Exponentials')
plt.xlabel('this is the $x$-axis') 
plt.ylabel('this is the $y$-axis')

plt.xlim([0,1])
plt.ylim([0,18])

plt.grid()
plt.legend(loc = 'upper right')

plt.show()
../../_images/9db4e7161f5baa13767e1bbfb92a870977e792141ac1a99598b55259c85947d7.png

Error Bars#

If we call plt.errorbar() instead of plt.plot() we can add error bars in the \(x\) and \(y\) directions by specifying either a constant error or a list as the xerr and yerr. These error bars can be formatted in various ways as shown with capsize and here.

plt.errorbar(x, ex, ls = 'None',  marker = '.', xerr=.02, yerr=.4, capsize = 3)
plt.errorbar(x, e2x, ls = 'None', marker = 'x', xerr=.01, yerr=.2*e2x, capsize = 2)
plt.errorbar(x, e3x, ls = 'None', marker = 'o', xerr=x/10, yerr=x, capsize = 0)
plt.show()
../../_images/b8ca5e21e1077f0597acd9676734eb6a928d74c1d571e63af43d937f8261fbfe.png

Asymptotes and Annotations#

Verticle and horizontal lines can be added using plt.vlines() and plt.hlines which takes in the value the line is at and the begining and end of the line as arugaments as shown. One can also have an array of vales for this which will give many lines.

The function annotate plt.annotate() is ver useful when you want to draw attention to a particular point in your plot. It takes in a string that will be displayed and the point you are interested in. One can change the text colour as you might expect by calling the c keyword. The text can be put at different point to the point of intrest using xytext and an arrow can be generated between using arrowprops and giving a dictionary that will specify the features as shown.

xs = np.arange(-7,7,.01)
ys = np.abs(xs - 1/(3-xs))

plt.xlim([-7,7])
plt.ylim([0,6])


plt.plot(xs,ys)

plt.vlines(3,0,10,color='r',ls='dashed',lw=.8)


plt.annotate('asymptote',(3.2,3),c='r')
plt.annotate('function not\nanalytic here',(2.6,0),c='w',xytext=(-1.7,2.5),arrowprops={'arrowstyle':'->', 'lw':1})
plt.annotate('function not\nanalytic here',(.35,0),xytext=(-1.7,2.5),arrowprops=dict(arrowstyle='->',lw=1))

plt.show()
../../_images/f1955572d6103693c5ce8463dfaf154050a06edfc8e0eeed297fcd9d01a50eec.png

Log Plot#

Very often data is better displayed using logarithmic scaling on the axes.

Log-log plot#

One can make a log-log plot using plt.loglog() in the place of plt.plot().

x = np.arange(0.1,20,.01)
y = np.exp(x)

plt.loglog(x,y)
plt.grid()
plt.show()
../../_images/90d10280097add87485f89fb3238b565689cb783746e217e8c9eddb347f9bb7b.png

Semi-log plot#

We can also scale just one axis with a log scaling.

x = np.arange(0.1,20,.01)
y = np.exp(x)

plt.semilogy(x,y)
plt.grid()
plt.show()
../../_images/fbe930974e514c6baa898510215c3d3f81e4f27fa8481fed390656df814f9a51.png
x = np.arange(0.1,20,.01)
y = np.log(x)

plt.semilogx(x,y)
plt.grid()
plt.show()
../../_images/72a6e90e52485b3e4bfd51311834fb9bd2ec7b5747eb1e88b1c5f75e233e91f6.png

Figure Object#

So far we have been plotting rather recklessly without looking into what objects we have been creating in our plots. This is fine when we only want to get a quick look at our data but for puplication or the like we will require more costomisability and to put this on more solid footing.

One creates a figure object using the plt.figure() function. We can specify the figure dimensions in inches as a tupple using the figsize keyword. We then can add a a set of axes to this figure using the .add_axes() attribute funtion of the figure. This function takes in the argument of a list that specifies things about our axes. The list elements are taken as the [left, bottom, width, height] as fractions of the size height an width.

fig = plt.figure(figsize=(9,4))
ax = fig.add_axes([0.1,0.1,.8,.8])
../../_images/71b81fecfab83d31f71502454fbfb60e45f93790e083578708ea0275f5280850.png

We can then plot on this set of axes, now using the attribute of the axis we can plot in a similar way.

fig = plt.figure(figsize=(9,4))
ax = fig.add_axes([0,0,1,1])

x = np.arange(0,10.2,.2)
ax.plot(x, np.sin(x), c='green', lw=.5, ls='--', marker='*', label='$\sin(x)$')
ax.plot(x, np.cos(x), c='blue', lw=2, ls='-.', label='$\cos(x)$')

ax.set_xlim(0,10)
ax.set_ylim(-1.03,1.03)
ax.set_title('Trig Functions')

ax.legend()
ax.grid()
<>:5: SyntaxWarning: invalid escape sequence '\s'
<>:6: SyntaxWarning: invalid escape sequence '\c'
<>:5: SyntaxWarning: invalid escape sequence '\s'
<>:6: SyntaxWarning: invalid escape sequence '\c'
/tmp/ipykernel_2357/2392594966.py:5: SyntaxWarning: invalid escape sequence '\s'
  ax.plot(x, np.sin(x), c='green', lw=.5, ls='--', marker='*', label='$\sin(x)$')
/tmp/ipykernel_2357/2392594966.py:6: SyntaxWarning: invalid escape sequence '\c'
  ax.plot(x, np.cos(x), c='blue', lw=2, ls='-.', label='$\cos(x)$')
../../_images/e883ebfdbfc060de7e737f969cb76a7c84c54aff5745dbbef7f236fb1810e6e2.png

Many of the functions act the same as before but now they are attributes of the axis object and have set_ before them.

We can also not save this image.

fig.savefig('first_image.png')