Introduction To Programming For Engineers Using Python
Introduction To Programming For Engineers Using Python
using Python
by
Logan G. Page
Daniel N. Wilke
Schalk Kok
Updated:
December 2012
Table of contents
Table of contents
ii
Chapter 1:
Introduction
1.1 Computer components . . . . . . . . .
1.2 What is a computer program? . . . . .
1.2.1 Examples of computer programs
1.3 Developing a program . . . . . . . . .
1.3.1 Flowcharts . . . . . . . . . . . .
1.4 Teaching philosophy . . . . . . . . . .
1.5 Formats used in these notes . . . . . .
.
.
.
.
.
.
.
1
1
2
2
3
3
4
5
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
7
8
10
11
14
15
16
17
18
19
20
22
24
26
31
31
33
35
36
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Chapter 2:
Python introduction
2.1 Installing Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 Using Python(x,y) . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.3 Interactive Python Console . . . . . . . . . . . . . . . . . . . . . . . .
2.3.1 Importing Modules, Functions and Constants . . . . . . . . .
2.3.1.1 Importing Modules . . . . . . . . . . . . . . . . . . .
2.3.1.2 Importing Functions . . . . . . . . . . . . . . . . . .
2.3.1.3 Importing Modules (The Wrong Way) . . . . . . . .
2.3.2 Math Module Computations . . . . . . . . . . . . . . . . . . .
2.3.3 Help Function . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.3.3.1 Help for Modules and Functions - help(function)
2.3.3.2 Interactive Help - help() . . . . . . . . . . . . . .
2.4 Programming Mode (Spyder) . . . . . . . . . . . . . . . . . . . . . .
2.4.1 Navigating the directory structure from Python . . . . . . . .
2.4.1.1 Summary . . . . . . . . . . . . . . . . . . . . . . . .
2.4.2 Python Script Names . . . . . . . . . . . . . . . . . . . . . . .
2.5 Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.6 Supplementary Sources . . . . . . . . . . . . . . . . . . . . . . . . . .
2.7 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Chapter 3:
Names and Objects
39
3.1 Numerical Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.1.1 Memory Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3.1.2 Example: Swapping objects . . . . . . . . . . . . . . . . . . . . . . 43
Page ii
Table of contents
iii
3.1.3
3.1.4
3.2
3.3
3.4
3.5
3.6
Assignment Operators . . . . . . . . . .
Integers vs. Floats . . . . . . . . . . . .
3.1.4.1 Why should you care about this
3.1.4.2 How to avoid integer division .
String Objects . . . . . . . . . . . . . . . . . . .
Boolean Objects . . . . . . . . . . . . . . . . . .
Acceptable Names . . . . . . . . . . . . . . . .
Summary . . . . . . . . . . . . . . . . . . . . .
Exercises . . . . . . . . . . . . . . . . . . . . . .
Chapter 4:
Unconditional loop
4.1 The range function . . . . . . . . . . . . .
4.2 The for loop statement . . . . . . . . . .
4.2.1 Example 1 . . . . . . . . . . . . . .
4.2.2 Example 2 . . . . . . . . . . . . . .
4.2.3 Example 3 . . . . . . . . . . . . . .
4.2.4 Example 4 . . . . . . . . . . . . . .
4.2.5 Indentation . . . . . . . . . . . . .
4.3 Summing using a for loop . . . . . . . . .
4.4 Factorial using a for loop . . . . . . . . .
4.5 Fibonacci series using the for statement .
4.6 Taylor series using the for loop statement
4.7 Exercises . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. . . . . . . . .
. . . . . . . . .
integer division
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Chapter 5:
Conditional loop
5.1 The while loop statement . . . . . . . . . . . . . .
5.2 Conditions . . . . . . . . . . . . . . . . . . . . . . .
5.2.1 Conditions as booleans . . . . . . . . . . . .
5.2.2 Conditions as questions . . . . . . . . . . . .
5.2.3 Examples of conditions . . . . . . . . . . . .
5.3 Simulating a for loop statement using a while loop
5.4 Taylor series using the while loop statement . . . .
5.5 Exercises . . . . . . . . . . . . . . . . . . . . . . . .
Chapter 6:
Branching
6.1 The if statement . . . . . . . . . . . . . . .
6.2 Simple example . . . . . . . . . . . . . . . .
6.3 Leg before wicket example . . . . . . . . . .
6.4 Number guessing game . . . . . . . . . . . .
6.5 The if statement with combined conditions
6.6 Exercises . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
statement
. . . . . .
. . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. . . . . .
. . . . . .
behaviour?
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
45
46
48
48
49
51
52
53
54
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
57
59
60
61
62
63
63
64
66
67
69
72
76
.
.
.
.
.
.
.
.
79
79
80
81
82
84
85
86
89
.
.
.
.
.
.
93
93
97
98
101
106
107
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Chapter 7:
Additional examples and statements
111
7.1 Additional statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
7.1.1 lambda function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
Page iii
Table of contents
iv
7.1.2
7.1.3
7.2
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
113
116
116
117
118
121
122
122
124
129
134
135
135
136
136
138
141
Chapter 8:
Numerical integration
147
8.1 Numerical integration using a while loop statement . . . . . . . . . . . . . 151
8.2 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
Chapter 9:
Data containers
9.1 Available Data Containers in Python . . . . . . . . . .
9.1.1 Lists . . . . . . . . . . . . . . . . . . . . . . . .
9.1.2 Memory model . . . . . . . . . . . . . . . . . .
9.1.2.1 Joining lists the plus operator (+) .
9.1.2.2 Indexing a list . . . . . . . . . . . . .
9.1.2.3 The append and insert functions . .
9.1.2.4 Looping through a list . . . . . . . . .
9.1.2.5 Disadvantage of using lists . . . . . . .
9.1.2.6 Working with lists examples . . . . .
9.1.3 Tuples . . . . . . . . . . . . . . . . . . . . . . .
9.1.3.1 Disadvantages of using a tuple . . . .
9.1.3.2 Working with tuples examples . . . .
9.1.4 Dictionaries . . . . . . . . . . . . . . . . . . . .
9.1.4.1 Adding to a dictionary . . . . . . . . .
9.1.4.2 Looping through a dictionary . . . . .
9.1.4.3 Disadvantage of using dictionaries . .
9.1.4.4 Working with dictionaries examples .
9.1.5 Summary . . . . . . . . . . . . . . . . . . . . .
9.2 Revisiting previous programs . . . . . . . . . . . . . . .
9.2.1 Taylor series using lists . . . . . . . . . . . . . .
9.2.2 Numerical integration using lists . . . . . . . . .
Page iv
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
159
159
159
161
162
163
164
167
167
169
171
172
173
174
176
177
177
178
179
180
180
182
Table of contents
9.3
9.4
Sorting algorithms . . . . . . . . .
9.3.1 Bubble sort algorithm . . .
9.3.2 Containers inside containers
Exercises . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
186
188
192
195
.
.
.
.
.
.
.
.
.
.
.
.
.
.
197
201
204
205
208
209
211
213
216
218
220
221
223
226
230
.
.
.
.
.
.
.
.
.
235
236
238
239
240
245
247
251
254
255
.
.
.
.
.
259
259
261
264
265
267
.
.
.
.
.
269
269
271
274
279
282
Table of contents
vi
13.2 3D Graphs . . . . . . . . . . . . . . . . . . . . . . . . . . .
13.2.1 The plot3D command . . . . . . . . . . . . . . . .
13.2.2 The plot wireframe and plot surface commands
13.3 Subplots . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13.4 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
282
283
284
289
292
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
311
311
311
312
314
316
321
326
334
338
341
347
Page vi
Chapter 1
Introduction
Welcome to the Introduction to Programming course. In this course, you will develop
the skills required to develop computer programs in Python. Python is an interpreter
(similar to translating german to english) that translates a program or script (readable by
humans) into instructions that can be understood and executed by the computer. Python
is freely available and free of charge (Open Source) as opposed to other programming
languages like Matlab, which is commercially available.
The course will focus on the types of programs engineers require i.e. programs that
compute, analyse and simulate a particular system. I use mathematics to teach programming because mathematics require the same logic as programming. Im of the opinion
that if you are good at (applied) mathematics, you will be a good programmer. Furthermore, if you are capable of turning mathematical statements into a program, you will be
able to translate any instructions into a program.
1.1
Computer components
Before we start to develop new programs, lets quickly review what equipment well need.
The most obvious is the computer itself. A computer consists of
1. A central processing unit (CPU). This is the brain of the computer. It performs all
the computations required to perform a certain task. The Intel Pentium iX CPU is
most common today.
2. Memory. Personal computer (PC) memory is referred to as RAM (Random Access
Memory). The contents of the memory is wiped out if the computer is switched off.
Page 1
Chapter 1. Introduction
3. Storage device. Usually a permanently mounted magnetic hard drive. The data
stored on this device is stored even after the computer is switched off. You save all
your files to such a device.
An operating system manages the above components to perform required tasks. Windows
XP, 7 and Ubuntu are examples of modern operating systems.
1.2
A computer program is a list of instructions for the computer to execute / run. Computers
are perfect slaves: they perform our tasks without complaint and exactly according to
our instructions. Above all, they perform these tasks extremely fast and accurate (they
never forget their instructions or their multiplication tables). This implies of course that
you need to know exactly which instructions to give in order to obtain the required
result. If you dont exactly understand what you want to do and how to do it, your list of
instructions to the computer (i.e. your program) will contain logical errors. The computer
will execute these flawed instructions exactly as stated, and will obtain the wrong result.
This is the reason for one of the famous computer acronyms GIGO (Garbage In, Garbage
Out).
1.2.1
You should already be familiar with many computer programs. The suite of Microsoft
Office programs are programs used to create documents, spreadsheets and presentations.
Games are programs for entertainment. Those of you familiar with the Windows operating system, a long list of programs appear if you click on the Start menu, followed by
Programs.
The components of a computer program are
Input.
Processing.
Output.
There can be multiple inputs, processing and outputs. Before any processing can
take place, the necessary inputs have to be obtained. In this course, the processing
Page 2
Chapter 1. Introduction
part is what we will focus most of our attention on. Given certain inputs, we have to
give explicit instructions on how to manipulate these inputs to provide a required result.
Once the processing is complete, we can provide outputs. The loop can then repeat itself,
if necessary.
Even a computer game contains these components. Input is generally obtained from
the keyboard, mouse or game controller (joystick), some processing is then performed
and output is generated to the screen (in the form of detailed graphics). This input,
processing, output loop runs continuously, or at least until you quit the game.
1.3
Developing a program
Just because you can use programs that other people have developed, does not mean that
you can develop programs yourself. It is a completely different scenario to be the user of
a program versus the developer. As a student that has to master the skill of developing
new programs, you will have to follow a few basic steps.
1. Get a clear idea of what your program must do. If the program is complicated,
break it into smaller chunks. Make sure you know what task each of these chunks
must perform.
2. Decide what inputs are required for each of the tasks that your program must
perform. Acquire these inputs.
3. Process the inputs as required. The processing could be as simple as sorting a list
of inputs, or could be very complicated e.g. the numerical simulation of a large
system.
4. Produce the required output. Your program is useless if it processes the inputs
correctly, but produces improper or incomplete outputs. The ultimate test of your
program is if it produces the correct output.
1.3.1
Flowcharts
It is common practise to use flowcharts during the initial program development stages.
Flowcharts are schematic representations of a computer program that illustrate the flow
of information through this program. A large number of flowchart symbols can be used,
but for the sake of simplicity we will only use the following three symbols:
Page 3
Chapter 1. Introduction
If you have difficulty developing a program directly (i.e. you generate code as you
figure out your program), try using flowcharts. It is sometimes very useful to organise your
thoughts. It also provides a schematic outline of your program, which can help to keep the
big picture in mind. Flowcharts are not really necessary to develop small programs that
produce few outputs, but as the complexity of the program increases flowcharts become
indispensable.
An example of a flowchart is depicted in Figure 1.1. The program reads a percentage
and checks if it is greater than 50%. If so, it assigns some symbol. If not, it assigns the
F symbol. The program then displays the chosen symbol. The details of program is not
important here: Im only trying to give you an example of a flowchart.
It should be clear from the flowchart above that this example has been broken up into
two chunks (assign the F symbol; and compute other symbol) and only the first chunk of
the program has been outlined with the flowchart. The second chunk compute symbol
could also be outline with a flowchart, which would have the inputs (yes, percentage)
and give the output (symbol).
1.4
Teaching philosophy
The main goal for this module is to acquire basic programming skills. These skills can only
be acquired through practise. It is no use to only use existing programs or to study the
examples of other programmers. You have to repeat all the examples in the lectures and
notes in Python yourself, and not just simply read or copy them. Try to understand what
concept is being taught and then try write a particular Python program from scratch,
and only if you get completely stuck, refer to the class example. Just study the example
long enough to get going, then close your notes and try to continue.
Page 4
Chapter 1. Introduction
1.5
In these notes I will make use of different text formatting as well as differently formatted
blocks of text or code. Below, I have listed the different formats used in these note as
well as an explanation of there purpose:
Page 5
Chapter 1. Introduction
More Info:
This formatted text block will be used to provide you with additional information
regarding a certain topic being discussed.
Take Note:
This formatted text block will be used for important information that you must
be aware of. It is recommended that you take a moment to fully understand the
information given in this type of text block and where possible practise the concepts
being discussed.
The following formatted block will be used for program outputs as well as programs
typed in the IPython Console. This block will have line numbers on the left side of the
frame and a light grey font colour inside the frame.
In [2]:
The following formatted block will be used for programs typed up in the Spyder. This
block will have line numbers on the left side of the frame and colour highlighted code
inside the frame.
for i in range(10):
print i
print "finished"
Page 6
Chapter 2
Python introduction
Python was developed by Guido van Rossum in the late eighties and early nineties at the
National Research Institute for Mathematics and Computer Science in the Netherlands.
Python is currently one of the most popular dynamic programming languages, along with
Perl, Tcl, PHP, and Ruby. Although it is often viewed as a scripting language, it is
really a general purpose programming language along the lines of Lisp or Smalltalk (as are
the others, by the way). Today, Python is used for everything from throw-away scripts to
large scalable web servers that provide uninterrupted service 24x7. It is used for GUI and
database programming, client- and server-side web programming, and application testing.
It is used by scientists writing applications for the worlds fastest supercomputers and by
children first learning to program.
Python is a high-level, interpreted, interactive and object oriented-scripting language.
Python was designed to be highly readable which uses English keywords frequently where
as other languages use punctuation and it has fewer syntactical constructions than other
languages.
1. Python is Interpreted: This means that it is processed at runtime by the interpreter
and you do not need to compile your program before executing it. This is similar
to PERL and PHP.
2. Python is Interactive: This means that you can actually sit at a Python prompt
and interact with the interpreter directly to write your programs.
3. Python is Object-Oriented: This means that Python supports Object-Oriented style
or technique of programming that encapsulates code within objects.
4. Python is Beginners Language: Python is a great language for the beginner programmers and supports the development of a wide range of applications, from simple
text processing to WWW browsers to games.
Page 7
2.1
Installing Python
Python is available for download from the new Click-UP system. It is recommended that
you download the installation file from campus to save yourself internet usage from home.
Insert your flash disk into a USB port on a computer in the computer labs.
Open Internet Explorer (or your favourite internet browser)
Log in at the University Portal and go to the new Click-UP system. Then go to the
Downloads section of the MPR213 module page.
Click on the Python(x,y)-2.7.3.1.exe file and save it to your flash disk. The file
size is 510 MB so make sure you have enough space on your flash disk.
More Info:
Page 8
Insert your flash disk into a USB port on your personal computer.
Open My Computer and navigate to where you saved the Python(x,y)-2.7.3.1.exe
file on your flash disk.
Double click the Python(x,y)-2.7.3.0.exe file to install it.
The Python(x,y) 2.7.3.1 Setup Wizard box will be displayed and in it the License
Agreement box, read through the licence agreement and then click I Agree to proceed with the installation.
The Choose Components box will be displayed. Choose either Full or Recommended
for the Select the type of install: drop down box. Then click Next to proceed with
the installation.
The Choose Install Location box will be displayed. Click Next to proceed with the
installation.
The Choose Start Menu Folder box will be displayed. You are now ready to install
Python, click Install to install Python.
Page 9
10
It will take several minutes to install Python, but when the installation has finished,
the Installation Complete box will be displayed. Click Next / Finish to exit the
Setup Wizard. You are now ready to run Python.
More Info:
You can find additional information about Python(x,y) and the addition packages
installed along with Python from:
http://code.google.com/p/pythonxy/wiki/StandardPlugins
Clicking on the package link will take you to that packages official website where you
can get more information.
2.2
Using Python(x,y)
You start the Python(x,y) Home Menu (shown in Fig. 2.1) by double clicking on the
Python(x,y) icon on the desktop or by going to the Start menu, then Programs, then
Python(x,y).
Page 10
2.3
11
To start an interactive IPython Console click on the console button as shown in Fig.
2.1. The optional interactive consoles to choose from in the drop down box are: IPython
(sh), IPython (Qt) and Python. For the sake of this course it is not important to know
the difference between the different consoles and you are free to use any one of them. All
of these interactive consoles run Python !
In these notes all examples are given either using the IPython (Qt) interactive console
or using the S pyder editor as explained in the next section (Section 2.4).
In [1]: 2 + 3
12
In [1]: 2 + 3
Out [1]: 5
In [2]:
The special prompt (In [1]:) changes to (Out [1]:) and the output of 5 is displayed to indicate that our instruction was computed successfully. The special prompt
(In [1]:) is then incremented from 1 to 2 (In [2]:) indicating that Python is waiting
for the second input command. Notice that when you type you will see that there is a
space directly after the special prompt. It is only there for readability to separate your
instruction from the special prompt.
Take Note:
1) I number the program lines in all the examples I include in these notes. This is
only so that I can refer to specific program line/s without you having to count lines
from top to bottom.
2) Do not enter line numbers whenever you write a program in Python
Take Note:
1) The special prompt is the instruction number Python is waiting for, this is not a
line number for the code.
2) To avoid confusion I will replace the instruction number from the special prompt
with the sharp symbol (#) in the notes (e.g. In [#]:) unless required for the
purposes of explanation.
If we however type
In [#]: a
-------------------------------------------------------NameError
Traceback (most recent call last)
/home/logan/<ipython-input-5-60b725f10c9c> in <module>()
----> 1 a
Page 12
13
In [#]:
We see what is called a Traceback. This indicates that Python was unable to compute
our instruction. The Traceback traces the error to where it was occurred and Python
thus tells you why and where it could not compute your instruction. In this case, you can
see that above the special prompt there is a line that starts with NameError. Python
tells us here what type of error was encountered and then the message for this error type:
name a is not defined. This information tells us that Python does not know what
a is. Where an error is encountered will be discussed in the Debugging section of the notes
(Section TODO??).
We can use (or abuse) Python as a calculator, i.e. always use it in the interactive mode
to compute numerical values. The symbols used for simple computations are summarised
in Table 2.1.
Computation
Addition
Subtraction
Multiplication
Division
Power
Math Symbol
+
*
/
xy
I Python
In [#]:
In [#]:
In [#]:
In [#]:
In [#]:
Console
5 + 2
5 - 2
5 * 2
5 / 2
5 ** 2
More Info:
Both the IPython and Python consoles run Python. The IPython console has been
modified to be more user friendly and easier to use and debug, thus we will be using
the IPython Console in this course.
Page 13
2.3.1
14
Python on its own is a very lightweight programming language and has been designed in
such a fashion that not all usable functions in Python are available as soon as it starts
up.
For example if we enter the following into the I Python Console:
In [#]: cos(1)
-------------------------------------------------------NameError
Traceback (most recent call last)
/home/logan/<ipython-input-6-edaadd132e03> in <module>()
----> 1 cos(1)
In [#]:
Which tells us that Python doesnt know what cos is. In order to tell Python what
any extra functions (like cos) are we first need to bring them into the I Python Console,
only then will Python know what these functions are and what they do. Extra functions
and objects (like ) are stored in what is called a module.
You can consider these modules as folders on your computer. Folders are used to store
and structure many different files on your computer in order to make it easier for the user
to find and work with them, similarly modules store and structure different functions and
objects that can be used in Python.
For example the exponential (ex ), logarithms (ln, log10, etc.) and trigonometric
(cos, sin, acos, etc.) functions are stored in the math module. The objects and e
are also stored in the math module.
So how do we bring these extra functions into the I Python Console?
The answer is with the (import) statement. There are several ways to import these extra
function: you can either import the full module (Section 2.3.1.1) or you can import the
functions and objects (Section 2.3.1.2) contained inside a module.
Page 14
2.3.1.1
15
Importing Modules
Here I will show you how to import the math module. As you have guessed by now the
math modules contains mathematical functions and objects.
We import the math module by entering the following in the I Python Console:
We have now told Python what math is and thus we can now use the math module
in Python. If we type In [#]: math in the I Python Console we get the following
output:
In [#]: math
Out [#]: <module math (built-in)>
In [#]:
So we can see that Python knows that math is a module. To be able to use the
functions and objects inside the math module we use a dot (.) after a math statement as
follows:
In [#]: math.cos(1)
Out [#]: 0.5403023058681398
In [#]: math.sin(1)
Out [#]: 0.8414709848078965
In [#]: math.pi
Out [#]: 3.141592653589793
Looking at line 5 you can see that the dot (.) tells Python that the function cos
resides in the module math.
More Info:
Page 15
16
Type help(math) into the IPython Console (after importing the math module) to
see a list of all the functions stored in this module
More Info:
You can also import a module and give it a new name to use in your program:
In [#]: some_other_name.cos(1)
Out [#]: 0.5403023058681398
In [#]:
2.3.1.2
Importing Functions
If we want to only import (use) a few functions and objects from the math module we
can do that straight away by using the from statement as follows:
Here we are almost literal telling Python: from the module named math import the
object pi and from the module named math import the function cos etc.
In the example above you can see that there is a lot of repetitive typing: each time we
import a function or object we repeat from math import . Because we are importing
functions and object from the same module we can consolidate the above example into
one line as follows:
17
Now we tell Python: from the module named math import the object pi and the
function cos and the function sin etc.
So now that Python knows what these extra functions are we can easily use them as
follows:
In [#]: pi
Out [#]: 3.141592653589793
In [#]: cos(1)
Out [#]: 0.5403023058681398
In [#]: sin(1)
Out [#]: 0.8414709848078965
More Info:
Again you can also import a function from a module and give it a new name to use
in your program:
In [#]: some_other_name(1)
Out [#]: 0.5403023058681398
In [#]:
2.3.1.3
The following example will import everything contained inside the math module. This is
the wrong way of importing functions and objects from a module and should only be
used in rare / special cases.
This tells Python: from the module named math import everthing
These extra functions can now be used as shown previously:
Page 17
18
In [#]: cos(1)
Out [#]: 0.5403023058681398
In [#]: sin(1)
Out [#]: 0.8414709848078965
In [#]: pi
Out [#]: 3.141592653589793
Take Note:
Often you will find online documentation or online worked examples that use this
method for importing functions and constants and the only reason for this example
in the notes is to let you know that this is the wrong way of import functions and
constants. You should always use the import methods described in Sections 2.3.1.1
and 2.3.1.2 above, and only in very very rare cases should you use this method.
2.3.2
Now that we know how to bring these extra functions into the I Python Console lets look
at some of the functions in the math module. A list of a few of the math module functions
is shown below.
Exponential function: exp
e.g. e1 is computed by
In [#]: exp(1)
Base 10 logarithms are computed using log10
In [#]: log10(100)
Natural logarithms (ln) are computed using log
In [#]: log(2.718)
Trigonometric functions (radians): sin, cos and tan
In [#]: sin(1)
Inverse trigonometric functions (radians): asin, acos, atan
In [#]: atan(1)
Factorial n! is computed using the factorial command
e.g. 5! is computed as
In [#]: factorial(5)
Page 18
19
Play around with the operations above and make sure you know how to use them.
Take Note:
Trigonometric functions work in radians, i.e. if you want to compute sin(90 ), you
must first convert 90 to radian using the math.radians() function, e.g:
In [#]: from math import radians, sin
In [#]: sin(radians(90))
Out [#]: 1.0
In [#]:
2.3.3
Help Function
The interactive mode is also used whenever you need help on some Python command.
I have more than 3 years Python programming experience and I still sometimes forget
the correct syntax of an Python command. The help function is probably the Python
command I use most frequently, so I think it is good advice to get acquainted with the
help function as soon as possible.
If you simply type help in the I Python Console you will see the following output:
In [#]: help
Out [#]: Type help() for interactive help, or
help(object) for help about object.
In [#]:
As you can see from the above output there are two ways of using the help function
in Python.
More Info:
Syntax refers to the correct usage of a programming language command. It is similar
to making a grammar mistake in a real language. If you use a command incorrectly
Python will produce an error message, indicating that you made some mistake.
Page 19
20
More Info:
1) The help(object) statement is used for imported modules, functions and objects.
2) The help() statement starts an interactive help system in the IPython Console
and is used for core Python statements like and, +, for, if, etc.
2.3.3.1
In [#]: help(math)
NAME
math
FILE
(built-in)
DESCRIPTION
This module is always available. It provides access to
the mathematical functions defined by the C standard.
FUNCTIONS
acos(...)
acos(x)
acosh(...)
acosh(x)
21
asin(...)
asin(x)
asinh(...)
asinh(x)
atan(...)
atan(x)
atan2(...)
atan2(y, x)
atanh(...)
atanh(x)
.
.
.
(END)
The output above has been modified for easier display and printing of these notes,
try this at home to see the actual output. At the end of the help information you will
see an (END) line. Type q (short for quit) to exit the help information. You can also
get the help information on functions contained inside the math module, for example try:
help(math.cos).
Take Note:
Page 21
22
Remember to first import the module (in this case the math module) or function into
the IPython Console before using the help(object) statement !!
More Info:
In the IPython Console you can also use a single or double question (? or ??) after
the imported module, function or constant to get help information:
In [#]: cos?
Type:
builtin_function_or_method
Base Class: <type builtin_function_or_method>
String Form:<built-in function cos>
Namespace: Interactive
Docstring:
cos(x)
In [#]:
2.3.3.2
For interactive help you simply type help() in the I Python Console and press the Enter
key:
In [#]: help()
Page 22
23
help>
You will see that the Python help() command will display a welcome message and
then the special prompt will change from In [#]: to help>. The interactive help has a
wealth of information. Unfortunately typing help> modules into the help system will
most likely cause the I Python Console to crash (close), this is a bug that is currently
being fixed by the Python developers. Typing help> keywords or help> topics
works fine and will display a list of Python keywords and help topics respectively.
If you now type in a valid Python command, keyword or help topic name after this
special prompt, text will appear in the I Python Console that gives more detail about the
command you typed in. As an example, type help> + and press the Enter key. The
following text will appears in the I Python Console:
help> +
Summary
*******
+------------------+---------------------------------------+
| Operator
| Description
|
+==================+=======================================+
| +, -
| Addition and subtraction
|
+------------------+---------------------------------------+
| *, /,
| Multiplication, division, remainder
|
| //, %
| [8]
|
+------------------+---------------------------------------+
Page 23
24
-[ Footnotes ]-
The output above has been modified for easier display and printing of these notes,
try this at home to see the actual output. Again at the end of the help information you
will see an (END) line. Type q (short for quit) to exit the help information. Spend some
time using the help functions to become familiar with the Python documentation environment. This will enable you to help yourself in a way that would enable you to focus,
find and correct your problem. In addition it will allow you to explore functions that are
not taught in this course but that Python has to offer.
More Info:
If you dont want to go through the interactive help system you can enclose the
keyword in quotation marks, e.g: help(+), help(and), help(keywords),
etc.
2.4
The interactive modes discussed above are only useful for a few scenarios. The usual
situation when we are developing a program is that we want to execute a large collection
of commands. It is very limiting to enter and execute these commands one at a time in
interactive mode.
Rather, we will type out our collection of commands, one below the other, in a file,
save this file somewhere on out computer and then run the file (execute the collection of
commands). We will be using the Spyder IDE (Integrated Development Environment) for
creating Python scripts that contain our collection of commands. To start Spyder first
run the Python(x,y) Home Menu (as shown in Section 2.2) and click on the button to run
Spyder.
Page 24
25
26
Once the file is saved, you can execute the list of commands in the Python script by
typing run and the name of the .py-file in the IPython Console, followed by pressing the
Enter key:
Alternatively you can run the Python script directly from the Spyder IDE by clicking
on the Run menu and then on Run (Short-cut key: F5).
Take Note:
When running a Python script from the IPython Console, you have to make sure
that the IPython Console is pointing to the directory where you saved the .py-file.
See Section 2.4.1 for navigating the directory structure.
More Info:
The official Spyder website will give you more information on how to use Spyder as
well as how to use the individual windows inside the Spyder application:
http://packages.python.org/spyder/
2.4.1
The computer has a hard drive with many files on them e.g. pictures, music, system files
and executable programs. In order to keep track of where the files are they are grouped
together using a directory structure (almost like filing documents in a filling cabinet). The
Python package is grouped in its own directory. To become familiar with the directory
structure click on Start, then double click on My Computer. You will see icons and
folders depending on your computer. Double click on HDD (C:) this will let you go
to the C: hard drive and display the content of this hard drive. Now double click on
Program Files (x86). You will now see many directories. Search for the one called
pythonxy and double Click on it. You will now see some folders and files inside the
pythonxy directory. The window you are currently viewing the files and directories in is
called Windows Explorer.
When you start the IPython Console (depending on how you start it) it will point to
one of the following directories:
(A) C:\Users\<user name>
Or
Page 26
27
In [#]: pwd
Out[#]: uC:\\Users\\MPR213
In [#]:
Take Note:
Page 27
28
<user name> will be replaced with your user name on your computer, in my case
<user name> is equal to MPR213.
Take Note:
Windows systems use the backslash (\) to separate directories, files and drives. And
this notation will be used in the notes.
Python (on a Windows system) uses either two backslashs (\\) or a forward slash
(/) to separate directories, files and drives.
For now lets assume that the IPython Console is pointing the C:\Users\<user
name> directory. When I type ls the following appears on the screen
In [#]: ls
Volume in drive C has no label.
Volume Serial Number is 24A9-A80B
Directory of C:\Users\MPR213
2012/12/10
2012/12/10
2012/12/10
2012/12/13
2012/12/10
2012/12/10
2012/12/10
2012/12/10
2012/12/10
2012/12/10
2012/12/10
2012/12/10
2012/12/10
2012/12/10
2012/12/10
2012/12/10
2012/12/10
11:11 PM
11:11 PM
10:31 PM
11:01 AM
10:02 PM
08:46 PM
10:35 PM
08:46 PM
08:46 PM
08:46 PM
08:46 PM
08:46 PM
08:46 PM
08:46 PM
08:46 PM
11:11 PM
08:46 PM
1 File(s)
16 Dir(s)
<DIR>
<DIR>
<DIR>
<DIR>
<DIR>
<DIR>
<DIR>
<DIR>
<DIR>
<DIR>
<DIR>
<DIR>
<DIR>
<DIR>
<DIR>
.
..
.ipython
.matplotlib
.xy
Contacts
Desktop
Documents
Downloads
Favorites
Links
Music
Pictures
Saved Games
Searches
107 Untitled0.ipynb
<DIR>
Videos
107 bytes
33 892 466 688 bytes free
In [#]:
The output of ls shows the content of the current directory and it is the same as
Page 28
29
when you browse the directories using My Computer (Windows Explorer ). When using
Windows Explorer it is easy to distinguish between files and directories as the icons are
there to assist you.
When using ls you can distinguish files from directories in the following way. Files
usually have an extension i.e. a filename followed by a dot (.) and then followed by an
extension. For example, Untitled0.ipynb has a filename Untitled0 and an extension
ipynb. The extensions tell Windows about the content of the file so that when you
double click on it Windows knows what application or program to use to open the file.
The directories only have a name without an extension. This is because directories are
not opened by an application or program, they only group files. Therefore Windows only
needs to know the name of the directory you want to go to from your current directory to
go there. Directories also have a DIR keyword placed in the third column of information
when you use the ls command.
You can change from your current directory or current hard drive to another directory or hard drive by using the cd command. If you type cd Documents and press the
Enter key you have changed from your current directory (C:\Users\<user name>) to
the C:\Users\<user name>\Documents directory. If you now type ls the following is
displayed on your screen:
In [#]: pwd
Out[#]: uC:\\Users\\MPR213
In [#]: cd Documents
In [#]: pwd
Out[#]: uC:\\Users\\MPR213\\Documents
In [#]: ls
Volume in drive C has no label.
Volume Serial Number is 24A9-A80B
Directory of C:\Users\MPR213\Documents
2012/12/13
2012/12/13
2012/12/13
11:11 PM
<DIR>
.
11:11 PM
<DIR>
..
11:11 PM
0 New Text Document.txt
1 File(s)
0 bytes
2 Dir(s) 33 175 799 744 bytes free
In [#]:
Page 29
30
which shows you that there are 2 directories and 1 files in your current
(C:\Users\<user name>\Documents) directory. You can type cd .. to go back to the
directory you just came from. By typing cd .. you move one directory up (to the parent
directory). E.g. the pythonxy directory is in the Program Files (x86) directory and
therefore the Program Files (x86) directory is one directory up from (or the parent
directory of) the pythonxy directory.
To change to a different hard drive, first open My Computer. My computer will
show you which hard drives are available on your computer and what their drive letters
are (e.g. C, E, F). If I type cd F:/ I change from my C: hard drive to my F: drive. By
typing cd C:/ I am back on my C: drive.
Take Note:
To change drives you have to use the forward slash (/) after the drive letter and
double colon (:).
E.g. cd C: - wont work !
E.g. cd C:\ - wont work !
Only cd C:/ will work.
This is important when you work in the labs because you need to go to your H: drive
and save your data there, otherwise your data will be lost when you log out. This is also
important for tests and exams as you will be in the labs when you write them. If for some
reason your computer hangs or the power fails then at least the data you saved on your
H: drive will still be there.
By typing cd C:/ I have moved to the C: drive. I am now in the root directory of
my C:/ drive. It is called the root directory as there are no other directories up from this
directory. When you type ls you will see that there are no [.] or [..] in the list of
directories.
Let us go back to the pythonxy default directory by typing cd C:/Program Files
(x86)/pythonxy and pressing the Enter key. Note that I have used the forward slash
here to separate the drive and directories.
To show you what happens when you use cd incorrectly let us try to change directory
to a file. When you type cd License-en.rtf the following appears on the screen:
In [#]: cd License-en.rtf
[Error 267] The directory name is invalid: uLicense-en.rtf
Page 30
31
In [#]:
This is because License-en.rtf is a file (with the .rtf extension) and not a directory.
2.4.1.1
Summary
pwd - Prints the current directory, to where the IPython Console is pointing, to
the screen.
ls - Print the contents of the current directory to the screen.
cd directory name - Changes the current directory to the directory specified (in
this case directory name).
Examples:
cd C:/ - Changes to the C: root directory.
cd ..
cd
/Documents
2.4.2
It is extremely important to only use allowable Python script names. Python script names
can contain any alphabetical characters (a-z, A-Z) as well as all the numerals (0-9), but
the first character has to be an alphabetical character. Python script names may not
contain any spaces or special symbols (! @ # $ % & \ + - ) except the underscore
Page 31
32
( ). Python is also case sensitive, i.e. it distinguishes between upper case and lower case
letters.
It is therefore important that you enter the name of the file as you saved it. Therefore
if your Python script name with extension is program1.py then you have to run it by
typing run program1.py in Python. If you type run PROGRAM1.py then Python will
complain with a similar error message to the one below:
Use the ls statement to view the files in your current directory. How the files are
displayed (i.e. upper and lower case letters) using this command is also the way you
should type them in the IPython Console.
Take Note:
You should avoid giving a program the same name as any of the Python keywords:
type help(keywords) to see the list:
and
as
assert
break
class
continue
def
del
elif
else
except
exec
finally
for
from
global
if
import
in
is
lambda
not
or
pass
print
raise
return
try
while
with
yield
More Info:
Because Python is so widely used in all areas of industry and research, there is a programming standard (coding conventions) for how you write up you programs. This
standard can be found at:
http://www.python.org/dev/peps/pep-0008/
Although as engineers we are not programmers, I do believe in doing things right
from the start and as such I will be referencing sections of the PEP8 standard where
needed.
Page 32
33
2.5
Documentation
Python has a wealth of online documentation and worked out examples. If you ever get
stuck, or the information from the help() function is insufficient, Google or any other
search engine will be you best friend.
34
35
2.6
Supplementary Sources
The following sources have great examples and explanations on a lot of topics we will
discuss in these notes. So if at any stage you find yourself needing more help or additional
examples regarding a specific topic or concept these sources should be you first stop, there
after Google.
1. http://en.wikibooks.org/wiki/Non-Programmers Tutorial for Python 2.6
2. http://docs.python.org/2/tutorial/index.html
3. http://pythonbooks.revolunet.com/
There is also a series of 24 lectures titled Introduction to Computer Science and Programming delivered by Eric Grimsom and John Guttag from the MIT available at http://
academicearth.org/courses/introduction-to-computer-science-and-programming.
This is aimed at students with little or no programming experience. It aims to provide
students with an understanding of the role computation can play in solving problems.
Page 35
2.7
36
Exercises
(2.1)
300 154/11/2 + 16 6
(2.2)
and
(2.3)
(2.4)
[If you were wrong or are still unsure investigate grouping different operations.]
Take Note:
Use brackets to avoid confusion in you operations.
6. Compute the answer of
154 17
29 + 54 3
17
+ 54 3
29
Page 36
(2.5)
(2.6)
37
15 +
+ 121 4
3.5
(2.7)
10. What is the difference between the functions math.log and math.log10?
11. Compute the unknown variable b:
b4+2=
19 cos()
(2.8)
p
cos(/4)
(2.9)
13. This question is only to make you aware of Pythons complex number capabilities.
In this course we do not consider complex numbers, but I feel it is important to be
aware of it. Try the following examples in the IPython Console:
import cmath
cmath.sqrt(-1)
1+2j
(1+3j) + (9+4j)
(1+3i) * (9+4i)
You can type your own complex numbers in e.g. by typing 1+2j. You can add
complex numbers e.g. (1+3i)+(9+4i). You can multiply complex numbers e.g.
(1+3i)*(9+4i).
If you find the answers to be surprising dont worry, the formal theory behind
complex numbers will be treated in subjects to come.
Page 37
38
Page 38
Chapter 3
Names and Objects
When we used Python interactively as a calculator, each number we typed was actually
created as an object in the computers memory. So typing 1 + 2 into the IPython Console
will create three objects in memory: the object 1; the object 2; and the object 3 (the
result of 1 + 2). It is important to note that everything in Python is an object in memory.
Names are used in Python to reference these objects in memory. As engineers, we
usually work with numbers, so most of the names we will deal with will reference numerical
objects. Python is unlike traditional programming languages (e.g. C, Pascal and Fortran)
in that we do not have to define names. Defining names basically means that at the start
of our program we have a list of all the names and their respective types (e.g. do we store
a real number, integer or string in the variable).
3.1
Numerical Objects
In Python (and all other programming languages Im familiar with), a object (value) is
assigned to a name by simply typing the name, followed by an equal sign, followed by the
numerical value e.g.
In [#]: a = 2.0
In [#]:
The above statement first creates the object 2.0 in memory and then binds the name a
to that object. You can enter the above line in the interactive mode or in the programming
Page 39
40
mode. You can now type a and press the Enter key and the following will appear
In [#]: a
Out [#]: 2.0
In [#]:
We do not get the error message we got when we started with Python in Section 2.3.
Remember we typed a and received an error message
-------------------------------------------------------NameError
Traceback (most recent call last)
/home/logan/<ipython-input-5-60b725f10c9c> in <module>()
----> 1 a
NameError: name a is not defined
In [#]:
Since we have now defined a we may use it in expressions just like numbers. We
can use the name a in a subsequent computation in a program. The value 2.0 will be
substituted for every name a in the expression, since the name a is bound to the object
2.0. As an example, consider the program line
In [#]: b = a + 3.0
In [#]: b
Out [#]: 5.0
In [#]:
The object 3.0 is first created in memory, then the object 2.0 is substituted into the
above expression, in the place of the name a. 3.0 is added to 2.0 and the resulting 5.0
object is created in memory. Finally the name b is bound to this resulting 5.0 object. All
of this is done in line 1 in a fraction of a second. Names are bound to objects until we
explicitly change the binding to something else e.g.
Page 40
41
In [#]: a = 4.0
In [#]:
The above statement will first create the object 4.0 in memory and then bind the
name a to this new object, thus removing the previous binding to the 2.0 object. The
name b is still bound to the object 4.0 and thus will remain unchanged. If we require an
updated evaluation of b, we must again type:
In [#]: b = a + 3.0
In [#]: b
Out [#]: 7.0
In [#]:
Take Note:
Please check the line numbers in each of the examples above to see how they follow
on from one another.
Take Note:
Only a name is allowed at the left of the equal sign, and all the quantities (numbers,
other names and objects) that appear to the right of the equal sign must be known.
Therefore, make sure that a object (value) has been bound to a name before you use
that name to the right of the equal sign to define another name.
3.1.1
Memory Model
Hopefully the previous section was not too confusing, I will try to clarify the namebinding-object behaviour of Python with a few memory model figures and explanations
in this section.
Four columns are shown in Figure 3.1, the first column is the line number for the
Python statement, the second the Python statement. This is what we see when creating
a Python program or when we use Python in an interactive mode. What happens behind
the scenes, between Python and the computers memory, is shown in the last two columns.
Page 41
42
43
The previous figure (Figure 3.2) shows the memory model for the examples shown in
the previous section. Review the previous section (Section 3.1) and see if you can link
the explanations, given in the examples, with the memory model shown above.
Take Note:
Objects in memory can have multiply names bound to them.
3.1.2
Lets consider the problem of swapping the objects bound by two names a and b. A first
attempt might be:
a
b
a
b
=
=
=
=
2.0
4.0
b
a
44
previously bound objects. The way we work around this problem is by using a new name
that remains bound to the first object. Heres the correct way to swap two objects:
a = 2.0
b = 4.0
a_copy = a
a = b
b = a_copy
a = 2.0
b = 4.0
b_copy = b
b = a
a = b_copy
This example illustrates that there is more than one correct method to achieve a
certain goal. You as the programmer will have to make sure that whatever logic you use
is consistent with the goal and will produce the correct output.
Page 44
3.1.3
45
Assignment Operators
We can also use the same name to the left and to the right of the equal sign. Remember
that when we use the equal sign that everything to the right has to be known or defined
whereas there is only one name to the left of the equal sign. What happens in the following
code?
number = 5
number = number + 3
Line 1 first creates the object 5 in memory and then binds the the name number to
the 5 object, therefore we can now use number on the right hand side of the equal sign.
On line 2 the right hand side gets computed until everything is done and then the name
(on the left hand side of the equal sign) gets bound to the resulting object (from the
computation of the right hand side of the equal sign). Therefore by the time line 2 is
reached, the name number is bound to the object 5 to which the object 3 gets added to
give the resulting object 8. The name number is then bound to this resulting 8 object.
Using names in this fashion allows you to remember what you have computed so far, and
reduces the number of names needed in your programs. Similarly, if we add line 3
number = number * 2
the name number is now bound to the object 16. This is a powerful technique which
comes in handy in many situations. There is also a shorthand method for modifying
variables in this fashion. Consider the following code:
number = 5
number += 3
number *= 2
This example does exactly the same as the above example. Line 1 binds the name
number to the object 5, line 2 adds the object 3 to the 5 object and line 3 multiplies the
object 2 with 8 object. This technique is know as Assignment Operators. The Table 3.1
shows a summary of the assignment operators (assume a holds the value of 10).
Play around with these operators and make sure you are comfortable with both methods of updating / changing the same variable.
Page 45
46
Example
a = a + 10
a = a - 10
a=a*2
a=a/2
a = a ** 2
Operator
a += 10
a -= 10
a *= 2
a /= 2
a **= 2
3.1.4
For numerical objects there are two distinct kinds of types, namely an integer and a
floating point number (real number). We can see the type of variable we are using by
typing type(variable) into the IPython Console:
In [#]: a = 10
In [#]: type(a)
Out [#]: int
In [#]: b = 2.0
In [#]: type(b)
Out [#]: float
Page 46
47
Recall, in Section 2.3, that typing 5 / 2 into the IPython Console returned 2 and not
2.5, this is because Python sees that both the 5 and the 2 are integers and neither of them
are floats and thus Python returns the result as an integer (throwing away the 0.5).
Consider the following example:
In [#]: a = 5
In [#]: a / 2
Out [#]: 2
In [#]: a / 2.0
Out [#]: 2.5
In [#]: b = 5.0
In [#]: b / 2
Out [#]: 2.5
In [#]:
In [#]: a = 5
In [#]: a /= 2
In [#]: a
Out [#]: 2
Page 47
48
3) Make sure you understand this behaviour and that you practice a few example to
see the difference between integers and floats.
3.1.4.1
Integer division can result in surprising bugs: suppose you are writing code to compute
the mean value m = (x + y)/2 of two numbers x and y. The first attempt of writing this
may read:
m = (x + y) / 2
Suppose this is tested with x = 0.5 and y = 0.5, then the example above computes
the correct answers m = 0.5 (because 0.5 + 0.5 = 1.0, i.e. a 1.0 is a floating point number,
and thus 1.0 / 2 evaluates to 0.5).
Or we could use x = 10 and y = 30, and because 10 + 30 = 40 and 40 / 2 evaluates
to 20, we get the correct answer m = 20. However, if the integers x = 0 and y = 1
would come up, then the code returns m = 0 (because 0 + 1 = 1 and 1 / 2 evaluates
to 0) whereas m = 0.5 would have been the right answer. We have many possibilities to
change the example above to work safely, including these three versions:
m = (x + y) / 2.0
m = (x + y) * 0.5
m = float(x + y) / 2
3.1.4.2
There are two ways to avoid the integer division problem and ensure that the results of
the computation is always returned as a floating point number:
1. Make use of Pythons future division: it has been decided that from Python version
3.0 onwards the division operator will return a floating point number even if both
the numerator and denominator are integers. This feature can be activated in older
Python versions (2.x) with the from future import division statement:
Page 48
49
In [#]: 15/6
Out [#]: 2.5
In [#]: 21/7
Out [#]: 3.0
In [#]:
2. Ensure that at least one number (either the numerator or the denominator) is a
floating point number (as discussed in section 3.1.4), the division operator will
return a floating point number. This can be done by writing 15. or 15.0 instead of
15, of by forcing conversion of the number to a float, i.e. use float(15) instead of
15:
In [#]: 15 / 6
Out [#]: 2
In [#]: 15. / 6
Out [#]: 2.5
In [#]: 15.0 / 6
Out [#]: 2.5
In [#]: 15 / 6.
Out [#]: 2.5
In [#]: float(15) / 6
Out [#]: 2.5
In [#]: 15 / float(6)
Out [#]: 2.5
3.2
In [#]:
String Objects
So far, I have only used names that have been bound to numerical objects. I hope you
realise that numbers arent the only type of information available, so you should recognise
Page 49
50
the need to also save other types of information. The type of non-numerical object we
will use most often is the string object. A string object is created by enclosing characters
in either single, double or triple quotes e.g.
The above statements create a string object in memory that has the content John and
then binds the name name to this string object. We can assign any content to a string
object, but remember to enclose the string in either single, double or triple quotes. Valid
examples are
If you want to include a double quote character as part of the string, add a backslash
symbol (\) before the quote character (otherwise Python will interpret the quote character
as the end of the string) i.e.
After you entered the above statements, type quote and press enter or type print
quote and press enter. The following will appear in the Command Window :
51
Do you see that the double quote characters are now interpreted as part of the string
object? String objects are often used to create appropriate feedback to the users of your
programs.
More Info:
The print command is used to print information to the IPython Console:
print 2.0 - will print the value 2.0
print "hello" - will print the string hello
print var1 - will print the value of the variable var1
print var1, var2 - will print the value of the variable var1 followed by a space
followed by the value of the variable var2
3.3
Boolean Objects
Another type of object I want you to be aware of is a boolean object, which can only have
one of two values, namely True or False. This type of object is commonly used in while
loop statements (Section 5.1) and if statements (Section 6.1), and I will show examples
of the use of this object in the relevant sections of these notes.
You create the boolean object, with the True or False values, and bind a name to it
the same way you would any other object:
In [#]: a = True
In [#]: a
Out [#]: True
In [#]: b == False
In [#]:
Take Note:
True and False must have the capital letters T and F respectively.
If you use true (lower case t) Python will give you an error message.
Page 51
3.4
52
Acceptable Names
Names have the same restrictions as program names. The case sensitive argument also
applies to names and therefore the name Value1, VALUE1 and VaLuE1 are distinct and
each can be bound to the same or to a different object.
When you bind the name of an imported function name in Python to a new object,
you can no longer access the original function any more. Consider the example where you
bind the name sin (after importing sin from the math module) to a new object:
In [#]: type(sin)
Out[#]: builtin_function_or_method
In [#]: type(sin)
Out[#]: float
In [#]:
From lines 3 and 4 you can see that the imported name sin is recognised as a function.
When you bind the name sin to the object 4.0 (line 6) the previous binding to the original
function is removed. You can also see in lines 8 and 9 that sin is now a float object.
If you tried to call sin now as if it where a function e.g. sin(1) you would get the
following error message:
In [#]: sin(1)
-------------------------------------------------TypeError
Traceback (most recent call last)
<ipython-input-75-93e746b069d9> in <module>()
----> 1 sin(1)
In [#]:
Page 52
53
Take Note:
Names should not be any of the Python keywords: type help(keywords) to see
the list:
and
as
assert
break
class
continue
def
del
elif
else
except
exec
finally
for
from
global
if
import
in
is
lambda
not
or
pass
print
raise
return
try
while
with
yield
More Info:
PEP8 standard for names:
lowercase with words separated by underscores as necessary to improve readability, e.g. my value and not My Value.
names should be at least 3 characters in length and descriptive.
The PEP8 standard is available at:
http://www.python.org/dev/peps/pep-0008/
3.5
Summary
Page 53
54
More Info:
You can also convert between object types using the following statements:
1. int() - Convert a string or number to an integer
2. float() - Convert a string or number to a floating point number (real number)
3. str() - Return a string representation of a number
More information on each of these will be given as and when they are used in the
examples in these note.
3.6
Exercises
1. Assigning an object to the name calc = 5*5*5 + 4*4. Which operators are executed first (addition and subtraction) or (multiplication and division)?
Redo the computation a = 5*5*5 + 4*4 using only ** and + operators as opposed
to * and +.
2. Compute the unknown variable a in the equation:
a b = sin(d) + c
(3.1)
cos(d)
b+c=
tan(e)
(3.2)
111.
( + 5)2 ( 5 e2 )
(1 e )(1 + e )
(3.3)
Attempt at least two methods to compute the above i.e. enter the complete computations as a single programming line, and break the computation into as many
lines as required to make the logic a bit easier.
Page 54
55
Page 55
56
Page 56
Chapter 4
Unconditional loop
Throughout this course, I will use mathematical concepts to illustrate programming principles. I do this because that is why engineers need to program: we will find some
mathematical expression or method in some textbook, and we want to use it to solve an
engineering problem.
As a first example, consider the sum of the first 100 integers:
100
X
(4.1)
n=1
which can be written as 1 + 2 + 3 + 4 + + 100. We would like to add the first hundred
integers, the question is how to go about it (if we pretend not to know about Gausss
little formula). We can add the first hundred numbers by adding each consecutive number
to the sum already calculated. Clearly this would be a time consuming approach which
becomes more time consuming when we increase the first hundred to a thousand or million.
Let us go through a calculator exercise in Python and add the first couple of numbers
to a name called mysum. Remember we first need to define the name mysum. Here is where
logic and understanding comes into play as more than one possibility exist. Ill explain
two possible approaches that comes to mind.
We can define mysum to be equal to the first term we want to add i.e. 1 and then add
the numbers 2 to 100 to it. Therefore a program that adds the first five numbers appears
as follows:
Page 57
58
OR
mysum
mysum
mysum
mysum
mysum
print
= 1
= mysum
= mysum
= mysum
= mysum
mysum
+
+
+
+
2
3
4
5
mysum
mysum
mysum
mysum
mysum
print
= 1
+= 2
+= 3
+= 4
+= 5
mysum
Alternatively, because we are doing addition we can define mysum to be equal to 0 since
0 + a = a. Therefore setting mysum equal to 0 does not affect the calculation. Adding
from 0 to 100 is the same as adding from 1 to 100. This approach results in the program:
OR
mysum
mysum
mysum
mysum
mysum
mysum
print
= 0
= mysum
= mysum
= mysum
= mysum
= mysum
mysum
+
+
+
+
+
1
2
3
4
5
mysum
mysum
mysum
mysum
mysum
mysum
print
= 0
+= 1
+= 2
+= 3
+= 4
+= 5
mysum
Both these approaches are tedious, especially when considering that we would like to
add the first 100 numbers. It is clear that in both approaches there is a pattern of what
we are doing
Firstly, we recognise that we know exactly how many numbers we wanted to add i.e.
from 1 to 100. Secondly we see a pattern when we add the numbers. We can use a for
loop statement to exploit these two aspects of the problem we wanted to solve. Before we
jump into the for loop statement I first need to cover the range function as this will be
used in the for loop statement itself.
Page 58
4.1
59
The range function is used to create a list of integers. The syntax of the range function
is:
Where start is the first integer in the list, end is the last integer in the list, and
increment is the step size. So we can use the range functions as follows:
In [#]:
Take Note:
The last integer in the list (end) is always omitted !!!
So, for example, range(5, 12, 1) will give a list of integers from 5 to 11 in incremental steps of 1.
The first integer in the list (start) is always included.
From this example we can see that the range function has created a list of integers
starting from 0 and ending at 9. The range function created this list by first starting at
0 (start = 0) and then adding the increment 1 (increment = 1) to this value to get the
next value, it keeps doing this as long as the value is less than the end value 10 (end =
10).
Both the increment and the start values can be omitted (left out). If we dont
specify a start value, the default value of 0 is used. If we dont specify an increment
value, the default value of 1 is used. Consider the following examples:
start = 0, end = 10, and increment = 3 are specified:
In [#]:
Page 59
60
In [#]:
start = 0 and end = 10 are specified. increment is equal to the default value of
1:
In [#]: range(0, 10)
Out[#]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [#]:
start = 6 and end = 10 are specified. increment is equal to the default value of
1:
In [#]:
Only end = 5 is specified. increment is equal to the default value of 1 and start is
equal to the default value of 0:
In [#]: range(5)
Out[#]: [0, 1, 2, 3, 4]
4.2
In [#]:
A for loop statement is a command used to repeat a section of code for a predetermined
number of times. The syntax of the for loop statement is:
Page 60
61
Take Note:
Note the double colon (:) at the end of the for loop statement in line 1. This tells
Python where the program statements inside the for loop start.
If the double colon (:) is left out you will see an error message.
where index is a name that gets bound to each of the integer objects in the list of
integers (created by the range command) during every repetition of the for loop until it
reaches the last integer object in this list. This happens automatically. We do not need to
define index as a variable having an initial value first and we do not have to increment
index with increment every time the loop is repeated. In fact, we must not. If we do
these things, the program will not function properly. Any number of statements can be
contained within the for loop statement. A few simple examples of for loop statements
follow.
4.2.1
Example 1
for i in range(3):
print "i = ", i
i =
i =
i =
0
1
2
int_list = range(3)
print "int list = ", int_list
for i in int_list:
print "i = ", i
Page 61
62
int_list = [0, 1, 2]
i = 0
i = 1
i = 2
4.2.2
Example 2
i =
i =
i =
0
2
4
Page 62
4.2.3
63
Example 3
i =
i =
i =
5
3
1
int_list = [5, 3, 1]
i = 5
i = 3
i = 1
4.2.4
Example 4
cnt = 0
print "cnt = ", cnt
for k in range(5):
cnt = cnt + 1
print "cnt = ", cnt
cnt
cnt
cnt
cnt
cnt
cnt
=
=
=
=
=
=
64
0
1
2
3
4
5
This last example is included to demonstrate that the index variable used in the
for loop statement (k in the case above) does not have to be used inside the for loop
statement. In such a case the for loop is simply used to repeat a certain set of commands
(or a single command as above) a known number of times. It also illustrates that only the
commands contained in the for loop statement are repeated. Since cnt = 0 is outside the
for loop statement it is only executed once at the start, when the program is executed.
4.2.5
Indentation
Indentation means the amount of white space placed in front of your code. Python, unlike
other programming languages, uses this white space to determine what statements are
contained inside the for loop statement. Other programming languages will either require
brackets (, (), []) or keywords (end) to tell that programming language which statements
are part of the for loop statement. Python uses white space, 4 spaces to be exact !!!
Consider the following example:
cnt = 0
for i in range(3):
cnt += i
print "Im inside the for loop"
print "Im outside the for loop"
When you execute this code you will see the following output:
Im
Im
Im
Im
65
From this you can see that line 4 is executed 3 times by the for loop statement and
after the for loop has finished only then is line 5 executed.
Take Note:
Python requires 4 spaces before all program statements inside a for loop statement.
The example above has 4 spaces in front of the statements on lines 3 and 4 to indicate
that these lines are inside the for loop statement.
Lets look at what happens if the code indentation is wrong. The above example has
been modified and only 3 spaces have been added in front of the statement on line 4:
a = 0
for i in range(3):
a += i
print "Im not indented properly"
print "Im outside the for loop"
If you run this example now you will see an error message similar to the following:
print "Im not indented properly"
^
IndentationError: unindent does not match any
outer indentation level
Take Note:
Be VERY careful and aware of this when typing you programs, as this can sometimes
lead to unexpected results and logic errors. There is a big difference between the
following two examples:
cnt = 0
for i in range(10):
print "cnt = ", cnt
cnt += i
print "cnt = ", cnt
cnt = 0
for i in range(10):
print "cnt = ", cnt
Page 65
66
cnt += i
print "cnt = ", cnt
Make sure you know the difference in the results of these two examples and why
they are different !!!
More Info:
Spyder will automatically add 4 spaces when you push the tab key. And Shift +
tab will automatically remove 4 spaces. This can also be used if you have highlighted
several lines of code. Try it !!
If this doesnt work properly, Click on the Tools menu in Spyder, then Click on
Preferences, then Click on Editor (on the left). Under the Advanced Settings
tab (on the right) make sure that Indentation characters is set to 4 spaces and
that Tab always indent is un-ticked.
4.3
Lets revisit the program that computes the sum of the first 100 integers. We can easily
perform this inside a for loop statement. Lets consider the first approach where we
initialised mysum to 1:
mysum = 1
for nextnumber in range(2, 101):
mysum = mysum + nextnumber
print mysum
If we consider our second approach where we initialised mysum to 0 the program would
look something like this:
mysum = 0
for nextnumber in range(1, 101):
mysum += nextnumber
print mysum
Page 66
67
Can you see how easy the for statement makes the computation. If we want to sum
the first 512 numbers we only have to change end value used in the range function in
the for loop statement.
Alternatively, we can use the for loop statement to merely execute the loop a specified
number of times without using the index name inside the for loop. Therefore, to sum
the first hundred integers together, we need to generate the correct integer to add to the
sum ourselves as shown in the example below:
mysum = 0
nextnumber = 1
for i in range(101, 201):
mysum += nextnumber
nextnumber += 1
print mysum
The index name i starts at 101. It increments by 1 every time we go through the for
loop and stops when the index name i is equal to 200. Clearly i does not contain any
of the integers between 1 and 100 and since we want to add the first hundred numbers
we need to compute the correct integer to add ourselves. This is achieved by using the
nextnumber name which is initialised to 1 in line 2. We add nextnumber to mysum
where after we add 1 to nextnumber so that nextnumber is equal to 2 the next time we
go through the loop. Once we added nextnumber the second time we go through the loop
1 will be added to nextnumber so that nextnumber is equal to 3 and so on. Every time,
before we complete the loop and after weve added nextnumber to mysum we add 1 to
nextnumber so that the next number in the summation sequence is added the following
time we go through the loop.
4.4
Consider a program that computes 10! i.e. 1 2 3 . . . 10. Again we know exactly how
many times we want to multiply numbers with each other. We also see a pattern in what
we want to compute. We can therefore use a for loop statement to compute it.
Let us call the name myfactorial. Before we can use myfactorial on the right hand
side we need to initialise it. Would it work if we initialize as myfactorial = 0? What
would the result be? As we are busy with multiplication the result will always be 0.
In this case we need to initialise myfactorial to 1. This can be viewed as a number
Page 67
68
that does not affect our calculation i.e. a 1 = a or either as the first number of the
sequence. For the former, the program is
myfactorial = 1
for nextnumber in range(1, 11):
myfactorial = myfactorial * nextnumber
print myfactorial
myfactorial = 1
for nextnumber in range(2, 11):
myfactorial *= nextnumber
print myfactorial
Take Note:
It is important to realise that both programs are correct. More importantly you need
to develop your own way of coding your programs as there is no unique solution to
a programming problem. Programs are based on the logic and understanding of the
programmer.
Think and understand before you type.
For instance we can use the for loop to merely execute the loop a specified number
without using the index name inside the for loop. Therefore, to compute the product of
the first 10 integers we need to generate the correct integer to multiply to the product
ourselves as shown in the example below:
myfactorial = 1
nextnumber = 2
for i in range(22, 31):
myfactorial *= nextnumber
nextnumber += 1
print myfactorial
The index name i starts at 22. It increments by 1 every time we go through the for
loop and stops when the index name i is equal to 30. Clearly i does not contain any of
the integers between 1 and 10. As we want to compute the product the first 10 integers
Page 68
69
we need to compute the correct integer that needs to be multiplied with myfactorial
ourselves. This is achieved by using the nextnumber name which is initialised to 2 in line
2. We then multiply nextnumber to myfactorial where after we add 1 to nextnumber
so that nextnumber is equal to 3 the next time we go through the loop. Once we have
multiplied nextnumber to myfactorial the second time we go through the loop 1 will be
added to nextnumber so that nextnumber is equal to 4 and so on. Everytime before we
complete the loop and after weve multiplied nextnumber with myfactorial we add 1 to
nextnumber so that the next number in the factorial sequence is multiplied the following
time we go through the loop.
Take Note:
The for loop statement is used for unconditional looping, i.e. we know in advance
how many times a section of code has to be repeated. At no point do we have to
check if a certain condition is True before we execute the loop another time.
More Info:
Remember that you can use the math.factorial(10) function to verify the results
of these examples in this section.
4.5
A very famous series was proposed by the mathematician Fibonacci: The first two terms
of the series are given by L0 = 0 and L1 = 1. Hereafter, each new term in the series is
given by the sum of the previous two terms in the series i.e.
Lk = Lk1 + Lk2
for k = 2, 3, 4, . . .
(4.2)
term_k_minus_2 = 0
term_k_minus_1 = 1
term_k = term_k_minus_1 + term_k_minus_2
term_k_plus_1 = term_k + term_k_minus_1
term_k_plus_2 = term_k_plus_1 + term_k
print term_k_plus_2
Page 69
70
Lets pretend we are only interested in the fifth term (k = 4). The solution above
requires five names with five values. If we require the twentieth term (k = 19), we would
have twenty names using this approach. Here we have written a program that does not
exploit the pattern we see from the mathematics i.e. the Fibonacci series only uses the
(k 1) and (k 2) terms to compute the k th term i.e. for k = 2 we require k = 1 and
k = 0 terms, for k = 3 we require k = 2 and k = 1 terms etc.
We only have to remember the previous two terms to compute the next term. Let us
do that by swapping objects. The computed k th term in the k th iteration becomes the
(k 1)th when we increment k by 1. Similarly the (k 1)th term becomes the (k 2)th
term. Let us rewrite the above code to use only three variables,
term_k_minus_2 = 0
term_k_minus_1 = 1
term_k = term_k_minus_1 + term_k_minus_2 #k = 2
term_k_minus_2 = term_k_minus_1
term_k_minus_1 = term_k
term_k = term_k_minus_1 + term_k_minus_2 #k = 3
term_k_minus_2 = term_k_minus_1
term_k_minus_1 = term_k
term_k = term_k_minus_1 + term_k_minus_2 #k = 4
print term_k
Take Note:
The sharp symbol (#) in a Python program is used to add comments to that program
giving more information regarding a certain line or several lines of code.
Anything written after the sharp symbol (#) will not be executed by Python.
for i in range(10):
# print "I am not part of the program"
# this line nor the one above will be executed
print "I am part of the program"
Make sure you understand what the output of this example will be and why !
Let us see how we would go about computing the 25th term (k = 24) of the Fibonacci
series. We need to compute an additional 23 terms since the first two terms are given.
Can you see why the for loop statement is applicable? We know how many terms we
want to compute and we have a pattern of what we would like to repeat 23 times. So, let
us compute the twenty fifth term (k = 24) using the for loop statement:
Page 70
71
term_k_minus_2 = 0
term_k_minus_1 = 1
for k in range(2, 25):
term_k = term_k_minus_1 + term_k_minus_2
term_k_minus_2 = term_k_minus_1
term_k_minus_1 = term_k
print "term_k = ", term_k
It is important to note that when we swap the objects we do it in such a way that
we dont end up with all names bound to the same object. We therefore start with the
term k minus 2 name in line 5, since its current content was used to compute the current
k th Fibonacci term but it is not used to compute the next Fibonacci term. We therefore
bind the name term k minus 2 to the same object bound by term k minus 1. Thereafter
we bind the name term k minus 1 to the same object bound by term k, that we just
computed. Now we are ready to increment k to compute the next term of the Fibonacci
series. When k is incremented, and the loop repeats, the name term k will get bound to
a new object resulting from term k minus 1 + term k minus 2 .
The program is ended with a single statement after the for loop, with the only purpose
to provide some output: we want to print the value contained in the name term k to the
IPython Console. We do so by using the print statement. The following output is
produced:
term_k =
46368
As stated before, we could write many programs that will do the same thing depending
on the logic the programmer follows. The important aspect is to understand what you
want to do, work out the logic of how you would do it. You can then program what you
require to end up with a program that will solve your problem.
Here is another example of a program that computes the 24th term of the Fibonacci
series:
term_k_minus_2 = 0
term_k_minus_1 = 1
term_k = term_k_minus_1 + term_k_minus_2
for k in range(3, 25):
term_k_minus_2 = term_k_minus_1
term_k_minus_1 = term_k
Page 71
72
See if you can work out the logic behind this program and why it works.
4.6
X
1 1 1 1
(1)k
= 4 1 + + ...
(4.3)
=4
2k + 1
3 5 7 9
k=0
Suppose that we want to approximate the value of by using the above Taylor series.
If we want to use a for loop statement to perform this computation, we must decide
how many terms we want to add up. Suppose we choose 100 terms. A program that
accomplishes this is:
taylor_pi = 0.0
for k in range(100):
taylor_pi += (4.0 * (-1)**k) / (2*k + 1)
print "pi = ", taylor_pi
pi =
3.13159290356
Page 72
73
Compared to the true value of = 3.14159 . . . , the 1st 100 terms of the Taylor series
approximation is only accurate to 2 digits. Note that since we start counting at k = 0, we
only need to repeat the loop up to k = 99 to add up the first 100 terms. Also note that we
must initialise the name taylor pi to zero outside the for loop statement. If we do not do
this, Python will produce an error message upon executing line 3 for the first time: were
instructing Python to add the value 4*(-1)^
k/(2*k+1) to the name taylor pi (whose
value is unknown). If we did not assign zero to the name taylor pi outside the for loop
statement, we cannot expect Python to perform the required computation. The program
is ended with a single statement after the for loop statement, with the only purpose to
provide some output: we want to print the function value approximation contained in the
name taylor pi to the IPython Console.
You can also perform the task by first computing the next Taylor term in the series
and then adding it to the sum of terms in a next line:
taylor_pi = 0.0
for k in range(100):
taylor_term = (4.0 * (-1)**k) / (2*k + 1)
taylor_pi += taylor_term
print "pi = ", taylor_pi
If we want to compute more accurately, we have to sum more terms in the Taylor
series. If we compute the sum of the first 1000 terms (by simply changing line 2 to: for
k in range(1000)), the output is
pi =
3.14059265384
Even the first 1000 terms in the Taylor series produces an approximate value for
that is only accurate to 3 digits. Before you decide that Taylor series approximations are
of no use, consider the approximation to the exponential function:
X xk
1
1
e x = 1 + x + x2 + x3 + =
2
6
k!
k=0
(4.4)
Lets use this Taylor series approximation to compute the value of e (i.e. compute ex
for x = 1). The following program will add the first 20 terms:
Page 73
74
taylor_exp = 0.0
x_val = 1.0
for k in range(20):
taylor_exp += x_val**k / factorial(k)
print "exp = ", taylor_exp
You should already be quite familiar with the math.factorial(k) function to compute k!. The example above produces the following output:
exp =
2.71828182846
which is accurate to all 15 digits. Here we have an example where a few terms in this
Taylor series produces a very accurate approximation, whereas the 1000-term approximation to was still inaccurate.
Let us pretend that the math.factorial function does not exist. How could we go
about computing the factorial of a number k. We could use the for loop statement as we
have done in Section 4.4 to compute the factorial of k, i.e. k! = 1 2 3 (k 2)
(k 1) k. Before we add a term to taylor exp we must make sure we have computed
the factorial correctly. The following program computes the first 20 terms of the Taylor
series of ex by using the for loop statement to compute the factorial:
taylor_exp = 0.0
x_val = 1.0
for k in range(20):
factorial = 1
for i in range(2, k+1):
factorial *= i
In the program above we compute the factorial of k for every iteration from scratch
i.e. during every iteration we initialize factorial to 1 (l ine 5) and then compute the
appropriate factorial before we compute the next term we want to add to taylor exp.
Page 74
75
Take Note:
Notice the indentation for this example.
Lines 4 to 9 require 4 space in front of the program statements to tell Python that
they are inside the first (outer) for loop.
Lines 7 and 8 require 4 + 4 spaces in front of the program statements to tell Python
that they are inside the second (inner) for loop.
Make sure you understand the behaviour and output of the following two examples
and why they are different !!
a = 0.0
for i in range(10):
a += 10 * 1
for j in range(5):
a /= 2
print "a = ", a
a = 0.0
for i in
a +=
for j in
a /=
print "a
range(10):
10 * 1
range(5):
2
= ", a
taylor_exp = 1.0
#changed from 0.0 to 1.0
x_val = 1.0
factorial = 1
for k in range(1, 19):
factorial *= k
taylor_exp += x_val**k / factorial
print "exp = ", taylor_exp
We initialise the name factorial outside the for loop statement to 1. Then for the
Page 75
76
current iteration we compute the appropriate factorial for this iteration by multiplying
the previously calculated factorial of the previous iteration with k. Also note that instead
of initialising taylor exp to 0 we now initialise taylor exp to 1. Also in the for loop
statement we start k at 1 instead of 0. By initialising taylor exp to 1 we have already
included the term when k is equal to 0 into our summation. We can therefore start k at
1 instead of 0 to avoid multiplying factorial with 0 i.e. when k = 0:
factorial *= k
then factorial is equal to zero and remains so thereafter irrespective of the value of k.
Take Note:
It is important to note that when you change a part of your code, you have to revisit
the rest of the code to ensure that everything is still working as expected.
4.7
Exercises
1. Write a program where you multiply two positive integers by using the addition +
operator. You are only allowed to use the multiplication * operator to verify your
answer. First assign the two numbers to two names: number1 and number2. Recall
that you can write
74=7+7+7+7=4+4+4+4+4+4+4
(4.5)
2. Simulates the throwing of a dice 5 times using a for loop statement and print the
result of each throw to the screen.
[Hint: Use the random module to generate a random integer between 1 to 6.]
3. I heard a story that the famous mathematician Gauss was rather a handful in
class. He always completed his assignment much faster than the other kids and
then created havoc due to boredom. In an attempt to keep Gauss busy for a while,
his teacher told Gauss to add the first 1000 integers, as punishment. A minute
later Gauss completed his task, because he reasoned as follows: If I have to add
0, 1, 2, . . . , N 2, N 1, N , then I can create the number pairs 1 + N , 2 + (N 1),
3 + (N 2) and so on. The sum of each of these pairs is N + 1, and there is N2 of
these pairs. So the sum is simply given by
N
X
n=0
n=
N
(N + 1)
2
Page 76
(4.6)
77
You can test the above formula for small values of N . Then write a Python program
that checks Gausss formula for N = 1000. So I want you to add the first 1000
integers (using a for loop statement), and then compare it to the formula above.
4. We can break up the addition of the first 1000 integers by adding the odd numbers
and then by adding the even numbers. You can check your calculation by adding
the odd and even numbers which must then equal the answer given in the previous
question.
Add all the odd numbers between 0 and 1000 i.e.
500
X
n=1
2n 1
(4.7)
Now add all the even numbers between 0 and 1000 i.e.
500
X
2n
(4.8)
n=1
(1)n+1
1 1 1
1
1
= 1 + +
n
2 3 4
100
(4.9)
6. Find the pattern of the following series and then compute the 30th term of the
series:
1 1 1 1
1
1
+ +
+
...
1 2 4 8 16 32
Page 77
78
7. The Basel problem was first posed by Pietro Mengoli in 1644 and was solved by
Leonhard Euler in 1735 which brought Leonhard Euler immediate fame at the age
of 28. Euler showed that the sequence
1
1
1
1 1 1
+ + +
+
+
+ ...
1 4 9 16 25 36
converges to 2 /6. Find the pattern of this series and then compute the first 100
and then first 1000 terms of this series and compare the accuracy to 2 /6.
8. A very famous series was proposed by the mathematician Fibonacci: The first two
terms of the series are given by L0 = 0 and L1 = 1. Hereafter, each new term in
the series is given by the sum of the previous two terms in the series i.e.
Lk = Lk2 + Lk1
for k = 2, 3, . . .
(4.10)
x3 x5 x7
+
...
3!
5!
7!
x19
19!
(4.11)
10. The wind chill factor (WCF) indicates the perceived air temperature to exposed
skin and is given by:
W CF = 13.12 + 0.6215Ta 11.37v 0.16 + 0.3965Ta v 0.16
with Ta the air temperature in degrees Celcius and v the air speed in km/h (measured at 10 metres standard anemometer height).
Write a program that displays a collection of WCFs using nested for loop statements. The temperature (Ta ) must range from -20 to 55 degrees Celcius in steps of
5 and wind speed (v) must rang from 0 to 100 km/h in increments of 10.
Page 78
Chapter 5
Conditional loop
In the previous section we had an example where a few terms in the Taylor series for ex
produced a very accurate approximation, whereas approximation to of the 1000th term
was still inaccurate. So how can we tell in advance how many terms will be required
to get an accurate function value? We cant. The only benefit of using the for loop
statement to compute the approximate value to a function is its simplicity. In general
our goal would be to compute an accurate function value rather than just add some
predetermined number of terms. So how should we approach the problem of computing
an accurate function value using a Taylor series approximation ?
I can think of a couple of ideas.
1. We can keep on adding terms to the approximation and keep track of the function
value. If the function value changes become insignificant, stop adding terms.
2. We can keep track of the size of each additional Taylor term. If the size of the new
Taylor term is insignificantly small, stop adding terms.
Clearly the for loop statement cannot be used for the above ideas. We need a type
of loop that will only be repeated if some condition is true. The while loop statement
performs this function.
5.1
The while loop statement is used for conditional loops, i.e. a section of code is repeated
only while some specified condition remains true. The syntax of the while loop statement
is:
Page 79
80
while condition:
program statements to be repeated
Ill repeat again, just to emphasise the syntax: the section of code contained in while
loop statement is repeated only while the condition remains True.
Take Note:
Note the double colon (:) at the end of the while loop statement in line 1. This is
the same as the for loop statement and tells Python where the program statements
inside the while loop start.
If the double colon (:) is left out you will see an error message.
Take Note:
The rules for indentation for the while loop are the same as for the for loop, see
Section 4.2.5.
Python requires 4 spaces before all program statements inside the while loop.
5.2
Conditions
Let us look at what these conditions more closely. Conditions can be viewed as booleans
(or questions) that we are allowed to make (or ask), which Python will tell us if they are
True (yes) or False (no). Python can only tell us True or False.
Take Note:
It is important to note that every name in a condition has to be defined.
Ill explain both viewpoints as some students may find considering conditions as
booleans easier to understand, rather than to consider the conditions as questions or
vice versa. It is important to go through both and then to choose the one that is aligned
with your way of thinking i.e. the one you best understand and the one you are the most
comfortable with.
Page 80
5.2.1
81
Conditions as booleans
When we view conditions as booleans in Python then every time we make a comparison,
Python will tell us whether the comparison is True or False. We can only make certain
types of comparisons. The comparisons we are allowed to make are listed:
1. The two numbers a and b are equal: (a == b)
2. a is not equal to b: (a != b)
3. a is smaller than b: (a < b) or (b > a)
4. a is greater than b: (a > b) or (b < a)
5. a is smaller or equal to b: (a <= b) or (b >= a)
6. a is greater or equal to b: (a >= b) or (b <= a)
Take Note:
When we make the comparison that two numbers are equal we use a double equal
sign (==). If we use a single equal sign (=) we will bind a to the object bound by b
instead.
Python will tell us whether our comparison(s) is True or False without hesitation or
error. Comparison can also be combined by using the and as well as the or statements.
The syntax for the and statement is as follows:
(comparison1) or (comparison2)
Here, and and or creates a statement about the answers of comparison1 and
comparison2 which Python then answers. Python will first evaluate comparison1 and
comparison2 before it evaluates the and or or statements. The evaluations of the and
and or statement are summarized in Table 5.1 and Table 5.2 respectively.
Page 81
comparison1
True
False
True
False
82
comparison2
True
True
False
False
Take Note:
Evaluation of the and statement:
Both comparison1 and comparison2 have to be True for the evaluation of the and
statement to be True.
comparison1
True
False
True
False
comparison2
True
True
False
False
comparison1 or comparison2
True
True
True
False
Clearly Pythons response to the and and or statements is either True or False.
5.2.2
Conditions as questions
Let us consider conditions as questions. We ask a question and Python will tell us True
(yes) or False (no). Keep in mind that Python only says True or False. But to consider
conditions as questions we need to think of True as yes and of False as no. Again, we
can only ask certain types of questions. The questions we are allowed to ask are listed:
1. Are the two numbers a and b equal?: (a == b)
2. Is a not equal to b?: (a != b)
Page 82
83
(question1) or (question2)
Here, and and or asks a questions about question1 and question2 for which Python
will then answer yes (True) or no (False). Before the and or or question is asked Python
answers question1 and question2. Only then does Python ask the and or or questions.
The evaluations of the and and or statement are summarized in Table 5.3 and Table 5.4
respectively.
question1
yes (True)
no (False)
yes (True)
no (False)
question2
yes (True)
yes (True)
no (False)
no (False)
Page 83
84
question2
yes(True)
yes(True)
no(False)
no(False)
question1 or question2
yes(True)
yes(True)
yes(True)
no(False)
5.2.3
Examples of conditions
Similarly, suppose we have to repeat a certain computation only if both a and b are
less than some specified name tolerance. In this case, the condition is programmed as
We may assemble any type of condition from any of the operators above (==, !=, <,
>, <=, >=, and, or). We can also chain comparisons together, for example:
Page 84
85
In this example Python works its way from left to right by first evaluating 10 <= a
and, as long as the answer is True, then moves to the next chain (a < b).
More Info:
Type help(==) in the IPython Console to see documentation on comparisons.
5.3
Now that we know how to write conditional statements, we can mimic the for loop
statement using the while loop statement. Let us do the addition:
100
X
n=1
n = 1 + 2 + 3 + + 100,
(5.1)
using the while loop statement. Before we proceed we need to recognise that when
we performed addition using the for loop statement, we made use of a counter (or index).
However, the while loop statement does not have a counter. We therefore need to program
our own counter. The following program mimics the addition of the for loop statement
using the while loop statement:
cnt = 1
mysum = 0
while (cnt <= 100):
mysum += cnt
cnt += 1
print "mysum = ", mysum
print "cnt = ", cnt
86
statement is True by the time we reach it in our program. We achieved that when we
initialised n = 1, since we want to continue adding while cnt <= 100. We added the last
line in the code so that you can see that the programs stops when cnt > 100 since our
condition then becomes False.
Take Note:
It you program does not want to break out of a while loop statement (i.e. infinite
loop because the while loop condition always remains True), you can press the Ctrl
button and while holding it in press the c button, in the IPyton Console, to stop the
program.
5.4
Now that we know how to write conditional statements, we can re-program the Taylor
series used to compute using a while loop statement as follows:
taylor_pi = 0.0
accuracy = 0.001
taylor_term = 1
#initialize greater than accuracy
cnt = 0
#term counter
while abs(taylor_term) > accuracy:
taylor_term = (4.0 * (-1)**cnt) / (2*cnt + 1)
taylor_pi += taylor_term
cnt += 1
print "pi = ", taylor_pi
print "cnt = ", cnt
pi = 3.14209240368
cnt = 2001
which means that we need to sum the first 2001 terms in the Taylor series to compute
the value of within an accuracy of 0.001.
The most important feature of the above program is the use of the while loop statement. Even though you as the programmer might quite easily recognise that you need to
Page 86
87
use a while loop statement, it is more important (and often more challenging) to find the
correct condition which must be used to decide whether or not to execute the loop again.
In this particular case we will repeat the while loop, which adds additional terms in the
Taylor series, as long as the magnitude of these terms are greater than some specified
small number (Ive used a name name accuracy in this program).
Take Note:
Also note that I check whether or not the absolute value of the next term is larger than
the value assigned to the name accuracy. A large negative term will not satisfy the
while loop condition and the loop will terminate prematurely, although the addition
of such a large negative term still has a significant impact on the accuracy of the
function value approximation.
In the above program, we initialise the name taylor pi to zero as before. We also
initialise the name cnt to zero and we assign a value of 0.001 to the name accuracy. Next,
we define a value of 1 to the taylor term name. This might not make any sense, but
it is required to ensure that the while loop condition is True for the very first iteration.
As long as we start off with a value on taylor term that is greater than accuracy, the
while loop condition is True and the while loop will be executed at least once.
Two separate outputs will be printed to the IPython Console after the program is executed: the function value approximation contained in the name taylor pi and the total
number of terms required in the Taylor series, which for this particular implementation
is equal to cnt.
Just to illustrate that there exist many solutions to the same programming problem,
consider the following alternative:
accuracy = 0.001
cnt = 0
#term counter
taylor_term = (4.0 * (-1)**cnt) / (2*cnt + 1)
taylor_pi = taylor_term
while abs(taylor_term) > accuracy:
cnt += 1
taylor_term = (4.0 * (-1)**cnt) / (2*cnt + 1)
taylor_pi += taylor_term
print "pi = ", taylor_pi
print "cnt = ", cnt+1
In this implementation, the first Taylor term is computed outside the while loop
statement. The subsequent terms are computed inside the while loop statement. The
Page 87
88
benefit of this solution is that we do not need to assign some artificial initial value to
taylor term just to make sure the while loop condition is True for the first iteration.
Also note that the number of terms added is now given by cnt+1 instead of cnt. This is
because we now increment the value of cnt at the start of the loop instead of the end of
the loop.
We can also use a different condition for the while loop statement. Ive mentioned
before that we can stop the loop if the function value approximation converges (instead of
checking if the next Taylor term is small enough). So how do we check for convergence?
Using a computer, all we can do is to check if the function value changes from one
iteration to the next is within some specified tolerance (or accuracy, as before). The
following program illustrates:
tolerance = 0.001
cnt = 0
#term counter
taylor_change = 1
#initialize greater than tolerance
taylor_pi = 0.0
while abs(taylor_change) > tolerance:
taylor_old = taylor_pi
taylor_term = (4.0 * (-1)**cnt) / (2*cnt + 1)
taylor_pi += taylor_term
taylor_change = taylor_old - taylor_pi
cnt += 1
print "pi = ", taylor_pi
print "cnt = ", cnt
In this program we check if the change in the Taylor series approximation, stored in
taylor change, is large enough. If so, we keep on adding terms to the series. Once the
change in the function value approximation becomes smaller than the tolerance, the while
condition is no longer True and the program stops adding terms. If you carefully analyse
the logic above, you will realise it is equivalent to the previous program: the change in
the Taylor series is exactly equal to the magnitude of the new term being added. So it
is equivalent to check the magnitude of the new term, or to check the magnitude of the
function value change.
More Info:
This is a very typical usage of a while loop statement. Frequently we are trying
to solve a problem, but we do not know in advance what the solution is. So we
generate a sequence of trial solutions to the problem. Usually, trial solutions improve
i.e. every new computed trial solution is superior to the previous trial solutions. If
the trial solutions produce a series of approximations that tend towards a specific
Page 88
89
fixed value, we say that the method (or solution) converges. We test for convergence
by simply checking if the difference between consecutive solutions are smaller than
some small specified value. I refer to this small value as the accuracy, or tolerance of
the computation. This process of repeatedly computing a trial solution to a problem
until some condition is met, is called iteration.
5.5
Exercises
1. Write a program that first generates a random integer N . The program must then
add the numbers 1, 2, 3, . . . together as long as the sum is less than the random
integer N . The program must then display the sum as well as the last added value
in an appropriate message to the screen.
2. Write a program that generates random numbers between 1 and 10. The program
must add these random numbers together until either 21 is exceeded or five numbers
where added. The program must then display the sum to the screen.
3. Write a program that simulates a single dice being thrown. The program must
continue to simulate the throw of a dice until an even number is thrown. The
program must then display the number of throws required to throw an even number.
4. The geometric series for 2 is given by
2n = 2
(5.2)
n=0
The geometric series only equals 2 when an infinite number of terms have been
summed together. Every term that you add to the geometric series gets the approximation closer to 2.
Write a program that approximates 2 using the given geometric series. Select a required accuracy to which 2 must be approximated by the geometric series. Compute
the absolute error to determine whether the required accuracy of the approximation is obtained i.e. take the absolute value of the difference between the series
approximation and 2 (|approximation 2|).
The program must stop when the absolute error is smaller than the required accuracy. If the absolute error is still bigger than required accuracy the next term in the
geometric series must be added. The program must continue to add terms until the
desired accuracy is obtained.
5. In the exercises for Chapter 4 you had to write a program that computed the first
20 terms of the Fibonacci series. Recall that the Fibonacci series is given by the
formula
Lk = Lk2 + Lk1
for k = 2, 3, . . .
(5.3)
Page 89
90
with L0 = 0 and L1 = 1. For this exercise you have to compute the limit of the
ratio of two consecutive terms in the series. I.e. compute:
Lk
k Lk1
p = lim
(5.4)
Of course we cannot compute p using the equation above, because we cannot generate an infinite number of terms. However, we can generate the series:
L2 L3 L4
, , ,...
L1 L2 L3
(5.5)
As we generate a new term in the Fibonacci sequence. The series above is supposed
to converge to the value of p. Write a program to compute p. Also display the
number of terms required in the series as part of your output.
Assume that you do not know the value to which the sequence converges, therefore
make use of a numerical error as discussed in class i.e. use the absolute difference
of two consecutive terms to compute an approximation to the error.
6. Redo Question 4 but instead of the absolute error use a numerical error i.e. compute
the absolute difference of two consecutive terms of the geometric series i.e. n and
n 1. Look at the hint below:
|2(n) 2(n1) |
(5.6)
[Hint: The program must stop when the numerical error is smaller than the required
accuracy. If the numerical error is still bigger than required accuracy the next term
in the geometric series must be added. The program must continue to add terms
until the desired accuracy is obtained.]
7. To what value does
1
1
1 1 1 1
+ +
+
...
1 2 4 8 16 32
converge? Write a program that computes (approximates) the sequence within a
specified tolerance (i.e. when the absolute difference of two consecutively computed
sequence approximations are less than the specified tolerance). The program must
then display the converged value to the screen as well as the number of terms
required.
Write a program that computes (approximates) the limit value within a user specified tolerance (i.e. when the absolute difference of two consecutively computed
sequence approximations are less than the specified tolerance). The program must
then display the limit value to the screen.
Page 90
91
9. In the exercises for Chapter 4 you had to compute the first 10 terms of the Taylor
series expansion for sin(x).
For this exercise I would like you now to write a program that approximates the
cos function with the Taylor series expansion (given below) to within a required
accuracy. Select a required accuracy as well as the x value at which you would like
to approximate cos(x). The program then has to keep on adding terms to the Taylor
series approximation is within the required accuracy from the math modules sin(x)
function.
X
(1)n 2n
x
cos(x) =
(2n)!
n=0
Page 91
92
Page 92
Chapter 6
Branching
So far, our programs have simply been executed one line at a time, from top to bottom.
All commands are executed. In cases where we used loop statements (for or while), all
the statements within the looping commands are repeated from top to bottom a certain
number of times. These type of programs are not as flexible as you might think. Certain
scenarios might exist where different strategies are necessary to perform different tasks. In
such cases, we require a programming structure that allows us to perform different tasks
depending on the situation. In this section, Ill illustrate the use of branching statements
i.e. statements that allow the program to decide between different courses of action. No
longer will all programming statements be executed, but only those that are required.
The if statement is based on conditions (the same boolean conditions or questions
we considered for the while loop statement), see Section 5.2. Again the conditions can
be either True or False.
6.1
The if statement
if condition_a:
statements_a
Chapter 6. Branching
94
executes statements a or ignores code after which the program continues with the code
after the if statement (from line 3 onwards).
In some cases it is desired to choose between multiple pieces of code and execute
only one or none of them. This is achieved by the more general if-elif structure given
below:
if condition_a:
statements_a
elif condition_b:
statements_b
The if-elif structure starts with condition a. If condition a is True then statements a
are executed and then the program continues with the code below the if statement
(from line 5 onwards), therefore ignoring condition b and statements b whether it
is True or False. Only when condition a is False does the if-elif structure proceed to condition b. If condition b is True then statements b is executed otherwise
statements b is ignored and the program continues with the code below the if statement
(from line 5 onwards). The if-elif structure can have as many elif (short for else if)
parts as you like e.g.
if condition_a:
statements_a
elif condition_b:
statements_b
elif condition_c:
statements_c
elif condition_d:
statements_d
The same rules however apply i.e. the program only proceeds to the next condition
if all the proceeding conditions were False. The only code that is executed is the code
between first condition from the top that is True and the next condition. The program
then proceeds with the code below the if statement (from line 9 onwards). Therefore
at most one of statements a, b, c or d is executed but nothing is executed when all the
conditions are False.
The if-elif structure is therefore not appropriate when you always want to execute
statements a, statements b, statements c and statements d when their respective
conditions are True. For that you have to use the if structure for each condition e.g.
Page 94
Chapter 6. Branching
95
if condition_a:
statements_a
if condition_b:
statements_b
if condition_c:
statements_c
if condition_d:
statements_d
Here the statements of every True condition are executed. Considering the if structures so far either some code is executed or no code is executed depending on whether
the conditions are True and also on which conditions are True.
In certain cases you might always want some code to be executed. In particular, you
might want to execute some code only when all the preceding conditions are False.
The if-elif-else structure allows for that and is given below:
if condition_a:
statements_a
elif condition_b:
statements_b
else:
statements_c
The if and elif structures works exactly as before. Therefore if condition a is True
then only statements a is executed. Only when condition a is False, condition b
is considered. If condition b is True then only statements b is executed. Only when
condition b is also False then the code after the else is executed. The program then
continues with the code below the if-elif-else statement (from line 7 onwards).
Take Note:
Note that the else in this structure does not have a condition since the code after
the else is only executed when all the preceding conditions are False.
Take Note:
Note the double colon (:) at the end of the if, elif and else statements (in line
1, line 3 and line 5 respectively). This is the same as the for loop statement and
tells Python where the respective program statements inside the if, elif and else
Page 95
Chapter 6. Branching
96
statement start.
If the double colon (:) is left out you will see an error message.
Take Note:
The rules for indentation for the if-elif-else statements are the same as for the
for loop, see Section 4.2.5.
Python requires 4 spaces before all program statements inside the if-elif-else
statements.
Take Note:
A else statement is always at the end, i.e. it cannot come before either the if or
the elif statements.
Like wise a elif statement cannot come before the if statements.
More Info:
Additional help is available by typing help(if) which will produce the following
output:
The if statement
********************
Page 96
Chapter 6. Branching
6.2
97
Simple example
Lets consider the flowchart problem given in Chapter 1 (Section 1.3.1), where we have to
compute a symbol based on a given percentage:
percentage = 40
if percentage >= 80:
symbol = "A"
elif percentage >= 70:
symbol = "B"
elif percentage >= 60:
symbol = "C"
elif percentage >= 50:
symbol = "D"
else:
symbol = "F"
print "symbol = ", symbol
You can see from this example, as Python evaluates the first if statement (line 2) and
then the elif statements (lines 4, 6 and 8) the conditions evaluated are False. Python
then finally gets to the else statement and the code on line 11 is executed. Python then
exits the if-elif-else block and executes the code on line 12.
If we changed the percentage value to 70, Python would evaluate the first if statement
(line 2) and the condition evaluated would be False. Then Python would evaluate the
first elif statement (line 4) and the condition evaluated would be True and Python would
then executes the code on line 5. Python would then exit the if-elif-else block and
execute the code on line 12.
Would this example work correctly if we changed the order, in which we evaluate the
percentage symbol, from small to large (as shown in the example below) ??
percentage = 70
if percentage < 50:
symbol = "F"
elif percentage >= 50:
symbol = "D"
elif percentage >= 60:
symbol = "C"
elif percentage >= 70:
Page 97
Chapter 6. Branching
98
symbol = "B"
else:
symbol = "A"
print "symbol = ", symbol
Take Note:
The answer is in fact No, this example would not work correctly and if you answered
differently, please go back and review Section 6.1 again. Python executes the code
in the first if or elif statement where the condition evaluated was True and skips
the others.
6.3
Ill use the leg before wicket (LBW) rule of the game cricket to illustrate the use of the
if statement again. The LBW rule is as follows:
1. A batsmen can only be given out (LBW) when the batsmens bat did not make
contact with the ball and the ball hit the batsmens leg / pad. The batsmen is then
out if:
(a) The ball would have continued on to hit the stumps.
(b) The ball did not pitch on the leg side.
(c) If the batsmen offered a shot, the ball must hit the pad in line with the stumps.
(d) If the batsmen did not offer a shot, the ball may hit the pad outside the line
of offstump.
We now proceed to build a flow chart from the above problem statement. The flow
chart for the leg before wicket problem is depicted in Figure 6.1. You can see how the flow
chart splits between various sections of the logic depending on whether the conditions are
True (yes) or False (no).
We are now ready to implement the logic into a program. Here follows the program
that uses the appropriate set of questions to determine whether or not a batsmen should
be given out LBW:
Chapter 6. Branching
99
Begin
No
End
Yes
No
No
Did the ball pitch on the legside?
Yes
End
End
Yes
No
End
Yes
Batsmen is OUT!
End
if Q2 == "n":
# leg side?
Q3 = raw_input("Batsmen played a shot? [y or n]: ")
if Q3 == "n":
# play shot?
print "Batsmen is OUT! No shot played."
else:
Q4 = raw_input("Pitched wicket to wicket? [y or n]: ")
if Q4 == "y":
# in line?
print "Batsmen is OUT!"
else:
Page 99
Chapter 6. Branching
100
else:
print "Batsmen NOT OUT! Ball piched leg-side."
else:
print "Batsmen NOT OUT! Ball missed stumps."
More Info:
The raw input statement is used to get information from the user. The syntax for
the raw input statement is:
The message between the quotation marks will be printed to the IPython Console,
and Python will wait for the user to input something.
The users input will then be assigned to name var1 and Python will continue.
The value assigned to var1 will always be a string !!!
This is discussed in more detail in Section 7.1.2.
Take Note:
You should by now be familiar with why this example is indented the way it is:
each nested (inner) if-else statement has 4 more spaces than the (outer) if-else
statement before it.
Make sure you understand the behaviour and output of the following two examples
and why they are different !!
a = 9
b = 60
if a == 10:
b = 100
if b > 50:
b *= 2
else:
a = 100
print "a = ", a
print "b = ", b
Page 100
Chapter 6. Branching
6.4
101
a = 9
b = 60
if a == 10:
b = 100
if b > 50:
b *= 2
else:
a = 100
print "a = ", a
print "b = ", b
In this section you wont learn anything new. Ill just use what you know already and write
a simple number guessing program. The computer will guess an integer between 0 and
100. The user (player) then gets a chance to guess the number. The program must give
feedback that the number is either too small, too large or correct. The game continues
until the correct number is guessed. Once the user has guessed the right number, the
program must tell the user how many guesses he or she took to guess the correct number.
By now, you should be able to tell that a while loop statement is required. The while
loop statement will terminate as soon as the correct number is guessed. Inside the while
loop, an if statement will check whether the number is too small, too large or correct.
How can the computer guess a number between 0 and 100? Well make use of the
random.randint(start, end) statement, which generates random integer numbers between start and end (inclusive of start and end).
Heres my version of this program:
import random
Chapter 6. Branching
102
More Info:
Type import random and then help(random) in the Python Console to get more
information on the available functions in the random module.
More Info:
Remember that the raw input statement returns the users input as a string, so we
use the int() statement to convert the string input into an integer.
The int() function converts either a float or a string representation of an integer
to an integer:
In [#]: int(2.5)
Out[#]: 2
In [#]: int("5")
Out[#]: 5
In [#]:
Page 102
Chapter 6. Branching
103
More Info:
The str() function converts either a float or an integer to a string:
In [#]: str(10)
Out[#]: 10
In [#]: str(12.5)
Out[#]: 12.5
In [#]:
In [#]:
Chapter 6. Branching
104
What can we do to make the game a little more challenging? Lets extend the game to
have three levels of play: easy for a number between 0 and 10, intermediate for a number
between 0 and 100, and difficult for a number between 0 and 1000. All that we have to do
to implement this change is to ask the user which level he or she wants to play and then
create a random number between the appropriate bounds. Heres one possible version of
the improved game:
import random
if level == 1:
# Computer guesses number between 0
print "Ive guessed a number between
number = random.randint(0, 10)
elif level == 2:
# Computer guesses number between 0
print "Ive guessed a number between
number = random.randint(0, 100)
elif level == 3:
# Computer guesses number between 0
print "Ive guessed a number between
number = random.randint(0, 1000)
and 10
0 and 10."
and 100
0 and 100."
and 1000
0 and 1000."
Chapter 6. Branching
105
count = 1
Choose a difficulty:
1: easy [number between 0 and 10]
2: medium [number between 0 and 100]
3: hard [number between 0 and 1000]
Difficulty Level: 3
Ive guessed a number between 0 and 1000.
Enter a guess: 500
You guessed too small.
Try again: 750
You guessed too small.
Try again: 875
You guessed too large.
Try again. 800
You guessed too large.
Try again. 775
You guessed too small.
Try again: 790
You guessed too small.
Try again: 795
You guessed too large.
Try again. 794
Congratulations, you guessed right!
You took 8 guesses.
In program lines 1220, I make use of an if statement. The if statement checks the
Page 105
Chapter 6. Branching
106
value of the level name and depending on the value, the bounds of the random number
is varied. In this program, the name level can have only three possible values, i.e. 1, 2
or 3.
6.5
We have already used the if statement inside another if statement. The rules as explained holds for if-elif-else structure. As you have seen it is important to see each
structure as a unit and that the rules apply for each unit.
Remember we can combine two conditions using the and or the or keywords (Section
5.2). Similarly for the if statements we can combine two conditions e.g:
if (condition1):
if (condition2):
statements
Both programs do exactly the same thing, it simply depends on the programmer
how to program and implement it. Again, this illustrates that programs are not unique.
Programs may look different but when you follow the logic you might find that they solve
the same problem.
We can also write the combination using the or keyword differently. The combined
conditions for the or keyword is given by
if (condition1) or (condition2):
statements
Chapter 6. Branching
107
if (condition1):
statements
elif (condition2):
statements
Some implementations have their advantages and some their disadvantages e.g. imagine statements being a long list of instructions and you need to make changes to statements,
which implementation would you prefer? Our last implementation duplicates the instructions of statements and you therefore have to make double the changes. As you grow
in your programming ability and start maintaining your own code, consideration of such
issues will become more important. Here I would just like to bring to your attention
alternative issues you might want to consider when you want to decide on which implementation you want to code to solve a problem.
6.6
Exercises
1. Write a program that asks the user to type a number. The program must then
display one of the following:
Positive number, if number is larger than zero,
Chapter 6. Branching
108
(c) There are 3600 seconds in a hour, for a value of greater or equal to 3600 the
program should display the number of hours in an appropriate message.
(d) There are 86 400 seconds in a day, for a value of greater or equal to 86400 the
program should display the number of days in an appropriate message.
(e) There are 604 800 seconds in a week, for a value of greater or equal to 604 800
the program should display the number of weeks in an appropriate message.
4. Write a program that asks the user to input the mass (kg) of his/her car and then
the speed (km/h) at which he/she drives. The program must then compute the
kinetic energy of the car at that speed:
1
Ekinetic = mv 2
2
(6.1)
The program must then display the height at which the potential energy is equal to
the kinetic energy as well as the kinetic energy of the vehicle.
Epotential = mgh
(6.2)
x2 x1
x
=
t
t2 t1
(6.3)
b) If the user types A or a for the input statement:- the program must ask the
user to type a value for the velocity v1 at time t1 . The program must then ask
the user to type a value for the velocity v2 at time t2 . The program must also
ask the user to enter a value for t1 and for t2 . The program must then compute
and display the average acceleration:
aavg =
v
v2 v1
=
t
t2 t1
Page 108
(6.4)
Chapter 6. Branching
109
6. Write a program that asks the user to enter a positive integer. The program must
then display one of the following
Even number, if the entered number is even (i.e. divisible by 2).
Odd number, if the entered number is odd (i.e. not divisible by 2).
Hint: A number is divisible by another number only if the remainder is equal to 0
after division. You may make use of the remainder (%) operator.
7. Write a program that asks the user to enter a positive integer. The program must
then display one of the following
Prime number, if the entered number is a prime number.
(6.5)
How many double dice throws would you like to make? 1000
Two: 27 Three: 57 Four: 82 Five: 99 Six: 132 Seven: 168
Eight: 135 Nine: 124 Ten: 89 Eleven: 57 Twelve: 30
Page 109
Chapter 6. Branching
110
10. Write a program that generates two random integers between 0 and 100 and asks
the user to enter the product of the two numbers. The program must continue to
ask the user to guess the product of the two numbers as long as the provided answer
is wrong. When the user supplies the correct answer the program must display the
correct answer to the screen with an appropriate message.
11. Extend the previous program such that every time the user completes a multiplication question, the program must ask the user whether he/she would like another
question. If the user enters 1 the program should continue to ask the user another
multiplication question otherwise the program should stop.
12. Dice run is a game of chance and requires a person to throw two dices. A person
continues to throw two dices until a 2 or 12 is thrown. The number of throws
required to throw a 2 or 12 are then counted to give a score for the run. Write a
program that computes and displays the average score over a user specified number
of runs of dice run.
Page 110
Chapter 7
Additional examples and statements
In this chapter we will cover a few additional statements, with short examples, commonly
used in Python (Section 7.1). We will also look at how to manipulate strings and string
objects for better output (feedback from the program). We will then apply what has
been learnt thus far to solving a few additional mathematical and engineering problems
(Section 7.2).
7.1
7.1.1
Additional statements
lambda function
A very powerful Python command is the lambda statement. It allows us to create additional functions that we can use in Python. The general syntax for the lambda function
is as follows:
Page 111
112
Take Note:
The given variables must be separated by a comma and a space (, )
Note the double colon (:) after the variables and before the function. This tells
Python where the function, based on the variables, starts.
If the double colon (:) is left out you will see an error message.
From this structure you can see that you create an additional function by using the
lambda statement, followed by the variables for the function, followed by a double colon
(:), followed by the function based on the variables.
As an example, consider the statement:
This statement defines a new function quad which is the quadratic equation x2 4x+10,
which is only a function of one variable (x). We can evaluate the new function quad at
any desired value x. Heres an example of a program using the lambda statement:
In [#]: quad(2.0)
Out [#]: 6.0
In [#]: x2 = 3.0
In [#]: quad(x2)
Out [#]: 7.0
In [#]: x3 = 5.0
In [#]: f3 = quad(x2)
In [#]: print f3
15.0
In [#]:
Page 112
113
We will use the lambda statement to expand the suite of built-in Python functions.
Especially when we work with long, complicated functions which we need to evaluate
often, the lambda statement is very useful.
As a second example consider the following function:
This statement defines a new function radius, which is the radius of a circle (r2 =
x2 + y 2 ) and is a function of two variables (x and y). We can evaluate the new function
radius at any desired value of x and y. Heres an example of a program using the lambda
statement:
In [#]: radius(0, 1)
Out[#]: 1.0
In [#]:
7.1.2
The use of the raw input statement was already introduced in Chapter 6 (Section 6.3).
I will, however, formally cover the use of the raw input statement in this section. The
raw input statement is used whenever our program requires input from the user. It is
unlikely that every person that uses our program wants to execute it using the same
settings. Consider again the Taylor Series approximation for (Section 5.4):
taylor_pi = 0.0
# The required accuracy typed in by the programmer
accuracy = 0.001
taylor_term = 1
#initialize greater than accuracy
k = 0
#term counter
while abs(taylor_term) > accuracy:
taylor_term = (4.0 * (-1)**k) / (2*k + 1)
Page 113
114
taylor_pi += taylor_term
k += 1
print "pi = ", taylor_pi
print "k = ", k
For example, the name accuracy in the program above is set to 0.001. But, some
users might require a more accurate value for and would like to set the name accuracy
to 0.0001. The simplest (and also least elegant) solution is to physically change the value
of accuracy by deleting and editing it. Then save the file and execute the program again.
Should you do that, the output of the program becomes:
pi = 3.14164265109
k = 20001
A more elegant solution is to develop the program such that certain settings are not
entered as fixed values, rather they are obtained from the user. The raw input statement
is used to do exactly this. You still type the name followed by the equal sign, but instead
of some number, we use the raw input statement:
Whatever you type between the double quotation marks appear exactly as is in the
IPython Console. This message is supposed to tell the user what he or she is supposed to
enter. A cursor appears immediately afters the message. The user types some input and
presses enter. The entered value is assigned to the name at the left of the equal sign. If
you omit the descriptive message, the program will still work, but the user will have no
idea what to enter in the IPython Console. The user will be confronted by a cursor in
the IPython Console with no instruction what to do. In the Taylor series example above,
I think it makes sense to rather get the required accuracy from the user. We do this by
changing the lines
115
However if we make these change and run the program we get the following output:
So what is happening? Why did the program not compute ? Why did the while
loop statement not execute (lines 6 9)?
This is because the raw input statement returns the users input as a string. So the name
accuracy contains the string "0.0001". For interest sake lets take a look at what the
condition for while loop statement (line 6) returns if accuracy is a string:
In [#]: taylor_term = 1
In [#]:
From this little example it is clear why the while loop statement did not execute. We
first need to convert the string to a floating point number (real number). We do this be
using the float() statement. The final version of the Taylor series approximation for
is given as:
taylor_pi = 0.0
# Get the required accuracy from the user
accuracy = float(raw_input("Input an accuracy to calculate pi: "))
taylor_term = 1
#initialize greater than accuracy
k = 0
#term counter
while abs(taylor_term) > accuracy:
taylor_term = (4.0 * (-1)**k) / (2*k + 1)
Page 115
116
taylor_pi += taylor_term
k += 1
print "pi = ", taylor_pi
print "k = ", k
Take Note:
The raw input statement ALWAYS returns the users input as a string. If you want
a numerical value input from the user you have to convert the string input, from the
user, to either an integer (int()) or floating point number (float()).
More Info:
The float() function converts either an integer or a string representation of an
integer of float to an float:
In [#]: float("2.5")
Out[#]: 2.5
In [#]: float(5)
Out[#]: 5.0
In [#]:
7.1.3
String manipulation
String manipulation is useful for either displaying results and information to the screen
(feedback from the program) or for witting results and information to a file (discussed in
Chapter 12). So far we have seen one example of string manipulation: the plus sign (+),
which adds two strings together (Section 6.4). Let us look at a few more:
7.1.3.1
For completeness of this section I will include the string plus (+) operator again. The
plus sign (+) between two or more strings will join them together:
Page 116
117
In [#]: "The quick brown " + "fox jumps " + "over the lazy ..."
Out[#]: The quick brown fox jumps over the lazy ...
In [#]:
You can see from this example that you can also chain multiple strings together with
the plus sign (+).
Take Note:
The string plus operator (+) can only be used to join strings. The following will thus
give an error in Python:
"The meaning to life is " + 42
7.1.3.2
The string multiply operator (*) is used by multiplying a string (or string object) with
an integer and this will repeat the string by the value of the integer specified. Consider
the following examples:
In [#]: a * 5
Out[#]: Some text. Some text. Some text. Some text. Some text.
In [#]: a = a + "\n"
In [#]: a *= 4
In [#]: a
Out[#]: Some text. \nSome text. \nSome text. \nSome text. \n
In [#]: print a
Some text.
Some text.
Some text.
Some text.
In [#]:
Page 117
118
You can see from this example that multiplying a string with an integer will repeat
that string to the value of the integer. You should also notice that the assignment operators (+=) and (*=) work in the same way.
Take Note:
The string multiply operator (*) can only be used to multiply a string with an integer.
The following will thus give an error in Python:
"Some Text. " * 2.5
More Info:
The \n character represents a newline. Whenever you print the newline (\n)
character a new line is started as shown in the example above.
The string plus operator (+) and multiply operator (*) can be combined in anyway
you can imagine, as long as they adhere to the rules give in each section. E.g. ("The "
+ "dog. ") * 3
7.1.3.3
This operator makes Python one of easiest programming languages to use when it come
to string manipulation. This operator is used to insert numerical values and/or strings
into an existing string template. Lets again consider the Taylor Series approximation for
:
taylor_pi = 0.0
accuracy = 0.0001
taylor_term = 1
#initialize greater than accuracy
k = 0
#term counter
while abs(taylor_term) > accuracy:
taylor_term = (4.0 * (-1)**k) / (2*k + 1)
taylor_pi += taylor_term
k += 1
print "pi = ", taylor_pi
print "k = ", k
119
The (%f) symbols in a string template are seen, by Python, as place holders for
information that we want to insert into the string template at these locations. After the
string template we use the (%) symbol to tell Python what information to add in the
locations of these place holders.
In this example above we add a (%f) symbol (place holder) in the string template
to tell Python that we will be inserting a floating point number into this location of the
string template. After the string template we use (%) symbol to tell Python that the
object after this symbol is what needs to be inserted into the string template (in the place
holder location). So pi is inserted into the string template on line 9 and accuracy is
inserted into the string template on line 10.
We can further change the formatting of the data by specifying if Python should
display an integer, float, scientific number or an additional string in the string template
(i.e. we can change the type of the place holder is the string template):
(%d) - insert an integer number
(%f) - insert a floating point number
(%e) - insert a scientific number
(%s) - insert another string
Consider the following examples:
Page 119
120
In [#]:
From this example you can see that we have added two place holders (or insert
points) in our string template and we tell Python to insert 12.44213 and a string into our
template. And as you would expect, in line 1 and 2, the integer formatting for the place
holder only inserts the integer part of the number (12.44213) into the string template
and discards the rest.
Take Note:
When inserting multiple pieces of information into a string template with multiple
place holders you have to enclose the information in round brackets and separate
the pieces of information with a comma and a space (, ), as shown in the above
example.
The last formatting example Im going to show is how to adjust the number of decimal
places that are displayed for either a floating point number or a scientific number. In order
to do this all you need to do is add a dot and the number of decimal places you want to
display (.5) in front of the letter (f, e). Consider the following examples:
121
In [#]:
7.1.3.4
String functions
So far I have told you that only modules contain functions, that we can use in Python.
Well, this is not entirely true. String objects also have many functions associated to them.
Lets look at a few of these functions (with my str = "hello world!"):
1. Convert the string to upper case characters:
In [#]:
Similarly to modules, the dot (.) tells Python that the upper function resides in the
string object.
2. Convert the string to lower case characters:
In [#]:
In [#]:
122
In [#]:
More Info:
Type help(str) in the IPython Console to see a list of available functions associated
to the string object (scroll past any function with in front or behind the name,
these functions are outside the scope of this course)
Alternatively you can create a string object e.g. a="name" and then type a. (note
the dot) and hit the tab key, IPython will then print a list of functions associated to
the string object to the screen.
7.2
Additional examples
In this section we will look at a few additional examples. We will not learn anything new,
we will simply apply what we have leant thus far to solving a few additional mathematical
and engineering problems. It is important that, in this section, you understand how the
logic of solving the problem is converted into a program.
7.2.1
(7.1)
As p approaches zero the above expression approaches the natural logarithm. We can
start the program by computing e for a value p = 0.1. We can perform this computation
inside a for loop statement, but we must ensure the value for p progressively approaches
zero every time the loop is repeated. The following program will do precisely that:
p_val = 0.1
for i in range(10):
Page 122
123
Do you see how I make sure that the value p val decreases every time the loop is
executed? I divide p val by 10 in line 4. So p val starts off with a value of 0.1 outside
the loop and at line 4, p val is divided by 10 and re-assigned to p val i.e. p val now has
the value 0.01. This happens every time line 4 is executed until the for loop is completed.
So my program uses a for loop to simulate the limit as p approaches zero. The output
of the above program is:
e
e
e
e
e
e
e
e
e
e
=
=
=
=
=
=
=
=
=
=
2.59374246
2.70481383
2.71692393
2.71814593
2.71826824
2.71828047
2.71828169
2.71828180
2.71828205
2.71828205
p
p
p
p
p
p
p
p
p
p
=
=
=
=
=
=
=
=
=
=
1.000e-02
1.000e-03
1.000e-04
1.000e-05
1.000e-06
1.000e-07
1.000e-08
1.000e-09
1.000e-10
1.000e-11
Note that in this program we did not explicitly make use of the value of the index i.
In such a program, the index is merely used as a counter i.e. the statements, inside the
for loop, are repeated the required number of times. However, the value of the index, i
in this case, is available inside the loop and can be used if required. In fact, the index
is just another name, which just happens to control the execution of the for loop. The
next example illustrates how the index can be used inside the for loop:
In this example, we did not need to define an initial value for p val outside the for
loop, since the value of p val is computed as a function of the index i. This program
will produce exactly the same output as the one before. Again note that there is not a
unique solution to a particular problem. As long as the list of instructions we give to the
computer makes sense and is consistent with the required task, the program is acceptable.
Page 123
124
You should be aware by now that if we wanted to solve for e only to a required
accuracy we would need to replace the for loop statement with a while loop statement.
The following program will allow the user to specify the accuracy to which e should be
computed:
p_val = 0.1
exp = 0
error = 1
accuracy = float(raw_input("Input accuracy: "))
while abs(error) > accuracy:
e_old = exp
exp = (1 + p_val) ** (1 / p_val)
error = exp - e_old
p_val /= 10
print "e = ", exp
We initialise p val with the value of 0.1 as before, in line 1. In line 2 we initialise exp
to 0, this is so that when we store exp in e old (line 6) for the very first loop Python
wont give us an error message saying that exp is not defined.
7.2.2
Numerical differentiation
Limits are also used to compute the derivative of a function. The definition of the derivative f 0 (x) of a function f (x) is given by
f 0 (x) = lim
h0
f (x + h) f (x)
h
(7.2)
The problem with the above definition, if we want to use a computer to compute the
derivative, is that we cannot divide by zero. We have encountered this problem before.
Although we cannot divide by zero, lets try dividing by a number that approaches zero.
We generate a series of approximations to f 0 (x) by using values of h that become progressively smaller. If such a series converges to some value, we have found the derivative.
You already know enough about programming to compute the derivative of a given
function f (x) using Eq.(7.2). As an example, lets try to compute the derivative of f (x) =
sin(x) at x = 0. It is important to realise that we can only compute the derivative of f (x)
at a specified value for x. We cannot get the analytical derivative, rather we compute the
numerical derivative at a specified value for x. The following program approximates the
derivative of sin(x) at x = 0 by using a h-value of 0.1:
Page 124
125
delta_x = 0.1
x_val = 0.0
func1 = sin(x_val + delta_x)
func2 = sin(x_val)
delta_func = (func1 - func2) / delta_x
print "df = ", delta_func
df =
0.998334166468
The exact derivative is cos(0) = 1.0. The numerical derivative is already accurate to 2
digits. We can get a more accurate value for the numerical derivative by using a smaller
value for h. If we used delta x = 0.001 instead, the output is
df =
0.999999833333
which is accurate to 6 digits. Lets also consider the function f (x) = ex . The analytical
derivative is f 0 (x) = ex . If we use this program:
delta_x = 0.1
x_val = 1.0
func1 = exp(x_val + delta_x)
func2 = exp(x_val)
delta_func = (func1 - func2) / delta_x
print "df = ", delta_func
the output is
df =
2.85884195487
Page 125
126
instead of the analytical value 2.7182818284589 . . . . This is only accurate to the first
digit. If we rather use delta x = 0.001 the output is
df =
2.71964142253
which is better but only accurate to 3 digits. These two examples illustrate the
difficulty in obtaining accurate numerical gradients: we do not know in advance how
small h must be. Sometimes h = 0.001 produces a numerical gradient accurate to 6 digits
but for some other function is might only give an answer accurate to 3 digits.
These examples illustrate the general approach to developing a program that must
perform some specified task. First of all, play around with specific examples in order
to fully understand the problem. Then you can generalise and develop the final version.
Lets investigate the behaviour of the numerical differentiation formula further. Ill use a
for loop statement to check how the numerical gradient value changes as the value for h
decreases (i.e. tend towards zero). Ill use the f (x) = ex function again.
x_val = 1.0
for i in range(1, 17):
delta_x = 10 ** (-i)
func1 = exp(x_val + delta_x)
func2 = exp(x_val)
delta_func = (func1 - func2) / delta_x
print "df = %.8f" % delta_func
Do you understand this program? Make sure that you follow the logic. We evaluate
the numerical derivative of the function ex at x = 1. The for loop index i is used to define
the value of delta x. As the index value changes from 1 to 16, the corresponding delta x
value changes from 101 to 1016 . The following output is produced (Ive modified the
output below to save space):
df
df
df
df
df
df
=
=
=
=
=
=
2.85884195
2.73191866
2.71964142
2.71841775
2.71829542
2.71828319
Page 126
df
df
df
df
df
df
df
df
df
df
=
=
=
=
=
=
=
=
=
=
127
2.71828197
2.71828182
2.71828204
2.71828338
2.71831446
2.71871414
2.71782596
2.70894418
3.10862447
0.00000000
The numerical gradients approach the analytical value as delta x is decreased, it then
stabilises for a range of delta x values (106 to 1010 ) that produce numerical gradients
very close to the analytical value and finally the numerical gradients become inaccurate
if delta x becomes too small. This is known as reaching machine precision i.e. the two
numbers func1 and func2 that we are subtracting are almost identical and the computer
does not use enough digits to perform this computation accurately. In fact, a value for
delta x = 1016 is so small that x val and x val + delta x are identical using 15 digits.
Therefore delta func = 0 for this case (and all values for delta x smaller than 1016 ).
So the trick in computing accurate numerical gradients is to find the value of delta x
that is just right: not too big to give inaccurate values and not so small to reach machine
precision.
Take Note:
Computations with very small numbers (around 1015 ) will give unexpected results
or errors. This is known as reaching machine precision.
The solution I suggest makes use of a while loop statement. We have seen the while
loop statement before when we computed a Taylor series. The idea then was to keep on
adding terms to the series until the function value approximation converged to within some
specified tolerance. I will make use of the same logic now: Ill keep on making delta x
smaller until the numerical derivative converges to within some specified tolerance. Heres
the program:
accuracy = 0.001
x_val = 1.0
delta_x = 0.1
func1 = exp(x_val + delta_x)
Page 127
128
func2 = exp(x_val)
delta_func = (func1 - func2) / delta_x
grad_change = 1
while grad_change > accuracy:
df_prev = delta_func
delta_x /= 10
func1 = exp(x_val + delta_x)
delta_func = (func1 - func2) / delta_x
grad_change = abs(delta_func - df_prev)
print "h = ", delta_x
print "df = ", delta_func
Do you understand the logic of the above program? Compare it to the last example of
the Taylor series program that used a while loop statement (Section 5.4). The similarity
should be obvious.
I start off by calculating a numerical gradient using the value delta x = 0.1 outside
the while loop statement. Next, I have to decide on the condition which has to be satisfied
(be True) in order to repeat the loop: this is the most important part of the program.
I decided to use a name grad change in which I store the change in the value of the
numerical gradient each time delta x is made smaller. Thats why I need to compute the
initial value for delta func outside the loop: I need the initial value in order to calculate
the change in the value. So the condition of the while loop statement is that I check
whether or not the change in the numerical gradient value is larger than the specified
accuracy. If this is true, I have to make delta x smaller (I do this by dividing delta x
by 10 in program line 12) and repeat the numerical gradient computation. This process
repeats itself until the change in the numerical gradient becomes smaller than the required
accuracy.
Inside the loop, the value of delta x is changed. Therefore, the function evaluated at
x val + delta x will change (func1 in my program). Since the value of x val doesnt
change, the function value evaluated at x val doesnt change and there is no need to
recompute func2 inside the loop. You are welcome to do it, but you are just wasting
valuable computer time. In my program, the value contained in func2 was assigned
outside the loop and that value remains in the name func2 until it is replaced by some
other value (which does not occur in this example). The program produces the following
output:
h = 1e-05
df = 2.71829541996
Just to make things interesting, I now include a different solution to the above probPage 128
129
lem.
accuracy = 0.001
x_val = 1.0
delta_x = 1.0
delta_func = 0.0
grad_change = 1
while grad_change > accuracy:
df_prev = delta_func
delta_x /= 10
func1 = exp(x_val + delta_x)
func2 = exp(x_val)
delta_func = (func1 - func2) / delta_x
grad_change = abs(delta_func - df_prev)
print "h = ", delta_x
print "df = ", delta_func
In the example above, I do not compute anything outside the while loop statement.
This means that I now have to be careful not to use anything inside the while loop that
is not defined the very first time the loop is executed. The first statement inside the
while loop saves the previous value for the derivative delta func in the name df prev.
This logic is OK from the second time the while loop is executed (since I compute the
name delta func in line 13 every time the while loop is executed). But I need some
value for delta func the very first time the loop is executed. Ive decided to use a value
delta func = 0.0, which I assign outside the while loop statement. This version of the
program is probably a bit risky, since the value I assign to delta func outside the while
loop is not based on a formula, rather I pick some arbitrary number. Note that program
line 12 makes the program less efficient than the previous solution. Since x val does not
change, it is unnecessary to recompute func2. An improved version of this program will
move program line 12 outside the while loop, somewhere below line 4.
7.2.3
Ill persist with my approach to teach programming by using yet another mathematical
algorithm. Although I will now give some background to the method, this is not necessary.
In general, I will expect you to be able to program some method even if you have never
heard about it before. I will give you enough information to implement the method
though.
Page 129
130
(7.3)
The function f (x) is one-dimensional i.e. it depends only on the variable x. Since
f (x) is non-linear, we cant simply isolate x on the left and group all constants to the
right. Examples of non-linear functions are f (x) = x sin(x) x2 , f (x) = cos(x/2) and
f (x) = x2 4x + 2. Non-linear functions can have more than one solution, but if we use
a computer to find the solutions, we will have no idea how many solutions there are. The
best we can do is to find one of the many solutions.
As Ive mentioned, Newtons method is iterative. That means that if we have a current
guess for the solution, we can use that guess to find a better guess. We keep going like
that until we find some x that satisfies f (x) = 0. Newtons algorithm starts with some
guess x0 to the problem f (x) = 0 and we then generate a series of improved solutions x1 ,
x2 , x3 etc. from the equation:
xk+1 = xk
f (xk )
f 0 (xk )
for k = 0, 1, 2, 3, . . .
(7.4)
where f 0 (x) is the derivative of f (x). This is sufficient information to program Newtons method. You simply have to realise that Eq.(7.4) defines the next solution (xk+1 )
in terms of the previous solution (xk ). So if we have xo , we can get x1 . If we have x1 , we
can get x2 . And so on.
Just to be friendly, Ill give you a bit of background on Eq.(7.4). Consider a general
non-linear function f (x) in the vicinity where f (x) goes through zero. This is depicted in
Figure (7.1). Also assume that we have some current guess xk and we want to find xk+1
that is an even better guess to f (x) = 0.
f (x)
y6
xk+1
xk
f (x
k)
131
This is depicted graphically in Figure (7.1). Now we need a mathematical expression for
Newtons method. The slope of the tangent line at xk is given by f 0 (xk ). This slope is
also given by
f (xk ) 0
(7.5)
f 0 (xk ) =
xk xk+1
If we solve Eq.(7.5) for xk+1 , we get
xk+1 = xk
f (xk )
f 0 (xk )
(7.6)
I hope that you agree Newtons method is elegant and simple. We only require three
pieces of information i.e. xk , f (xk ) and f 0 (xk ) in order to find the next (improved) solution
to f (x) = 0. Ill now illustrate Newtons method for the function f (x) = cos(x/2) using
the initial guess x0 = 3. Hopefully you recognise that Im looking for the solution x = .
Lets see how Newtons method produce the series x1 , x2 , x3 , . . . that converges to . We
first get the function value and the derivative value:
and
f (x0 ) = cos( 23 ) = 0.070737201
0
1
3
f (x0 ) = 2 sin( 2 ) = 0.498747493
(7.7)
x1 = x0
(7.8)
and
(7.9)
x2 = x1
(7.10)
132
You might think the above exercise is a waste of time. But, how do you expect to
program a method if are not completely comfortable with the application of the method.
In my opinion, this should be your approach whenever you attempt a new program. First
make sure that you yourself can perform the task required, only then attempt to give a
computer a list of instructions to do what you just did. So here we go. In the program
below I use a computer to perform 5 iterations of Newtons method.
x_val = 3.0
for i in range(5):
func_val = cos(x_val / 2.0)
deriv_val = -0.5 * sin(x_val / 2.0)
x_val -= func_val / deriv_val
print "x = %.8f
f(x) = % .8f" % (x_val, func_val)
x
x
x
x
x
=
=
=
=
=
3.14182969
3.14159265
3.14159265
3.14159265
3.14159265
f(x)
f(x)
f(x)
f(x)
f(x)
= 0.07073720
= -0.00011852
= 0.00000000
= 0.00000000
= 0.00000000
Do you think the above program is a good one? In general, do you think that we will
know in advance (i.e. before we run our program) how many iterations would be required
to find the solution? The answer is No: it is unlikely that we will know how many times
the for loop must be executed. Lets modify the program to rather make use of a while
loop statement.
Whenever we make use of a while loop statement, the most important part is to figure
out the condition that must be satisfied (be True) in order to keep on repeating the while
loop. Or, find a condition which tells us that the loop must not be repeated any more.
Can you suggest an idea? I can think of two conditions that tell me that we have found
the solution:
1. If f (x) = 0, we can stop. However, when we are dealing with real numbers represented by a computer, we cannot expect to check if the function value f (x) equals
zero. What we can do, is to check if f (x) is almost zero i.e.
tol f (x) tol
Page 132
or
|f (x)| tol
(7.11)
133
where tol is some small value (I use terminology such as tolerance, or accuracy).
If Eq. 7.11 is satisfied, it means that we have found the solution (or at least an
accurate enough approximation to the solution) and the while loop must terminate.
However, the syntax of a while loop statement requires that the condition must be
satisfied (be True) to continue (not terminate) the loop. So in our program the
inequality changes to:
where func val is a name that contains the function value. If we use this idea,
the specified accuracy ensures that f (x) will be less than or equal to the specified
accuracy once the program terminates.
2. If the absolute value of x = xk+1 xk = f (xk )/f 0 (xk ) is very small, we can stop.
This is an indirect criterion that tells us we have found the solution. You should
recognise from Eq. 7.4 that if f (xk ) = 0, xk+1 = xk , i.e. x = 0. If we want to use
this condition in our program, the while loop statement will be:
where delta x is the name containing xk+1 xk . If we use this idea, the specified
accuracy ensures that the distance between two consecutive guesses for x will be
less than or equal to the specified accuracy.
Here follows a program that makes use of the first idea (i.e. we want to make sure
that f (x) 0, condition 1 above):
x_val = 3.0
func_val = cos(x_val / 2.0)
tol = 1E-6
while abs(func_val) > tol:
deriv_val = -0.5 * sin(x_val / 2)
delta_x = -func_val / deriv_val
x_val += delta_x
func_val = cos(x_val / 2)
print "x = %.8f
f(x) = % .8f" % (x_val, func_val)
Do you follow the logic of the above program? I define the original guess to x in
line 3. Next I compute the function value in line 4 and then define a tolerance in line 5.
Page 133
134
Lines 4 and 5 are essential when viewed in conjunction with line 6. In line 6 I test if the
absolute function value is greater than the tolerance. In order to perform this check, I
need the values of both func val and tol. Inside the loop, I compute the derivative (line
7), followed by the change in x (line 8). In line 9 I increment x (the old value) with x
to get the new improved guess for x. In program line 10, I compute the function value,
using the x value just computed in line 9. The function value which was just computed
in line 10 is compared to tol and a decision is made on whether or not to repeat the loop
once more (line 6).
The program above produces the following output. As you can see, using the while
loop statement we only require two iterations before |f (x)| is less than 106 .
x = 3.14182969
x = 3.14159265
f(x) = -0.00011852
f(x) = 0.00000000
As an alternative, Ill now give a few examples of how we can use the condition
|x| > tol to decide whether or not to repeat the loop.
7.2.3.1
Example 1
In this example, I make a copy of x val just before I compute the new value of x val
(line 9). After the new value of x val is computed, I subtract it from the old value (line
9) and use this difference (delta x) to decide whether or not to repeat the while loop
(line 6).
x_val = 3.0
tol = 1E-6
delta_x = 1
while delta_x > tol:
func_val = cos(x_val / 2.0)
deriv_val = -0.5 * sin(x_val / 2.0)
x_old = x_val
x_val -= func_val / deriv_val
delta_x = abs(x_val - x_old)
print "x = %.8f
f(x) = % .8f" % (x_val, func_val)
Page 134
7.2.3.2
135
Example 2
In this example, I use a name x new to store the value of the next x. Since I dont destroy
the content of the name x val in this case, I compute the difference between the new
estimate x new and previous estimate x val in line 10 and store it in delta x. However,
since the next value of x is now stored in x new, I need one additional program line. Line
11 moves the new value of x stored in x new to the name x val. Youll see that if the
program now returns to line 6 and decides to repeat the while loop, the name x val in
lines 9, 10 and 11 contains the improved guess for x.
x_val = 3.0
tol = 1E-6
delta_x = 1
while delta_x > tol:
func_val = cos(x_val / 2.0)
deriv_val = -0.5 * sin(x_val / 2.0)
x_new = x_val - func_val / deriv_val
delta_x = abs(x_new - x_val)
x_val = x_new
print "x = %.8f
f(x) = % .8f" % (x_val, func_val)
7.2.3.3
Example 3
In this example, we recognise that x = xk+1 xk = f (xk )/f 0 (xk ). Therefore, we might
as well check if f (xk )/f 0 (xk ) is small enough to decide whether or not to terminate the
loop. By now you should be able to figure out the logic in the program below.
x_val = 3.0
tol = 1E-6
delta_x = 1
while abs(delta_x) > tol:
func_val = cos(x_val / 2.0)
deriv_val = -0.5 * sin(x_val / 2.0)
delta_x = -func_val / deriv_val
x_val += delta_x
print "x = %.8f
f(x) = % .8f" % (x_val, func_val)
Page 135
7.2.3.4
136
Example 4
In this last example I will use the combined condition to test whether or not to continue
the while loop statement: if |x| > tol and |f (x)| > tol continue the while loop. By
now you should be able to figure out the logic in the program below.
x_val = 3.0
tol = 1E-6
delta_x = 1
func_val = cos(x_val / 2.0)
while (abs(delta_x) > tol) and (abs(func_val) > tol):
func_val = cos(x_val / 2.0)
deriv_val = -0.5 * sin(x_val / 2.0)
delta_x = -func_val / deriv_val
x_val += delta_x
print "x = %.8f
f(x) = % .8f" % (x_val, func_val)
7.2.3.5
Example 5
Page 136
func_val = func(x_val)
delta_x = 1
while abs(delta_x) > tol:
deriv_val = deriv(x_val)
delta_x = -func_val / deriv_val
x_val += delta_x
func_val = func(x_val)
print "x = %.8f
f(x) = % .8f" % (x_val, func_val)
x = 3.14182969
x = 3.14159265
x = 3.14159265
f(x) = -0.00011852
f(x) = 0.00000000
f(x) = 0.00000000
If the function and derivative are change in program lines 6 and 7 respectively:
x
x
x
x
x
=
=
=
=
=
2.00000000
1.75000000
1.73214286
1.73205081
1.73205081
f(x)
f(x)
f(x)
f(x)
f(x)
= 1.00000000
= 0.06250000
= 0.00031888
= 0.00000001
= -0.00000000
Page 137
137
7.2.4
138
In the previous example we developed a program that uses Newtons method to solve a
non-linear equation f (x) = 0. One of the requirements for the program is the derivative
of the function f (x). Which is easy enough to add to the program if you either have
simple functions (e.g. f (x) = 2x5 + 4) or you remember the derivative of the function
f (x). However, the more likely scenario is that you will either have very complicated
functions or you cant differentiate them.
Fortunately, we have already developed a program that can calculate numerical gradients. So instead of adding the derivative of f (x) to the program, we can compute a
numerical derivative. A program using Newtons method to solve f (x) = 0 using numerical gradients might look like this:
h_val = 0.1
diff = 2.0 * tol
deriv_val = (func(x_val + h_val) - func_val) / h_val
while diff > tol:
h_val /= 10
df_previous = deriv_val
deriv_val = (func(x_val + h_val) - func_val) / h_val
diff = abs(deriv_val - df_previous)
139
numerical gradient. When we first programmed Newtons method, these 8 lines were
reduced to a single program line:
deriv_val = deriv(x)
The same examples I used to illustrate Newtons method, using analytical derivatives,
now follow. Compare the results and youll see the subtle differences in the computed
values.
func = lambda x:
x = 3.14182969
x = 3.14159265
x = 3.14159265
func = lambda x:
f(x) = 0.07073720
f(x) = -0.00011852
f(x) = 0.00000000
x
x
x
x
x
=
=
=
=
=
1.99999992
1.75000002
1.73214286
1.73205080
1.73205081
f(x)
f(x)
f(x)
f(x)
f(x)
= -2.00000000
= 0.99999967
= 0.06250007
= 0.00031888
= -0.00000003
The above implementation of Newtons method is not very efficient since we spend a
lot of computational effort inside the inner while loop statement to find an appropriate
value for h val. Im of the opinion that once we obtain a value for h val that gives
accurate numerical derivatives, we do not need to repeat this calculation for every Newton iteration. (However, if we change the function f (x), we might need to find a new
h val value). We can simplify Newtons method, using numerical gradients, by using two
consecutive while loop statements instead of two nested while loop statements. In the
program that follows, program lines 1018 are used to compute an appropriate value of
Page 139
140
h val that provides an accurate numerical gradient. This value for h val is then used in
all subsequent computations.
More Info:
Nested statements refers to statements within statements:
E.g.: nested for loop statements refers to for loop statements within for loop
statements
h_val = 0.1
diff = 2.0 * tol
func_val = func(x_val)
deriv_val = (func(x_val + h_val) - func_val) / h_val
while diff > tol:
h_val /= 10
df_previous = deriv_val
deriv_val = (func(x_val + h_val) - func_val) / h_val
diff = abs(deriv_val - df_previous)
The two previous programs again illustrate how different solutions can exist to the
same problem. Its up to you as the programmer to decide on a particular implementation.
Page 140
7.2.5
141
Here we go again. Ill use a simple numerical algorithm to highlight some programming
ideas. To solve an equation f (x) = 0, we have already used Newtons method. Another
method, which is based on common sense rules is called the bisection method.
The idea is simple. First of all, we have to start with a function f (x) as well as an
interval x [xl , xu ] such that the function undergoes a sign change between xl and xu . xl
refers to the lower bound of the interval and xu refers to the upper bound of the interval.
The sign change implies that either f (xl ) < 0 and f (xu ) > 0, or f (xl ) > 0 and f (xu ) < 0.
If the function is continuous and it changes sign somewhere between xl and xu there must
be a point x between xl and xu where f (x) = 0.
The interval is now divided into two equal parts. The function is also evaluated at
this midpoint xm = 21 (xl + xu ). All that we have to do now is to decide in which of the
two new intervals the solution lies. Either the solution lies between the lower bound xl
and the midpoint xm , or the solution lies between the midpoint xm and the upper bound
xu .
We want to implement the bisection algorithm to find a solution to f (x) = 0. Recall
that we have a lower bound xl , midpoint xm and upper bound xu and we have to decide
in which of the two possible intervals the solution lies (i.e. in which of the two intervals
a sign change occurs). An elegant method is to check if f (xl ) f (xm ) < 0 ? If this
condition is true, it means that there is a sign change between the lower bound and the
midpoint. Therefore the solution must lie in this lower interval, and we discard the upper
interval.
If the condition is not true, the sign change does not occur in the lower interval and
it must therefore occur in the upper interval. The solution must lie in the upper interval,
and (in this case) the lower interval is discarded. We then repeat the process on the
retained interval until the solution is found.
So when do we stop dividing intervals in half? There is two possible criteria we can
use to decide when to stop the algorithm.
As soon as |f (xm )| < , where is some very small number, we can assume we have
found the solution. There is no way of knowing how many times the initial interval
has to be divided in two to reach the point where |f (x)| < , so we will have to
make use of a while loop statement.
As soon as the interval size is very small (i.e. xm xl < or xu xm < ) we
know that we have found the solution to x within an accuracy of . We can also
use a while loop statement here, but is it really necessary? We know the size of
Page 141
142
the initial interval and we know that this initial interval is repeatedly divided in
two equal parts. If the initial interval size is x, the interval size
after
1 iteration
1 1
1 2
1
is 2 x. After 2 iterations, the interval size
is equal to 2 2 x = 2 x. After
1 n
n iterations, the interval size will be 2 x. So we can determine the required
number of iterations n if we require the final interval size to be equal to some
specified size , i.e.
1 n
x =
(7.12)
2
We can solve Eq. 7.12 for n:
ln
n=
ln
x
1
2
(7.13)
Here follows both above implementations of the bisection method. First the version
that uses a while loop statement. The comment statements in the program explains the
logic.
143
else:
# sign change must be in upper interval
# midpoint becomes new lower bound
x_lower = x_mid
f_lower = f_mid
# At the end of the if statement, we either updated
# the lower or the upper bound and now we must update
# the midpoint and associated function value
x_mid = 0.5 * (x_lower + x_upper)
f_mid = func(x_mid)
print "x_m = %.8f" % x_mid
Many students understand the use of the while loop statement (program line 21) as
well as the if statement (program line 24), but they cannot understand program lines 36
and 37. Ill do my best to explain:
All that the if statement does is to decide which of the two possible intervals to retain
(upper or lower). Either the upper bound is replaced with the midpoint (program line 26)
or the lower bound is replaced with the midpoint (program line 31). At the completion
of the if statement (program line 33), the new bounds have to be used to compute the
new midpoint (program line 36) and the associated function value (program line 37).
The new midpoint that is calculated, every time the while loop is executed, is computed in program line 36. Heres an example of using the bisection method to find a root
to the equation f (x) = 1.2 x in the interval [1.0; 2.0] (Of course we know the solution
is x = 1.2, but lets see how the bisection algorithm finds this solution).
Enter
Enter
Enter
x_m =
x_m =
x_m =
x_m =
x_m =
x_m =
x_m =
x_m =
x_m =
x_m =
x_m =
x_m =
144
Now for the bisection algorithm version that makes use of a for loop statement:
f_mid = func(x_mid)
print "x_m = %.8f" % x_mid
Enter
Enter
Enter
x_m =
x_m =
x_m =
x_m =
x_m =
x_m =
x_m =
x_m =
x_m =
x_m =
x_m =
x_m =
x_m =
x_m =
Page 145
145
Page 146
146
Chapter 8
Numerical integration
Recall from the Riemann sum in Calculus that integration implies finding the area under
a curve, as depicted in Figure 8.1.
Lets try to develop a program that can be used to integrate any function numerically.
A simple method to approximate the integral of a function f (x) between the lower bound
a and upper bound b, is by constructing rectangles over intervals between a and b, as
depicted Figure 8.1. We can then easily calculate and add the areas of the rectangles.
f(x)
f(x)
...
b
Rb
a
We can simply make the width of each rectangle the same i.e. x but then we still need
to calculate the height of each rectangle. We can compute the height of the rectangles in
various ways. I can think of at least 3 methods:
1. A left-point method, where the interval [a, b] is divided in n equal intervals of width
x and the height of each rectangle is computed at the left point of each interval.
Page 147
...
a
148
...
a
b
(i)
...
a
b
(ii)
b
(iii)
Rb
Figure 8.2: Numerical integration of a f (x)dx using a (i) left point method, (ii) right
point method and (iii) midpoint method.
I.e. the first rectangles height is f (a), the next rectangles height is f (a + x), and
so on. The last rectangles height is given by f (b x). This method is depicted
in Figure 8.2 (i).
2. A right point method is where the rectangle heights are computed by f (a+x), f (a+
2x), . . . , f (b) as depicted in Figure 8.2 (ii).
3. A midpoint method is where the rectangle heights are computed by f (a+ 21 x), f (a+
3
x), . . . , f (b 12 x) as depicted in Figure 8.2 (iii).
2
f (x)
f(a+ x)
f(a)
(8.1)
149
Should we approximate the total area using trapeziums, the integral is given as
Z b
f (x)dx 12 x [f (a) + f (a + x)] + 21 x [f (a + x) + f (a + 2a)] +
a
= x
1
2
+ 21 x [f (b x + f (b)]
(8.2)
f (a) + f (a + x) + f (a + 2x) + . . .
+f (b 2x) + f (b x) + 12 f (b)
(8.3)
Now that we have a few ideas of how to integrate a function numerically, lets develop
the program. First of all, Ill list the information that has to be available to perform
numerical integration:
1. The function f (x)
2. The bounds of integration
3. The number of intervals
4. The method i.e. left-point, right-point, mid-point or trapezium method
After we get the above information from the user (using a number of raw input
statements), Ill compute the interval size x and then use a for loop to add all the
areas. Heres my implementation of a numerical integration program:
lower = 0
upper = pi / 2
num_int = 20
150
elif method == 2:
# right-point method
for i in range(num_int):
x_val = lower + (i + 1) * delta_x
integral += delta_x * func(x_val)
elif method == 3:
# mid-point method
for i in range(num_int):
x_val = lower + i * delta_x + 0.5 * delta_x
integral += delta_x * func(x_val)
elif method == 4:
# trapezium method
for i in range(num_int):
x_L = lower + i * delta_x
x_R = x_L + delta_x
integral += 0.5 * delta_x * (func(x_L) + func(x_R))
# Display answer
print ("Integrating %s between " +
"%.3f and %.3f = %.6f") % (func_str,
lower,
upper,
integral)
Page 150
151
Do you notice the use of an outer while loop statement that keeps on repeating the
numerical integration program as long as the name method is not equal to 5. The program
listed above is used to numerically integrate the sin function between 0 and /2. The
analytical answer is 1, so lets see how the program performs using 20 intervals. I ran the
program once and entered option 1 to 5 in that order. The output follows:
8.1
As you can see from the example above 20 intervals is not very accurate for using either the
left-point or right-point methods, but it seems adequate for the mid-point and trapezium
methods. So how do we know in advance how many intervals we need to get an accurate
numerically integrated value? We dont, so therefore I suggest the use of a while loop
Page 151
152
statement to keep increasing the number of intervals until the numerically integrated result
converges to within some specified tolerance. Here follows the modified program:
lower = 0
upper = pi / 2
num_init = 20
acc = float(raw_input("Enter required accuracy: "))
153
elif method == 2:
# right-point method
for i in range(num):
x = lower + (i + 1) * delta_x
integral += delta_x * func(x)
elif method == 3:
# mid-point method
for i in range(num):
x = lower + i * delta_x + 0.5 * delta_x
integral += delta_x * func(x)
elif method == 4:
# trapezium method
for i in range(num):
x_L = lower + i * delta_x
x_R = x_L + delta_x
integral += 0.5 * delta_x * (func(x_L) + func(x_R))
# Display answer
print ("Integrating %s between " +
"%.3f and %.3f = %.6f") % (func_str,
lower,
upper,
integral)
print "%d equal intervals needed" % num / 2
# Display options again
print ("\n" +
"1: Left-point 2: Right-point 3: Mid-point \n" +
"4: Trapezium 5: Quit program")
method = input("Please enter your option: ")
Page 153
154
Ill only discuss the changes Ive made. In program line 8, I now call the number of
intervals num init, to indicate that this is the initial number of intervals. As the program
proceeds, the number of intervals increase. In program line 9 I also ask the user to enter
the required accuracy, which will be used in the condition for the while loop statement.
In program line 21 I define a name error, which is the difference between consecutive
numerical integrals (using a different number of intervals). The number of intervals is
initially set to the value specified by the user in program line 24.
In line 27 I set the name integral to zero. This statement is required because I assign
the value of integral to the name integral old in program line 34. The very first time
the while loop is executed (line 31) no calculations have been performed and therefore
no value is available in the name integral. Hence I require line 27.
Program lines 3960 compute the numerical integral, based on the chosen method.
This part of the program is unchanged. In line 64 I compute the difference between
the integral and the value obtained previously. I then double the number of intervals
in line 66 and the program returns again to the while loop in line 31. If the difference
between consecutive numerical integration values (computed in line 64) is smaller than
acc the while loop terminates and the program continues at program line 68. Answers
are displayed in the IPython Console and the options are re-displayed. Depending on the
users input, the program is either re-run (Method = 1 to 4), or terminates with a friendly
message (Method = 5). If error > acc, the while loop (program lines 3166) is repeated
again.
Here follows the output for the same example I presented earlier. An accuracy of
0.0001 is specified. As you can see, the different methods require a different number of
intervals to achieve this specified accuracy. Clearly the mid-point and trapezium methods
(num=80) is much more efficient that the left-point and right-point methods (num=10240).
155
Here follows one more version of this program, see if you can understand the differences.
lower = 0
upper = pi / 2
num_init = 20
acc = float(raw_input("Enter required accuracy: "))
while True:
print ("\n" +
"1: Left-point 2: Right-point 3: Mid-point \n" +
"4: Trapezium 5: Quit program")
method = int(raw_input("Please enter your option: "))
if method == 5:
Page 155
156
break
num = num_init
error = 2 * acc
integral = 0
while error > acc:
integral_old = integral
integral = 0
delta_x = (upper - lower) / num
for i in range(num):
if method == 1:
x_val = lower + i * delta_x
integral += delta_x * func(x_val)
elif method == 2:
x_val = lower + (i + 1) * delta_x
integral += delta_x * func(x_val)
elif method == 3:
x_val = lower + i * delta_x + 0.5 * delta_x
integral += delta_x * func(x_val)
elif method == 4:
x_L = lower + i * delta_x
x_R = x_L + delta_x
integral += 0.5 * delta_x * (func(x_L) + func(x_R))
8.2
157
Exercises
1. Write a program that computes the numerical integral of a selected function f (x)
between the user specified upper bound xU B and lower bound xLB as depicted in
Figure 8.4.
The user has to enter the number of intervals N he requires for the numerical
integration scheme. The interval size can then be computed using
f(x)
1 2 3
x LB
...
N1
N
x UB
x =
xU B xLB
N
(8.4)
The area of each interval must then be approximated using trapeziums. The area
of the first trapezium A1 is given by
A1 = x
f (xLB ) + f (xLB + x)
2
(8.5)
(8.6)
(8.7)
You should be able to derive the above formula on your own! If you consider the
calculation of the individual trapeziums e.g. A1 and A2 , youll see some of the
function evaluations are repeated e.g. f (xLB + x) is evaluated in both A1 and A2 .
Can you come up with a better algorithm where you only evaluate the function at
each point once without looking at the notes. Test then your expression and see if
it gives you the same answer as the algorithm above.
Page 157
158
2. Write a numerical integration program that asks the user to select a numerical
integration scheme by typing left-point for a left-point scheme, right-point for
a right-point scheme and midpoint for a midpoint scheme. Choose any function
f (x) you like for the program. The program must then ask the user to define the
upper bound xU B , lower bound xLB and accuracy . The program must then start
with N = 2 intervals and numerically integrate f (x). The program must then
double the N and recalculate the numerical integral. The program must continue
to do so until the difference of two consecutive calculated numerical integrals is less
than the user specified accuracy .
The left-point scheme, right-point scheme and midpoint scheme is illustrated in
Figure 8.2 and given below:
For a left-point scheme the interval [xLB , xU B ] is divided in n equal intervals of
width x and the height of each rectangle is computed at the left point of each
interval. I.e. the first rectangles height is f (xLB ), the next rectangles height is
f (xLB + x), and so on. The last rectangles height is given by f (xU B x).
This scheme is depicted in Figure 8.2 (i).
A right point method is where the rectangle heights are computed by f (xLB +
x), f (xLB + 2x), . . . , f (xU B ) as depicted in Figure 8.2 (ii).
A midpoint method is where the rectangle heights are computed by f (xLB +
1
x), f (xLB + 32 x), . . . , f (xU B 12 x) as depicted in Figure 8.2 (iii).
2
Page 158
Chapter 9
Data containers
So far, we have only dealt with names that contain a single value i.e. scalar names. But
how do we represent a quantity that contains more than a single value i.e. a collection
of values / information. We could use scalar names, one for each value, but if we have a
large collection of values this becomes very difficult to work with and very cumbersome.
9.1
In Python there are several data containers in which we can group a collection of values
together under one name. Ill list the data containers (available in Python) below and
discuss each one in a different section of this chapter:
List Object
Tuple Object
Dictionary Object
9.1.1
Lists
Lists are the most commonly used data container in Python for grouping a collection
of values or information together under one name. Ill start off by showing an example of
how you create a list in Python. A list is defined by enclosing the values (or components)
in square brackets ([]) as follows:
Page 159
160
The values of the list are separated by a comma and then a space (, ), as shown
above. If the comma is left out Python will give you a syntax error. You can access each
value of the list object by using an indexing integer, enclosed by square brackets ([]), after
the list name:
In [#]: a[0]
Out[#]: 1
In [#]: a[1]
Out[#]: 12
In [#]: a[2]
Out[#]: 32
In [#]:
Take Note:
All data containers start with a base index of zero (0) !!!
So the very first value / component in a data container is accessed with index = 0
(See lines 3 4 above).
It is easier just to remember to start counting from zero (0) and not one (1).
Take Note:
All data containers are indexed using square brackets after the object name.
The values / components of a list object can also be changed using an indexing integer:
In [#]: a
Page 160
161
In [#]: a
Out[#]: [100, 12, 297, 87, 5]
In [#]:
Take Note:
List values can only be changed one at a time (i.e. each individual component at a
time). See Section 9.1.2.5 on how multiple values can be changed using a for loop
statement.
It is worth noting that you have already encountered lists before with the range function (Section 4.1). Recall that the range function is used to create a list of integers:
In [#]: a = range(5)
In [#]: a
Out[#]: [0, 1, 2, 3, 4]
In [#]:
9.1.2
Memory model
The memory model for a list object is shown in figure 9.1. When a list object is created
(either using the range statement or manually a = [0, 1, 2]), the individual components are created in memory first and each index / element of the list object is then bound
(points) to each of the objects. Lets try explain each statement in figure 9.1 in more
detail:
1. Line 1: The integer objects (0, 1 and 2) are created in memory first. Each index /
element of the list object is then bound (points) to each of these integer objects.
Finally the name a is bound to the list object.
2. Line 2: The name b is bound to the first integer object 0. It is important to note
that the name b is not bound to the zero index of the list object but is bound to
the integer object referenced by the zero index of the list object.
Page 161
162
3. Line 3: The new integer object 5 is first created in memory. The zero index of the
list object is then bound (points) to this new integer object. The name b is still
bound to the 0 integer object and the first and second indexes of the list object is
still bound to the 1 and 2 integer objects respectively.
9.1.2.1
Separate lists can be joined by using the plus symbol (+) between two or more lists:
In [#]: a = [1, 2, 3, 4, 5]
In [#]: a + b
Out[#]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In [#]: a
Out[#]: [1, 2, 3, 4, 5]
In [#]: b
Page 162
163
In [#]: a += b
In [#]: a
Out[#]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In [#]:
9.1.2.2
Indexing a list
I showed above that you can access each values of a list by using an indexing integer,
enclosed by square brackets ([]), after the name. However you can also access each values
in reverse order by using a negative indexing integer. The last component in a list is
accessed using an index of -1, the second last component with -2, the third last component
with -3, and so forth:
In [#]: a = [1, 12, 32, 87, 5]
In [#]: a[4]
Out[#]: 5
In [#]: a[-1]
Out[#]: 5
In [#]: a[3]
Out[#]: 87
In [#]: a[-2]
Out[#]: 87
In [#]:
Take Note:
You cannot access, or change the value of, a component / entry that does not exist
!!!
The following examples will thus give error messages in Python:
164
9.1.2.3
Data containers, like strings, also have certain functions associated with them. Lets
consider the example where we want to add a single value to the end of a list.
We could use the plus operator (+) as follows:
In [#]: a = [1, 2, 3, 4, 5]
In [#]: a += [6]
In [#]: a += [7]
In [#]: a
Out[#]: [1, 2, 3, 4, 5, 6, 7]
In [#]:
In [#]: a = [1, 2, 3, 4, 5]
In [#]: a.append(6)
In [#]: a.append(7)
In [#]: a
Out[#]: [1, 2, 3, 4, 5, 6, 7]
In [#]:
With this example you can see that the code is much easier to understand and less
prone to us making mistakes. Similarly to modules, the dot (.) tells Python that the
Page 164
165
In [#]: a = [1, 2, 3, 4, 5]
In [#]: a
Out[#]: [1, 2, 100, 3, 4, 5]
In [#]: a
Out[#]: [800, 1, 2, 100, 3, 4, 5]
In [#]:
You use the insert function by first specifying the index (where you want to insert the
value) and then the value (what you what to insert). The rest of the entries / components
are shifted to the right.
Take Note:
All data containers start with a base index of zero (0) !!!
Thus in the example above a.insert(2, 100) (line 3) will insert the value 100 into
the third position of the list.
It is easier just to remember to start counting from zero (0) and not one (1).
In the following example I will show you how to create an empty list object and, using
a for loop statement, we will append values to it. Say, for example, we had a function
that we evaluate a few times and we want to store the results for each evaluation in a list
object:
Page 165
166
func = lambda x: (x ** 2) / 16
x_val = 1
results = []
for i in range(5):
x_val += 3**i
func_val = func(float(x_val))
results.append(func_val)
I create an empty list object (line 4) by only specifying the square brackets ([]). In
line 7 I evaluate the function value, for x val, and temporarily store that in the variable
func val. In line 8 I then append this function value to my list of results results. The
output of this example is the following:
f(x) =
Take Note:
The float(x val) in the above example (line 7) is used to convert x val from an
integer to a floating point number (See section 3.1.4).
Alternatively line 1 could have been changed to the following to ensure that a
floating point number is returned from the lambda function:
More Info:
Type help(list) in the IPython Console to see a list of available functions associated
to a list container (scroll past any function with in front or behind the name,
these functions are outside the scope of this course).
Alternatively you can type list. (note the dot) and hit the tab key, IPython
will then print a list of functions associated to the list container to the screen.
Page 166
9.1.2.4
167
We can loop through a list in the same way we did with the range function (Section
4.2):
Entry
Entry
Entry
Entry
Entry
0
1
2
3
4
in
in
in
in
in
my_list
my_list
my_list
my_list
my_list
is
is
is
is
is
10
50
54
12
132
You can see from this example that the name val is bound to each consecutive integer
object (one after the other) of my list for each loop.
Take Note:
It is important to understand the val (in the example above) is bound to each
consecutive object of my list for each loop and it is NOT an indexing integer. In
this example cnt has been added to act as the indexing integer.
9.1.2.5
The disadvantage of using lists is that you cannot do computations on all values / components at once. You have to use a for loop statement and do the computations on each
component (one at a time). Lets consider the example where we want to multiply each
component in our list by 2. We need to multiple each value, in our list, by 2 using a for
loop statement:
Page 167
168
Take Note:
The len() function returns the length (number of entries) in any data container.
Note that len() starts counting from 1:
In [#]: len(my_list)
Out[#]: 5
In [#]:
This is so that the len() function can easily be used with the range() statement
to create a list of indexes for accessing the components of any data container (as done
is the example above):
In [#]: len(my_list)
Out[#]: 5
In [#]: range(5)
Out [#]: [0, 1, 2, 3, 4]
In [#]:
Take Note:
If we tried to multiply the entire list by 2 we would get the following:
In [#]: my_list *= 2
Page 168
169
In [#]: my_list
Out[#]: [10, 50, 54, 12, 132, 10, 50, 54, 12, 132]
In [#]:
Not exactly what we want. The multiplication sign (*) causes the components in
the list to be repeated.
9.1.2.6
Consider the example shown earlier, where we want to multiple each component in our
list by 2:
In this example above we use the for loop statement (along with the range() and
len() statements) to generate the index number used to access each component in my list
and multiple that component by 2.
We could also use the for loop statement to simple loop through each component in
our list, as follows:
In this example above the name val (in the for loop statement) is bound to each
consecutive object of my list for each loop. I thus need to create an index name ind
(line 2) that gets updated for each loop (line 5). ind is the indexing integer used to access
the individual components of my list and val is used to calculate the new value for that
component (line 4).
Page 169
170
Python has a function (enumerate()) that incorporates the above functionality into
one line in the for loop statement:
my_list = [10, 50, 54, 12, 132]
for index, val in enumerate(my_list):
my_list[index] = val * 2
print my_list
In this above example the statement enumerate(my list) returns both the index
number and the consecutive object of my list in the for loop statement.
Take Note:
Note that two names are specified in the for loop statement in the example above.
one name (index) for the index number of my list
and another name for the consecutive object of my list
If only one name is given then both the index number and the consecutive object
of my list will be stored in a tuple and be bound by that name:
(0,
(1,
(2,
(3,
(4,
10)
50)
54)
12)
132)
More Info:
List objects can contain any type of object (integer, float, string, boolean) and these
types of objects can also be jumbled up inside one list, making Python extremely
flexible:
Page 170
171
Take Note:
It is important to note that because a data container can contain different object
types, the word value in this chapter not be mistaken for only an integer or floating
point number. The word value can refer to either an integer value, float value, string
value or boolean value.
9.1.3
Tuples
A tuple object is almost exactly the same as a list object and it is defined by enclosing
the values (or components) in round brackets () as follows:
The following two differences separate a tuple object from a list object:
1. The values / components cannot be changed after the tuple is created:
This can be useful for when you have a collection of values that wont change or you
dont want them to accidentally be changed somewhere in you program.
2. A tuple does not have the append and insert functions associated to it (it does
however have the same behaviour as a list when using the plus sign (+) operator):
a.append(10)
172
a += (132, 23)
# this is allowed
Everything else (behaviour, indexing, looping through, etc.) is the same as a list
object.
9.1.3.1
The one disadvantage of using a tuple object is when trying to create a tuple object with
only one component. Consider the following example:
In [#]: a = (10)
In [#]: a
Out[#]: 10
In [#]: a = (10,)
In [#]: a
Out[#]: (10,)
In [#]:
You would expect to simple enclosing the single value in round brackets as shown in
line 1 above. However this is seen by Python as a separation of mathematical operations
and not the creation of a tuple object. To create a tuple object with just one component
you have to add a comma (,) after the single value entry, as shown in line 6.
More Info:
Type help(tuple) in the IPython Console to see a list of available functions associated to a tuple container (scroll past any function with in front or behind the
name, these functions are outside the scope of this course).
Alternatively you can type tuple. (note the dot) and hit the tab key, IPython
will then print a list of functions associated to the tuple object to the screen.
Page 172
9.1.3.2
173
Lets say that we want to evaluate a second order polynomial function (f (x) = ax2 +bx+c).
The simplest solution would be to add the coefficients into the function when creating
it (lines 35 below), however for the sake of explanation lets store these coefficients in a
tuple object.
Because the coefficients for the polynomial function will remain unchanged in our
program it makes sense to store them in a tuple object (line 1) and then use the tuple
object in the equation for the function (line 35). This is of course a very simple example,
but what if we also wanted to evaluate the function derivative and add a few more degrees
to the polynomial function:
*
*
*
*
*
(x
(x
(x
(x
(x
**
**
**
**
**
4)
3)
2)
1)
0)
+
+
+
+
)
(x
(x
(x
(x
**
**
**
**
3) +
2) +
1) +
0)
)
174
You can now see from this example how a tuple object starts becoming useful, if we
later wanted to change the values of the coefficients and re-run the program we would
only have to change one line of code in place of several lines (and possibly miss a few).
More Info:
As with list objects, tuple objects can contain any type of object (integer, float,
string, boolean) and these types of objects can also be jumbled up inside one tuple
object:
my_tuple = (10, "logan", 54.123123, True)
9.1.4
Dictionaries
A dictionary is defined by enclosing the values (or components) in curly brackets. Each
component is first given a keyword, followed by a double colon(:) and then followed by
the component:
a = {"score": 12,
"age": 45,
"year": 2013}
Page 174
175
In [#]: a
Out[#]: {age: 24, score: 12, year: 2013}
In [#]: a["age"]
Out[#]: 24
In [#]: a["year"]
Out[#]: 2013
In [#]:
A dictionary object is often used for storing a collection of more real or physical
information about a certain aspect or category (e.g. the make, model and year of a car;
or the name, surname, age and email address of a student; etc.) The dictionary object
allows for easy grouping of this information and giving each component an easy to associate keyword.
Take Note:
The values or components of a dictionary object can only be accessed by using the
keyword, as shown above, the following will give an error in Python:
In [#]: a[0]
# KeyError: 0
In [#]: a["name"]
# KeyError: name
In [#]: a["ScoRe"]
# KeyError: ScoRe
In [#]:
.....:
176
"year": 2009}
In [#]: a
Out[#]: {make: Toyota, model: Corolla, year: 2009}
In [#]: a
Out[#]: {make: Toyota, model: Corolla, year: 2006}
In [#]: a["year"] -= 5
In [#]: a
Out[#]: {make: Toyota, model: Corolla, year: 2001}
9.1.4.1
Adding to a dictionary
You can add new keyword: value entries to a dictionary object by simply indexing it
with a new keyword and a new value:
In [#]: a
Out[#]: {make: Toyota, model: Corolla, year: 2009}
In [#]: a
Out[#]:
{colour: red,
make: Toyota,
model: Corolla,
year: 2001}
In [#]: a
Out[#]:
{code: 133L,
Page 176
177
colour: red,
make: Toyota,
model: Corolla,
year: 2001}
In [#]:
Take Note:
Dictionary objects do not support the plus operator (+) and thus cannot be joined
in this fashion, as tuple or list objects could !!!
9.1.4.2
Dictionary objects behave very differently to tuple and list objects when it come to looping
through them. Looping through a dictionary will thus be outside the scope of this course,
however please feel free to Google this topic if you are interested in knowing how it is
done. There is a lot of information and examples on the web covering this.
9.1.4.3
The only disadvantage I am going to mention is that of misspelling the keyword when
trying to change the value of a certain entry.
In [#]: a
Out[#]: {make: Toyota, model: Corolla, year: 2009}
In [#]: a
Out[#]:
{Year: 2006,
make: Toyota,
model: Corolla,
year: 2009}
Page 177
178
In [#]:
As you can see from this example there are now 2 year keywords, one with a lower
case y and one with an upper case Y, take care in making sure you type the keyword
exactly the same each time.
9.1.4.4
By now you should be able to figure out the logic of the following program:
You should now notice that the string formatting operator (%) requires a tuple object for the information being inserted into the string template, when multiple pieces of
information are being inserted.
More Info:
Type help(dict) in the IPython Console to see a list of available functions associated
to a dictionary object (scroll past any function with in front or behind the name,
these functions are outside the scope of this course).
Alternatively you can type dict. (note the dot) and hit the tab key, IPython
will then print a list of functions associated to the dictionary object to the screen.
Page 178
179
More Info:
As with list objects, dictionary objects can contain any type of object (integer, float,
string, boolean) and these types of objects can also be jumbled up inside one dictionary object. This also holds for the keywords (the following is thus allowed):
my_dict = {1: True
2: "yes"
"a": 12.445}
9.1.5
Summary
So far I have shown how flexible Python can be with three different data containers, each
of which, not restricted to only one object type. This allows you, as the programmer,
to choose how you want to group information together in order to make your program
easier to use and easier to understand. There is one additional data container available to
Python, which will be covered extensively in Chaper 10. This additional data container
is a numpy array which is generally used for vector and matrix algebra.
In the following sections I will go through a few examples showing the use of these data
containers. It should be even clearer now, with the vast flexibility of Python, that there
is no one solution for a program and as the programmer you will need to fully understand
the problem being solved and decide how you want to solve it in Python.
More Info:
As you saw in section 3.5 and related examples in the following chapters; object types
can be converted from one type to another. Data containers can also be converted
from one type to another using the following statements:
1. tuple() - Convert to a tuple container
2. list() - Convert to a list container
3. dict() - Convert to a dictionary container
More information on each of these will be given as and when they are used in the
examples in these note.
Page 179
9.2
180
Ill give a few more examples of the use of data containers. Ill first use lists to compute
a Taylor series approximation. Then Ill illustrate how lists can be used to perform
numerical integration.
9.2.1
I discussed Taylor series analyses in Chapter 4 (Section 4.6) and Chapter 5 (Section 5.4) of
the lecture notes. I used both for loop statements and while loop statements to perform
the required computations. Revisit these sections before you continue.
You should have noticed that when we computed a Taylor series approximation at the
start of the year, we only used scalar variables. We kept on adding new terms in the series
until the series converged.
Lets compute a Taylor series approximation again, but now making use of two list.
In the first list, called terms, I will save the Taylor series terms i.e. the first term in the
series is saved in the first component, the second term in the series is saved in the second
component, and so on. In the second list, called taylor approx, I will save the current
value of the Taylor series approximation. The first component will contain the first term,
the second component will contain the sum of the first two terms, and so on. We proceed
like this and save the n term Taylor series approximation in the n-th component of the list
taylor approx (where n = 0, 1, 2, . . . ). When the complete lists are displayed, we should
clearly see how the components of the list terms become smaller, while the components
of the list taylor approx should converge to the analytical result.
As an example, lets compute the Taylor series approximation to the exponential function, which is given by
x
e =1+x+
1 2
x
2
1 3
x
6
+ =
X
xk
k=0
k!
(9.1)
Heres a program that uses a while loop statement to compute the Taylor series
approximation to ex within a specified accuracy.
181
terms = []
taylor_approx = [0.0]
# Set term counter to 0
cnt = 0
# Set initial Error > acc to make sure while loop is entered
error = 1
while error > acc:
# Compute next term in series
terms.append(x_val**cnt / factorial(cnt))
# Next component in list equal to previous component
# plus new term
taylor_approx.append(taylor_approx[cnt] + terms[cnt])
# Error = difference between consecutive components
error = abs(taylor_approx[cnt+1] - taylor_approx[cnt])
# Increment the counter
cnt += 1
Here follows the output of the program listed above. You can clearly see how the
Taylor series converges to e = 2.718281828459 . . . as the number of terms in the series
increases.
0.00000276
0.00000028
0.00000003
182
2.71828153
2.71828180
2.71828183
Modify the above program to compute the Taylor series approximation to . Be careful
not to specify a very small accuracy. Youll see that the Taylor series converges very
slowly, so you need a large number of terms for an accurate approximation.
9.2.2
Lets revisit the numerical integration program developed in Section 8. In that section, the
name integral old is used to have the previous value of the numerical integral available
after the new value is computed. Now I propose that we not only save the most recent
value of the numerical integral, but that we save the numerical integral for every case
analysed. We can save all these values in a single list.
Im going to create two lists. In the first list, called num int, Ill save the number of
intervals. As the number of intervals change, Ill save the new number of intervals in the
next component of the list num int. The second list, called integral, will contain the
numerical integrals associated with the number of intervals in the list num int.
Heres the modified numerical integration program:
lower = 0
upper = pi / 2
acc = float(raw_input("Enter required accuracy: "))
183
while method != 5:
# Define initial Error greater as acc,
# to enter while loop
error = 1
#Since the number of intervals change during program
# execution, reset N to the initial number of intervals
num_int = [0, 10]
num = num_int[-1]
# Set Int to zero to have a value the first time
# the while loop is executed
integral = [0.0]
elif method == 2:
# right-point method
for i in range(num):
x = lower + (i + 1) * delta_x
integral[-1] += delta_x * func(x)
elif method == 3:
# mid-point method
for i in range(num):
x = lower + i * delta_x + 0.5 * delta_x
integral[-1] += delta_x * func(x)
elif method == 4:
# trapezium method
for i in range(num):
x_L = lower + i * delta_x
x_R = x_L + delta_x
integral[-1] += ( 0.5 * delta_x *
Page 183
184
(func(x_L) + func(x_R)) )
# Display answer
print "Intergrating %s between %.3f and %.3f:" % (func_str,
lower,
upper)
print "Intervals \t Integral"
for i in range(len(integral)):
print "%d \t %.8f" % (num_int[i], integral[i])
Very few changes were made compared to the previous version. Since the number of
intervals N is now a list, I need to specify which component of N I need e.g. program
lines 25, 68 and 69. Similarly the numerical integrals are now stored in the list integral,
so I need to refer to a specific component of integral, e.g. program lines 45, 50 and 61.
Ive also decided to change the output. Now I display the first k components of the
lists num int and integral. Program lines 7577 displays these lists. I make use of a for
loop statement to print each component of the lists num int and integral to the screen.
More Info:
The tab (\t) character is used to add white space in its place when printed to the
screen.
Here follows the output from the program. I only executed option 2 (right-point) and
option 4 (trapezium) before I quit the program (option 5). Note the tabular output, where
Page 184
185
we now clearly see the progress towards the analytical result as the number of intervals
is increased.
9.3
186
Sorting algorithms
Lets further develop our programming skills by trying to write a program that can sort
the components of a list in ascending order (small to large). In order to develop a sorting
algorithm, you have to think of a simple operation that a computer can perform that will
enable a list be sorted. If required, this operation can be repeated a large number of times
(which will be the case).
The operation Im referring to is to compare two numbers. If the number pair is in
the correct order (1st value smaller than the 2nd), then move to a different number pair.
If the number pair is not in the correct order, swap the 2 numbers. This simple operation
is sufficient to order a list.
I can think of a few sorting algorithms. Ill start off with a method that is easy to
explain and implement, but not very efficient. Lets compare the first component of the
list with all the other components, one by one (starting at component 2, ending at the
last component), i.e. compare component 1 with 2, then component 1 with 3 and so on.
Whenever the 1st component is larger than the component it is compared to, we swap the
components. If not, we simply move to the next component of the list. When we reach
the end of the list, the smallest number will be in component 1. We start at the top again
but now we compare the 2nd component of the list with all the components from 3 to the
end (Its no use to start at 1 again, it already contains the smallest number). We repeat
this process till we compare the last two numbers. This process is implemented below.
We need two nested for loops: the first loop provides the index of the 1st number in the
number pair, while the second loop provides the index of the 2nd number in the number
pair.
187
# complete
print "list = ", my_list, "\n",
In program line 2 I define the list my list. Ive decided to use a list that is sorted
from large to small. Such a list will really test if my program can sort in ascending order.
Program line 4 gets the number of components of the list. The first for loop statement
is coded in program line 6: do you see that the index of the first number in my number
pair starts at 0 and ends at n-1 (one from the end). The second for loop statement is
coded in program line 8. The 1st number in the number pair is given by index i. I now
have to compare this number with the remaining numbers below position i in the list.
That is why the second index j starts at i+1 (the very next component in the list) and
ends at n, the last entry of the list.
In program line 10 I check if the number pair is in the correct order. If the i-th
component (1st number of the pair) is greater than the j-th component, I swap the pair
of numbers. This is done by first making a copy of the i-th component (program line
12), then assigning the old j-th component to the new i-th component (program line
13). Finally the copy of the old i-th component is assigned to the new j-th component
(program line 14). Program line 17 simply displays the list every time the outer for loop
is complete. This will provide output that will illustrate how the program proceeds to
sort the list.
Here follows the output of the above program:
list
list
list
list
=
=
=
=
[1,
[1,
[1,
[1,
5,
2,
2,
2,
4,
5,
3,
3,
3,
4,
5,
4,
2]
3]
4]
5]
As you can see, the program correctly sorts the descending list into ascending order.
After the for loop is executed once, the smallest number occupies the first list position.
After the for loop is executed again, the first 2 numbers are in their correct positions.
This process repeats until all the numbers are correct after the for loop has been executed
4 times.
Another version of this program is given below to illustrate the use of the continue
statement. The continue statement is used to tell Python to continue with a looping
statement (for or while) and skip any code after the continue statement is encountered.
So in the example below if the j-th component is larger than the i-th component (i.e. in
the correct order) then the continue statement, on line 11, is executed and lines 1215
are skipped. The for loop statement (line 8) then continues by incrementing j.
Page 187
188
9.3.1
Lets also look at an alternative sorting algorithm. It will help to illustrate that many
solutions exist to the same problem. The bubble sorting algorithm also checks if a number
pair is in the correct order. The only difference with the algorithm above is that the bubble
sort algorithm always compares a component with the very next component in the list,
i.e. compare component 1 with 2, then component 2 with 3, then component 3 with 4 and
so on. After the last number pair has been compared, the largest number will occupy the
last position in the list. Now we have to start at the top again and compare component
1 with 2, 2 with 3 and so on. This time we stop 1 pair from the bottom, because we
already know the largest number is in the correct position. This process is repeated till
only components 1 and 2 are compared. Heres my implementation of the bubble sort
algorithm:
189
The output of the bubble sort algorithm, using the same 5 component list as before,
follows:
list
list
list
list
=
=
=
=
[4,
[3,
[2,
[1,
3,
2,
1,
2,
2,
1,
3,
3,
1,
4,
4,
4,
5]
5]
5]
5]
As you can see the largest entry occupies the last list position after the outer for loop
is executed once. The largest two entries are in their correct positions after the outer for
loop is executed again. This process repeats until all entries are correct after outer the
for loop is executed four times.
Ill now run the bubble sort program again, but this time I replace program lines 12
with
which will create a random 8 component list my list. I include the output of the
bubble sort algorithm for one of the cases I ran after the above change:
Page 189
list
list
list
list
list
list
list
=
=
=
=
=
=
=
[1,
[1,
[1,
[1,
[1,
[1,
[1,
5,
5,
5,
3,
1,
1,
1,
5,
5,
3,
1,
3,
3,
3,
5,
3,
1,
3,
3,
3,
3,
3,
1,
3,
5,
5,
5,
5,
190
1,
3,
5,
5,
5,
5,
5,
3,
5,
5,
5,
5,
5,
5,
8]
8]
8]
8]
8]
8]
8]
The algorithm clearly works. For the 8 component list above, the outer for loop is
executed 7 times. However, the output for this particular example shows that the list is
in the correct order after only 4 outer for loop executions. So how can we improve the
bubble sort algorithm to stop once the list is in the correct order?
The answer is quite simple. We have to keep track of number pair swapping. If we
find that we start at the top of the list and scan all the way to the bottom and never find
a number pair in the wrong order, we know the list is sorted and the algorithm can stop.
This type of logic requires a while loop.
191
Ill only explain the program lines that are new. The index ind is set equal to zero
in program line 12 and is incremented by one inside the while loop in program line 34.
This replaces the for loop that was repeated a predetermined number of times (n-1). I
also use a new name called is sorted to indicate whether the list is sorted or not. I set
is sorted equal to True in program line 16, before the while loop starts. The while
loop condition in program line 19 states that the sorting loop is repeated as long as the
name is sorted equals False and the index ind is less than n-1 (We know that the list
will be sorted once i = n-1). As soon as the loop starts, I set the name is sorted equal
to True in program line 21 i.e. I assume that the list is already in the correct order.
Should I find a pair of numbers that is not in the correct order, I change the value of the
name is sorted to False (program line 32). The rest of the program is unchanged.
Here follows an example of the modified bubble sort algorithm. I chose a case in which
the original list my list was almost sorted already, so the while loop is repeated only 5
times for a list of length 8. As you can see the program now terminates as soon as the
list is scanned from top to bottom without a number pair swapping. In fact, the while
loop is repeated only once for any list that is already sorted in ascending order.
list
list
list
list
list
list
=
=
=
=
=
=
[8,
[4,
[4,
[4,
[4,
[4,
4,
4,
4,
4,
4,
4,
4,
8,
7,
6,
4,
4,
8,
7,
6,
4,
6,
6,
7,
6,
4,
7,
7,
7,
6,
4,
8,
8,
8,
8,
4,
8,
8,
8,
8,
8,
8]
8]
8]
8]
8]
8]
Page 191
9.3.2
192
So far I have shown how flexible Python is with three different data containers, each of
which, not restricted to only one object type. Python can be even more flexible by being
able to have data containers inside data containers. The most common of which, for
example, is a list object of list objects:
From the example above you can see I have created a list of three lists. The lists inside
my list are separated with a comma (,). The individual components of the list can be
indexed in the same manner as in section 9.1.1. my list[0] returns a reference to the first
list object ([1, 2, 3]) and thus my list[0][0] returns a reference to the first integer
object (1) in the first list object ([1, 2, 3]). Consider the following examples:
In [#]: a
Out[#]: [[0, 1, 2], [1, 2, 3]]
In [#]: a[0]
Out[#]: [0, 1, 2]
In [#]: a[0][0]
Out[#]: 0
In [#]: a
Out[#]: [[0, 1, 102], [1, 2, 3]]
In [#]:
Here is where understanding the memory model becomes quite important. Every thing
in Python (at least from a memory point of view) is an object and names are merely bound
(or point) to that object. The same is true for lists, and the indexes / elements of a list
Page 192
193
object are merely bound (or point) to other objects. So in the example above, the element
a[0] is bound to the list object created in memory by [1, 2, 3] and the element a[0][0]
is bound to the integer object 0. Consider the following examples:
In [#]: a
Out[#]: [[0, 1, 2], [1, 2, 3]]
In [#]: b = a[0]
In [#]: b
Out[#]: [0, 1, 2]
In [#]: a
Out[#]: [[0, 1, 2], [1, 2, 3]]
In [#]: b[0] = 5
In [#]: b
Out[#]: [5, 1, 2]
In [#]: a
Out[#]: [[5, 1, 2], [1, 2, 3]]
In [#]:
In the example above it is important to notice that when changing b[0] = 5 (line 15)
that the binding of the first element of the list object [0, 1, 2] is changed. b is the same
list object as a[0] and b[0] is the same integer object as a[0][0]; so changing b[0] to
5 only changes the binding of the first element of one list object ([0, 1, 2]), but both
a[0] and b are bound to the same list object. See if you can understand the effect of line
15, in the example above, on the memory model shown in figure 9.2
Lets consider the case where we want to store a list of student information. Here is an
example of a list of dictionary objects, where each dictionary object stores one students
information. You should by now be able to figure out the logic of this example.
Page 193
194
all_students = []
name_template = "\nEnter student %ds name [q to quit]: "
cnt = 1
while True:
name = raw_input(name_template % cnt)
if name == q:
break
mark = float(raw_input("Enter student %ds mark: " % cnt))
student = {name: name,
mark: mark}
all_students.append(student)
cnt += 1
195
student[mark])
9.4
Exercises
[4, 2, 5, 6, 1]
6. Write a program that determines the infinity or maximum norm ||x|| of a list x
(i.e. the entry of a list with the largest absolute value e.g.)
x = [1 5 2 7 13 14 8]
(9.2)
the index with the largest absolute value is index number 5 where the entry has an
absolute value of | 14| = 14. Therefore
||x|| = 14
(9.3)
7. Write a program that simulates a user specified amount of single player dice battle
games. The program must then return the score for each game in a list.
The dice battle game works as follows:
First a single dice is thrown. If the number thrown is
Page 195
196
Page 196
Chapter 10
Vectors and matrices
As mentioned in chapter 9 (section 9.1.5) there is one additional data container available
in Python: the numpy array object. numpy arrays are used for either vector or matrix
algebra (i.e. dot product or two vectors or matrices, inverse of a matrix, transpose of
a matrix, etc). numpy itself is actually a module containing the numpy array object and
a multitude of functions for vector and matrix algebra. We will thus be importing the
required data container and functions, as we need them, from the numpy module.
Ill start off by showing as example of how we create a row vector in Python. A row
vector can be seen as almost being the same as a list and in fact we will use lists to create
an array (an array is a generic programming term use for both vectors and matrices).
In line 1 we import the array object from numpy and use it to create a row vector
in line 4. Now lets move on to creating a column vector. A column vector is created by
using a list of lists, shown in the following example:
vec = array([[1],
Page 197
[2],
[3]])
198
print vec
This example defines a column vector with the same three components. As you can
see each value is stored in a list and that list is in turn stored in another list. Lines 46
can also be re-written as follows to create the same column vector:
Row and column vectors are indexed (values are accessed) in exactly the same way
you would with a list. A matrix is also defined by using a list of lists e.g.
This example creates a 3 3 symmetric matrix mat. Matrices are indexed using two
indexing integers enclosed by square brackets ([]), one index for the row and one index
for the column, separated by a comma and a space:
In [#]: mat
Out[#]:
array([[1, 2, 3],
[2, 3, 4],
[3, 4, 5]])
In [#]: mat[0, 2]
Out[#]: 3
Page 198
199
In [#]: mat[2, 0]
Out[#]: 3
In [#]: mat[1, 1]
Out[#]: 3
In [#]:
As you can see the first index corresponds to the row and the second index corresponds
to the column (going from the top down and left to right). Matrices can also be indexed
by enclosing each indexing integer in its own square brackets ([]) (as you would in the
case of a list of lists) as follows:
In [#]: mat
Out[#]:
array([[1, 2, 3],
[2, 3, 4],
[3, 4, 5]])
In [#]: mat[0][2]
Out[#]: 3
In [#]: mat[2][0]
Out[#]: 3
In [#]: mat[1][1]
Out[#]: 3
In [#]:
Reverse indexing (negative indexing integers) can also be used for both vectors and
matrices (e.g. mat[-1][-1], mat[-1][0], etc).
More Info:
Page 199
200
Import the numpy module into the IPython Console and type numpy? for available
documentation. The following is a snippet taken from this documentation.
.
.
.
Available subpackages
--------------------doc
Topical documentation on broadcasting, indexing, etc.
lib
Basic functions used by several sub-packages.
random
Core Random Tools
linalg
Core Linear Algebra Tools
fft
Core FFT routines
polynomial
Polynomial tools
testing
Numpy testing tools
f2py
Fortran to Python Interface Generator.
distutils
Enhancements to distutils with support for
Fortran compilers support and more.
.
.
.
Many of the functions available in the numpy module are sorted into sub-packages
(or sub-modules) as shown by the documentation snippet above. Remember that
you can get documentation or information on any sub-packages, functions, etc. by
typing help(object) or object?. For example the following documentation snippet
is taken for typing numpy.linalg?
.
.
.
Core Linear Algebra Tools
------------------------Page 200
201
norm
inv
solve
det
lstsq
pinv
- matrix_power
.
.
.
More Info:
For more information regarding the numpy module and available functions please see
the following sources:
http://docs.scipy.org/doc/numpy/user/
http://www.scipy.org/Tentative NumPy Tutorial
http://docs.scipy.org/doc/numpy/reference/
http://mathesaurus.sourceforge.net/numeric-numpy.html
10.1
The one big advantage of numpy arrays is that you can do computations on all components
of a vector or matrix at once, without having to use a for loop statement:
In [#]: mat * 2
Out[#]:
array([[ 2, 4, 6],
[ 4, 6, 8],
[ 6, 8, 10]])
In [#]: mat
Page 201
202
Out[#]:
array([[1, 2, 3],
[2, 3, 4],
[3, 4, 5]])
In [#]: mat *= 10
In [#]: mat
Out[#]:
array([[10, 20, 30],
[20, 30, 40],
[30, 40, 50]])
In [#]:
Vectors and matrices of the same size (shape) can be added together or subtracted
from one another, on a component by component basis, simply by using the plus or minus
operators (+ -):
In [#]: mat1
Out[#]:
array([[11, 22, 33],
[22, 33, 44],
[33, 44, 55]])
In [#]:
Take Note:
numpy arrays should only be used for integer values or floating point values !!
Page 202
203
numpy arrays can only contain one object type (i.e. all components are either
integers or all components are floating point numbers)
Take Note:
numpy arrays also behave differently for integer values and floating point values, what
I mean by this is that if you specify all the components of a vector or matrix as
integers then the array will be an integer type array. At least one component must
be a floating point value for the array to be a float type array. Even I got caught out
by this will compiling these notes so please be aware of the following examples:
In [#]: vec1.dtype
Out[#]: dtype(int64)
In [#]: vec3
Out[#]: array([ 2.,
2.,
2.])
In [#]: vec3.dtype
Out[#]: dtype(float64)
2.5,
3. ])
In [#]:
The .dtype function, associated to the array container, is used to find out the
data type of the array. Alternatively you can force the type of the array to be either
a float type or integer type when you create the array:
Page 203
204
In [#]: vec1
Out[#]: array([ 4.,
5.,
6.])
In [#]: vec2
Out[#]: array([2, 3, 9])
In [#]:
You do this by adding a comma and a space after the list, and then the dtype=<type>
keyword as shown above. In my opinion it is good practice to create all vectors or
matrices with the optional dtype=<type> keyword added.
More Info:
Remember you can use Pythons future division to avoid this integer division behaviour:
from future import division
10.2
As shown in Section 9.1.2.4 (for a list) we can make use of a for loop statement that starts
at the first entry and displays it to the screen, and then goes to the second entry and
displays it, etc. (until we have displayed all the entries). The following example illustrates
the use of a for loop statement to display the entries of a vector one at a time:
205
We can also use the other looping techniques we learnt for a list (Section 9.1.2.4) for
a vector:
Each of the looping methods in the example above gives the same output:
Entry
Entry
Entry
Entry
Entry
Entry
0
1
2
3
4
5
of
of
of
of
of
of
the
the
the
the
the
the
vector
vector
vector
vector
vector
vector
is
is
is
is
is
is
10
9
8
7
6
5
The methods (shown above) for displaying the components of a vector will also work
for displaying the components of a list or tuple.
10.3
Now that we have seen how to display the components of a vector to the screen let us
display the components of a matrix to the screen. As we have seen we required one for
loop statement to display the entries of a vector to the screen.
Page 205
206
We can basically consider each row (or column) of a matrix as a vector. As we know
we need a for loop statement to display the entries of the vector to the screen. We
also require an additional for loop statement to move between the rows (or columns) of
matrix. The for loop statements have to be inside each other (nested) because for every
row (or column) we want to display all the entries of the respective row (or column) to
the screen.
Here, is an example program that displays each component of a matrix one at a
time:
207
Each of the looping methods in the example above gives the same output:
Row
Row
Row
Row
Row
Row
Row
Row
Row
Row
Row
Row
Row
Row
Row
Row
Row
Row
0
0
0
0
0
0
1
1
1
1
1
1
2
2
2
2
2
2
and
and
and
and
and
and
and
and
and
and
and
and
and
and
and
and
and
and
Col
Col
Col
Col
Col
Col
Col
Col
Col
Col
Col
Col
Col
Col
Col
Col
Col
Col
0
1
2
3
4
5
0
1
2
3
4
5
0
1
2
3
4
5
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
the
the
the
the
the
the
the
the
the
the
the
the
the
the
the
the
the
the
matrix
matrix
matrix
matrix
matrix
matrix
matrix
matrix
matrix
matrix
matrix
matrix
matrix
matrix
matrix
matrix
matrix
matrix
is
is
is
is
is
is
is
is
is
is
is
is
is
is
is
is
is
is
10
9
8
7
6
5
20
30
40
50
60
70
11
9
13
7
15
5
The len command can also be used for vectors to determine how many entries they
have. For matrices if we need to know the number of rows and columns and in the matrix
we can use the .shape (note the dot) command. The .shape command is associated to
the numpy array data container and it returns a tuple with two values. The first value is
the number of rows and the second value the number of columns of the matrix e.g.
208
Here, num rows is equal to 3 and num cols equal to 6, since my mat has 3 rows and 6
columns. If only specify a single name for the output of the .shape command as in the
following example
mat_shape = my_mat.shape
print mat_shape
then mat shape is a tuple with two entries. The first entry of mat shape is the number
of rows of the specified matrix my mat and the second entry of mat shape is the number
of columns of my mat.
The following example uses the .shape command to determine the dimensions i.e.
number of rows and columns of my mat:
10.4
A student asked me whether or not a matrix can be read in using the raw input statement.
As weve seen before, the raw input statement is very convenient to read a single value
Page 208
209
from the IPython Window. But how can we read all the entries of a matrix using the
raw input statement? I can think of a simple program making use of two for loops
statement:
print user_mat
More Info:
The numpy.zeros() function is used to create a vector or matrix containing only (0)
values. Import the numpy module into the IPython Console and type numpy.zeros?
for available documentation.
10.5
Other than referring to a single matrix entry at a time or doing simple computations
on a matrix, many other matrix manipulations and computations are possible in Python
(through the numpy module). To illustrate the use of vectors, lets develop a program that
computes the norm (magnitude or length) of a vector a (name my vec), defined as:
v
u n
uX
kak = t
a2i ,
(10.1)
i=1
Page 209
210
where n is the number of components. As a first example, lets assume the vector has
three components and lets compute the norm manually in a program:
Norm of vector =
3.74165738677
The norm of a vector can also be calculated using the built-in norm function in the
numpy.linalg module as shown in the following example:
Page 210
211
More Info:
Import the numpy module into the IPython Console and type numpy.linalg.norm?
for available documentation.
10.6
n
X
xi yi
(10.2)
i=0
where n is the dimension (or length) of vector x and y. The dot product is equivalent
to multiplying a row vector (1 n) with a column vector (n 1) which gives a 1 1
matrix or scalar value.
The following program computes the dot product between two vectors. The program
first checks whether the vectors have the same length before it starts computing the dot
product.
212
[ 1.]
The dot product of two vectors can also be calculated using the built-in dot function
in the numpy module as shown in the following example:
More Info:
Import the numpy module into the IPython Console and type numpy.dot? for available documentation.
Page 212
10.7
213
Lets develop a program that can multiply a matrix with a vector. Recall from your linear
algebra course that matrix-vector multiplication is defined as
ci =
n
X
Aij aj
for i = 0, 2, . . . , m
(10.3)
j=0
214
where the function .shape assigns the number of rows and columns of the matrix
matrix to the names num rows and num cols. Now we are ready to write a program that
multiplies a matrix with a vector:
[
[
[
[
[
[
[
[
[
215
1.59491837]
2.29118772]
1.88103115]
2.84548976]
2.19079554]
1.34920096]
1.25477803]
3.39309945]
2.80552001]]
Program lines 68 creates a random matrix mat and a random vector vec, just to test
our program. Program lines 1319 contain the actual matrix-vector multiplication and
program lines 2324 creates the output to the IPython Console. In the program above it
is not strictly necessary to use the .shape command, since we know that the size of the
matrix is 10 by 8. It is however sensible to use the .shape statement to prevent changing
the code in lines 1024 should we choose to change the matrix mat.
Here follows another version of the example shown above. You should be familiar with
the programming logic used.
216
The matrix-vector multiplication can also be calculated using the built-in dot function
in the numpy module as shown in the following example:
10.8
Let us try and write a program that would allow is to multiply two vectors with each
other. We have already looked at multiplying a row vector (1 n) with a column vector
(n 1) which results in a (1 1) matrix or scalar value.
1
2 3 1 2 = 2 1 + 3 2 + 1 3 = 11
3
(10.5)
1
2
3
1
4
2 2 3 1 4 = 4 6 2 8
3
6 9 3 12
Here follows a general vector-vector multiplication program.
Page 216
(10.6)
#
#
#
#
#
217
vector1 = array([[1],
[2],
[3]], dtype=int)
vector2 = array([[2, 3, 1, 4]], dtype=int)
218
vector1 = array([[1],
[2],
[3]], dtype=int)
vector2 = array([[2, 3, 1, 4]], dtype=int)
10.9
Gauss elimination
The solution of a system of linear equations remains one of the numerical computations
performed most often in all engineering disciplines. In your linear algebra course, you have
already mastered Gauss elimination. Gauss elimination remains one of the most efficient
direct methods to solve large systems of linear equations. Lets revisit the method.
During the forward reduction step of Gauss elimination, the original system matrix is
reduced to a system that contains zeros below the diagonal. This is achieved by subtractPage 218
2 3
1 1
2 4
1 2
219
4 1
x
10
2 1
x
5
2
=
5 2
x3
13
3 4
x4
10
Replace Row 2 of the system with (Row 2 - 12 Row 1) i.e.
2
3
4 1
x
10
0 0.5 0 0.5 x2 0
=
2
4
5 2
x3
13
1
2
3 4
x4
10
Then replace Row 3 with (Row 3 - 22 Row 1) i.e.
2
3
4 1
x
0 0.5 0 0.5 x2
=
0
1
1 1
x3
1
2
3 4
x4
Finally, we replace Row 4 with
2
3
0 0.5
0
1
0 0.5
10
0
3
10
(Row 4 - 12 Row 1) i.e.
4 1
x1
10
0 0.5 x2
0
=
1 1
x
3
1 3.5
x4
5
(10.7)
(10.8)
(10.9)
(10.10)
We have succeeded in reducing the first column below the diagonal to zeros. Now we
proceed to reduce the second column
below the diagonal to zeros. This is achieved by
1
replacing Row 3 with (Row 3 - 0.5 Row 2) i.e.
2
3
4 1
x1
10
0 0.5 0 0.5 x2 0
=
(10.11)
0
0
1 2
x3
3
0 0.5 1 3.5
x4
5
The
last entry in column 2 is
Row 2) i.e.
2
3
0 0.5
0
0
0
0
0.5
0.5
4 1
x1
0 0.5
x2
1 2
x3
1 4
x4
Page 219
10
0
=
(10.12)
220
We complete the forward reduction step by reducing the column 3 entries below the
diagonal (there is only 1) to zero: Row 4 = (Row 4 - 11 Row 3) i.e.
2
3
4 1
x
10
0 0.5 0 0.5 x2 0
=
(10.13)
0
0
1 2
x3
3
0
0
0 2
x4
2
We are now done with the forward reduction step. All the entries of the matrix below
the diagonal is zero. Now we proceed to solve the unknowns x4 , x3 , x2 and x1 , in that
order. This part of the Gauss elimination process is called back-substitution.
The 4th equation in the above system reads
2x4 = 2
(10.14)
(10.15)
x3 = 3 2x4 = 1
(10.16)
0 0x3 0.5x4
=1
0.5
(10.17)
10.9.1
10 3x2 4x3 x4
=1
2
(10.18)
Now that we have reviewed the Gauss elimination process, lets attempt to program it.
You will find this program quite challenging, and will have to review the steps numerous
times before you finally understand them all.
To perform Gauss elimination, we require a linear system to solve. Ill use the same
linear system as above during the program development.
Page 220
221
First of all, we have to decide what type of computations are required during Gauss
elimination. You should be able to recognise that a predetermined number of operations
are performed, based on the size of the linear system. So well use for loop statements,
since the size of the matrix has to be known.
The next challenge is to decide how many for loop statements are required. This is
not a trivial decision. We must use the numerical example of the previous section to help
us out. Try to identify processes / computations that repeat and attempt to write these
computations as a repeating for loop statement.
10.9.1.1
Gauss elimination required three nested for loop statements during the forward reduction
step. Here is what each loop will do:
1. One index (loop) tracks the diagonal term we are currently using to make all other
terms below it zero. This row is called the pivot row and the entry on the diagonal
is called the pivot element.
2. A second index (loop) will track which row we are currently working on. All the
rows below the pivot row will be changed by adding or subtracting an appropriate
fraction of the pivot row.
3. The last index (loop) is used to perform the required computation for all the columns
of the rows below the pivot row.
Lets implement the forward reduction step of Gauss elimination. This will illustrate
the necessity of the three required loop statements more clearly.
222
a = array([[10],
[5],
[13],
[10]], dtype=float)
# Compute size of matrix
n = A.shape[0]
# Start of 1st loop: pivot row counter
for i in range(n-1):
# Start of second loop: which row to reduce
for j in range(i+1, n):
# Compute the multiplier i.e. which fraction of the
# pivot row i has to be subtracted from row j
mult = A[j][i] / A[i][i]
# Start of 3rd loop: re-assign all columns of row j
# i.e. Row_j = Row_j - mult*Row_i for columns 1 to n
for k in range(n):
A[j][k] -= mult * A[i][k]
# Also modify the RHS vector
a[j] -= mult * a[i]
In program lines 816, the matrix A and vector a are defined. Program line 18 gets the
number of rows of the matrix A and this is assigned to the name n. The actual forward
reduction step of Gauss elimination starts at program line 20.
Since we use the index i to indicate the pivot element, what must the bounds be on
this index? You should be able to recognise that we will start this index at 0 (recall that
we make all the entries of the matrix below the [0,0] entry equal to zero?). After all the
entries below the [0,0] entry are zero, we make all the entries below the [1,1] entry zero
(so the pivot element index = 1). The pivot element index keeps on incrementing by one
till it reaches the value n-1. Does this make sense to you? We have to stop at this value,
because a pivot element of [n,n] makes no sense: there are no entries in the matrix below
the [n,n] entry to make zero. I hope you now understand program line 18 which starts
the pivot element index at 0 and increments with one till the final pivot element index of
n-1.
Program line 22 defines the row index j. Its initial value is set to i+1, and it increments with one till a final row index of n. Does this make sense? If the pivot element
index is i, we must reduce all entries below the [i,i] entry to zero. So we start with
row i+1 and continue to row n.
Page 222
223
Program line 25 defines the multiplier i.e. which fraction of the i-th row must be
subtracted from the j-th row in order to set the A[j,i] term to zero. The multiplier is
simply the A[j,i] term divided by the pivot element A[i,i].
Program lines 2829 simply subtracts the appropriate fraction of row i from row j
and assigns this to row j. A for loop is required to repeat this for all columns (1 to n).
Program line 30 performs the same computation on the right hand side vector a.
Note that the order of the three nested for loop statements is fixed. The outer for
loop statement sets the pivot row counter (i). Once i is known, the next for loop
statement sets the lower bound of the row counter j in terms of i. Once i and j are
known, the forward reduction step can be performed on row j for all columns k (the third
and final for loop statement).
This example above will give the following output:
A =
[[ 2.
3.
[ 0. -0.5
[ 0.
0.
[ 0.
0.
a =
[[ 10.]
[ 0.]
[ 3.]
[ 2.]]
4.
0.
1.
0.
1. ]
0.5]
2. ]
2. ]]
As you can see, the forward reduction step is implemented correctly. The matrix A
and vector a agree with Eq.(10.13).
10.9.1.2
Back-substitution step
After we reduced the system to an upper triangular system, we are ready to start the
back-substitution process. As before, we first have to decide what type of computations
are required. for loop statements are again required, because the number of operations
we have to perform is directly related to the size of the matrix (which is known).
Just to illustrate the required steps more clearly, consider a general 4 4 system that
Page 223
is already upper-triangular:
0
0 A33
0
0
0
224
A14
x1
A24
x2
A34
x3
A44
x4
a2
=
a3
a4
(10.19)
(10.20)
(10.21)
(10.22)
(10.23)
n
X
Aij xj
ai
j=i+1
xi =
A
ii
xn =
(10.24)
for i = n 1, n 2, . . . , 0.
(10.25)
From Eq.(10.25) you should be able to recognise the need for two for loops: i =
n 1, n 2, . . . , 0 and j = i + 1, i + 2, . . . , n. The i counter points to the current
component of the vector x we are solving, while the j counter keeps track of the sum of
products we must subtract from ai . The order in which the for loop statements appear
is fixed: the outer for loop statement must contain the name i, and the inner for loop
statement the name j. This is because the j for loop lower bound is defined in terms of
the current value of the i counter.
Ill go ahead and program the back-substitution step of the Gauss elimination algorithm.
Page 224
225
Add the back-substitution part below the forward-substitution part and run the program. The following output should appear:
x =
[[ 1.]
[ 1.]
[ 1.]
[ 1.]]
10.10
226
Weve just developed our own implementation of Gauss elimination. It can solve any
n n system of linear equations. I believe that Gauss elimination is quite a challenging
algorithm to understand, so if you manage you are well on your way to becoming a
proficient programmer.
Since the numpy module in Python was originally developed to perform linear algebra,
you probably expect that numpy has built-in capability to solve large linear systems. This
is in fact the case, more than one method exists to solve linear systems. If the linear
system
Ax = a
(10.26)
has a unique solution, the inverse of A exists and we can pre-multiply Eq.(10.26) with
A1 to obtain
x = A1 a
(10.27)
This operation is performed in Python by using the the numpy.linalg.inv and
numpy.dot statements i.e.
x = dot(inv(A), a)
x = solve(A, a)
Page 226
227
More Info:
Import the numpy module into the IPython Console and type
1. numpy.linalg.inv?
2. numpy.linalg.solve?
for the respective documentation available.
So lets compare the efficiency of our algorithm with that of numpys built-in functions.
So here follows a complete program that solves a 100 100 system using the three
methods:
Our Gauss elimination algorithm,
numpys dot(inv(A), a) method and
numpys solve(A, a) function.
A_copy = copy(A)
a_copy = copy(a)
start = time()
# Compute size of matrix
n = A.shape[0]
# Start of 1st loop: pivot row counter
for i in range(n-1):
Page 227
228
stop = time()
print "Elapsed time = %f" % (stop - start)
start = stop
stop = time()
print "Elapsed time = %f" % (stop - start)
start = stop
Page 228
229
stop = time()
print "Elapsed time = %f" % (stop - start)
More Info:
The time.time function returns the current time in seconds. Import the time module
into the IPython Console and type time? and time.time? for available documentation.
The output of the above program for matrix dimension of 100 100 is
As you can see the inverse method is now about 3 magnitudes times faster than our
method and the solve method is also about 3 magnitudes times faster than our method.
Let us attempt to improving the performance of our algorithm. We can make a
dramatic improvement in our algorithms efficiency by making a subtle change during
the forward reduction step. Refer to program line 31 above: youll see that we subtract
a fraction of line i from line j, for columns 1 to n. But is this really necessary? As
we perform the forward reduction step of Gauss elimination, we know that the entries
to the left of the diagonal will become zeros. And we never use these zeros during the
back-substitution step of the algorithm. So why do we bother computing them at all?
Computers take the same amount of time to multiply zeros with some other number as
compared to non-zero number multiplication, so we are wasting valuable time. All that
we have to do to improve our algorithm, is to change program line 31 to
Page 229
230
This new program line will only subtract those columns that will end up as non-zeros,
so we arent computing the zeros any more. You can check that this change doesnt affect
the solution, and the times required to solve a 100 100 system now becomes
This simple change has improved the speed of our algorithm by approximately 35%! In
the weeks to come well make more changes to improve the performance of our algorithm.
10.11
Exercises
231
5. Write a program that asks the user to input a vector. The program must then
compute the average value of vector components and display it to the screen, without
using the average function from numpy. You can check your answer using the
average function from numpy: from numpy import average.
6. Write a program that asks the user to enter a matrix. The program must then
display a matrix that contains the absolute value of each component of the matrix,
without using the abs function from numpy. You can check your answer using the
abs function from numpy: from numpy import abs.
7. Write a program that asks the user to enter a vector u. The program must then look
at every component of vector u and check whether the component is divisible by 2.
If it divisible by 2 the program must store a 1 in the position of the even component.
Otherwise the program must store a zero in the position of the respective component.
The number 0 is an even number.
Here is an example:
ueven
2 0 6 7 10 4 11
= 1 1 1 0 1 1 0
u=
(10.28)
8. The trace of a square (n n) matrix A is defined as the sum of the diagonal entries
of A. Write a program that asks the user to enter a matrix. The program must
then check whether the matrix is square i.e. whether the number of rows is equal
to the number of columns. If the matrix is square the program must compute the
trace of the matrix (without using the numpy.trace function) which is given by
n
X
tr(A) =
A(i, i)
(10.29)
i=0
You can check your answer by using the the numpy.trace function. Just for interesting sake: The trace of a matrix is the sum of its eigenvalues. The trace is
an invariant of a matrix. Similarly, the length of a vector is an invariant as the
length of a vector does not change length when the basis (or reference frame or axis
system) changes.
9. Matrix B is a antisymmetric (skew-symmetric) if matrix B is square and the following holds for all i = 0, 2, 3 . . . , n and j = 0, 2, 3, . . . , n:
B(i, j) = B(j, i)
(10.30)
Write a program that asks the user to enter a matrix. The program must then tell
the user whether the matrix entered is skew-symmetric or not. Here is an example
of an anti-symmetric matrix:
0
1 2
3
1
0
4 5
(10.31)
2 4
0
6
3
5 6
0
Page 231
232
5 6 1
5 4 3 2
4 7 0
A = 6 7 8 9 AT =
3 8 12
1 0 12 11
2 9 11
(10.32)
(10.33)
You can check your answer by using the the numpy.transpose function.
11. Write a program that asks the user to enter a square matrix X. The program must
then compute the antisymmetric (skew-symmetric) part of matrix X component by
component.
The antisymmetric part of a square matrix X is given by the following:
1
(X X T )
2
(10.34)
n2
nm n2 nm
EA
m2 nm m2
nm2
k=
(10.36)
n2
nm
l n nm
nm m2
nm
m2
where
l=
(x2 x1 )2 + (y2 y1 )2
Page 232
(10.37)
233
with
n=
x2 x1
l
(10.38)
m=
y2 y1
l
(10.39)
and
13. Some square matrices can be factored into an upper triangular matrix U and a lower
triangular matrix L. Write a program that will compute the LU factorization of a
square (n n) matrix A.
First let us compute the upper triangular matrix U of matrix A. We start by setting
U = A component for component.
Then for each column i = 0, 1, 2, . . . n 1 we need to compute the following for the
rows j = i + 1, i + 2, i + 3, . . . n of matrix U :
(a) Set L(i, i) equal to 1.
(b) Compute a scale factor and store it in matrix L given by:
L(j, i) =
U (j, i)
U (i, i)
1
1
0
3
2
1 1
1
A=
3 1 1
2
1
2
3 1
(10.40)
1
0
2
1
L=
3
4
1 3
0
0
1
0
0
0
0
1
1
1
0
3
0 1 1 5
U =
0
0
3
13
0
0
0 13
(10.41)
You can also test your answer using the lu function from the scipy module: from
scipy.linalg import lu
Page 233
234
15. Write your own matrix-matrix multiplication program that works on a component
by component level, and test the answer of the previous question. Refrain from
looking at the notes attempt this on your own.
Start by doing matrix-matrix multiplication by hand and then proceed to put the
logic in place.
16. Matrix A is strictly row diagonally dominant if for every row, the absolute value
of the diagonal term is greater than the sum of absolute values of the other terms.
That is if
n
X
|A(i, i)| >
|A(i, j)|
(10.42)
j=1;j6=i
5
3
0
3
8
1
4
6
15
0.5 1.5 3.25
1
2
3
7
(10.43)
1
2
3
7
(10.44)
5
3
0
3
2 1
4
6
15
0.5 1.5 3.25
since the diagonal entry of the second row is 2 which is less than the sum of the
absolute values of the rest of the entries in the second row.
17. Redo the above exercise only using the sum and diag functions from numpy: from
numpy import diag, sum.
18. Carl Gustav Jakob Jacobi came up with an iterative method to solve a linear system
of equations Ax = b, where A is an nn matrix and both b and x are n1 vectors.
Jacobis strategy starts with an initial guess x0 for the solution x at iteration 0.
The solution for the (k + 1)th iteration is then computed using
!
n
X
1
b(i)
A(i, j) xk (j) , i = 1, 2, 3, . . . , n
(10.45)
xk+1 (i) =
A(i, i)
j=1;j6=i
Jacobis strategy continues to compute xk + 1 as long as the solution has not converged i.e.
kxk + 1 xk k .
(10.46)
Write a program that computes the the solution to a linear system of equations
using Jacobis method above.
Page 234
Chapter 11
Functions
A function is a collection of program statements that perform a specific task. Functions
are called from other programs, or from other functions. We have encountered a number
of Python functions so far. The first is the range functions, which is a function that
generates a list of integers. Do you recall the syntax of the range statement? The
statement
my_list = range(num_ints)
generates a list (my list) of integers that has num ints number of integers in the list
(starting from 0). As you can see, the range function requires at least 1 scalar input (in
the example above num ints) and it generates a single list as the output (my list in the
example above).
We have also encountered the numpy.linalg.inv function, which generates the inverse
of a given matrix. The numpy.linalg.inv functions requires a single square matrix as
input and it generates the inverse of this matrix as output. The statement
mat_inv = inv(mat)
will generate the matrix mat inv which is the inverse of the matrix mat. The trigonometric functions sin, cos, tan and the exponential function exp (from the math module)
Page 235
236
are other examples of functions that we have used so far. All of these functions require a
single object as input and it produces a single object as output.
A special type of function we have used is the lambda function, which a function that
generates other functions. The lambda function requires a few variables as inputs and it
generates a function that will return a single value output. lambda functions can be useful
in many cases, however often you will find the lamba function to be very limited when it
come to data containers; or when you need more than one output from the function; or
when the function needs to be more than 1 line of code.
11.1
Programming will be a very difficult task if we could only make use of existing functions.
Not only will our programs become very long and difficult to keep track of, but it will
also be impossible for a group of programmers to develop different components of a new
program simultaneously. Fortunately, all programming languages allow for creating new
functions.
In Python, new functions are created by defining a function with the following syntax:
The def statement is used to define a new function with a specified function name.
The set of objects (inputs) sent to the function are enclosed in round brackets after the
function name. These inputs are separated by a comma and a space (, ). Then the set of
objects (outputs) that the function will generate are returned to the main program by the
return statement. Different outputs are separated by a comma and a space (, ). These
input and output objects can have any name, as long as you obey the object name rules.
The function name must also obey the usual object and program name rules.
Take Note:
Note the double colon (:) at the end of the def statement in line 1. This tells Python
where the program statements inside the function start.
If the double colon (:) is left out you will see an error message.
Page 236
237
Take Note:
The rules for indentation for a function are the same as for the for loop, see Section
4.2.5.
Python requires 4 spaces before all program statements inside the function.
Take Note:
The return statement tells Python what information must be returned from the
function as outputs. Without the return no information will be returned from the
function. The return statement exits the function immediately and no code (in the
function) after the return statement will be executed.
More Info:
PEP8 standard for function names:
lowercase with words separated by underscores as necessary to improve readability, e.g. my function and not My Function.
names should be at least 3 characters in length and descriptive.
The PEP8 standard is available at:
http://www.python.org/dev/peps/pep-0008/
The objects that you use within the function are local objects, i.e. these object values
are only known inside the function. The values of the objects used in your function will
not be available to the calling program (or in the IPython Console). If you want a object
value to be available to a calling program, send the object back to the calling program by
adding this object to the set of output objects.
Ill now give numerous examples of functions. Ill start of by writing a function that
can add two numbers. My functions will need 2 inputs (the two numbers I want to add)
and it will produce a single output (the sum of these two numbers). So I decide that the
syntax of my function is:
where the name num3 is the sum of the names num1 and num2. Now I need to write
the function add:
Page 237
238
For this example, the function add contains only 3 program lines. The first line defines
the inputs and the name of the function. The remaining program line in a function
computes the sum and returns the results as an output. In this case a single program
line is sufficient. Notice that inside the function, you assume that the input objects are
available (i.e. these values will be passed in from the calling program, so you should not
define these values). Any other names and objects that you require during computations
have to be defined within the function. Also note that the names you use inside the
function are independent from the names you use when calling the function.
New functions can be saved in the same file as your main program or in a completely
separate file, it is up to you as the programmer how you want to consolidate / group
different function and code. You can call the new function add from any program or
other functions. You can also call the function from the IPython Console. Ill illustrate
calling the function add from a program.
11.1.1
Example 1
In this example I will assume that the function add is being called from the same file as
the main program:
# ----- main program ----# Define the inputs that the function add requires
var1 = 4.0
var2 = 5.0
# Call the function, passing the input names
# var1 and var2 into the function and getting the
# output var3 back
var3 = add(var1, var2)
Page 238
239
The function add is called in program line 14 and the output of the program listed
above is:
11.1.2
Example 2
In this example I will assume that the function add is being called from the main program
in a different file. For this example lets assume that the function add has been saved in
a file called my functions.py:
Then in the main program we need to import the add function, like we did with the
math module:
The function add is imported to the main program in line 3 and then called in line 11
and the output of the program listed above is:
Page 239
240
Take Note:
In this example the main program must be saved in the same location as the my functions.py
file.
Take Note:
Functions have a special type of variable called local variables. These variables only
exist while the function is running. When a local variable has the same name as
another variable (such as a global variable), the local variable hides the other. Sound
confusing? Well, these next examples should help clear things up:
a = 4
def print_func():
a = 17
print "in print_func a = ", a
print_func()
print "a = ", a
in print_func a = 17
a = 4
Variable assignments inside a function do not override global variables, they exist
only inside the function. Even though a was assigned a new value inside the function,
this newly assigned value was only relevant to print func, when the function finishes
running, and the as values is printed again (the originally assigned value 4).
11.2
Functions (Continued)
Lets consider another example. Suppose we want to write a function that takes two
numbers as inputs, and then performs the simple operations addition, subtraction, multiPage 240
241
plication and division to provide four outputs. I decide the function name is arithmetic,
so the syntax of such a function is:
where the two scalar inputs are num1 and num2 and the four scalar outputs are add,
subtract, multiply and divide. The function arithmetic follows:
7.0
-1.0
0.75
12.0
Take Note:
You should notice that the names used to call the function have nothing to do with
the names used inside the function.
As the programmer, you can choose the data containers for the inputs and outputs
of your function, i.e. scalar objects, lists, dictionaries, or numpy arrays. As an example,
lets assume you prefer that the input to your arithmetic function is a list of the two
number:
Page 241
242
Notice that the input is now a single list object numbers that contain two components.
The function arithmetic cannot be called using two inputs any more, it now has to be
called using a single list containing two components as an input. The four scalar outputs
could also be replaced by a single list object as well, by simply enclosing the returned
outputs in square brackets:
To illustrate this last version of the arithmetic function, the output from the program
will be:
243
As you can see, I called the arithmetic function using the [3.0, 4.0] list as an
input. I obtained a single list results as output, with the four computed values available
in the four components.
The final simple function well write is a function that computes the components of a
vector, if the length and angle of the vector is provided. Lets decide that the angle can
be specified in either degrees or radians. I choose the name of the function as decompose
(meaning a function that decomposes a vector into its components). The syntax of the
function is
where the inputs are the length of the vector (length), the vectors angle (angle) and
the name option, which must distinguish between degrees or radians. The outputs are
the two components comp x and comp y, and a string name message that can contain an
error message. As the programmer, you decide how you want to use the option name.
You can decide that the option name can have a value of 1 or 2, where 1 indicates the
angle is expressed in degrees and 2 indicates the angle is expressed in radians. Or you can
decide that the name option is a string object that can either contain a d to indicate
degrees or a r to indicate radians. Ill use the latter option. If the option name doesnt
contain either a r or d, I set the message object equal to Incorrect Input.
Here follows the function decompose:
import math
from math import pi
Page 243
244
I called the new function from the IPython Console to produce the following output.
I used both options i.e. expressed the angle as 45 degrees and as 4 radians. I also called
the function using incorrect input. As you can see, the first two cases produce the same
output (as it should) and the last case produces the error message. In this example the
decompose function is saved in the test.py file.
In [#]:
Page 244
11.3
245
Let us again consider the range function. You should recall that the range function can
be used with additional inputs (i.e. the start integer and the step size):
These additional inputs are known as optional inputs. We can also create new functions with optional inputs using the following syntax:
Here the first two inputs (input1 and input2) will be required inputs (and must be
specified when calling the functions). The last input (input3) is the optional input, its is
optional because when we create the function we give input3 a default value. If input3
is specified when calling the function then input3 will equal the specified value, if input3
is not specified when calling the function then input3 will equal the default value. Let
us consider a few examples to explain this concept further.
print test_convergence(1E-8)
print test_convergence(1E-8, tol=1E-10)
In this example we give tol the default value of 1E-6 when we created the function
test convergence (line 1). The first time we call test convergence (line 7) with an
error of 1E-8 we do not specify the optional input for tol. Thus in the function (line 1)
tol is given the default value of 1E-6.
The second time we call test convergence (line 8) with an error of 1E-8 we now
specify the input for tol. Thus in the function (line 1) tol is given the value of 1E-10.
Page 245
246
Lets consider one last example, you should by now be able to figure out the logic of
this example:
if show_title:
title = "Mrs "
if person["gender"] == "m":
title = "Mr "
if initials_only:
name = person["name"][0].upper()
greet(person)
greet(person, initials_only=False)
greet(person, show_title=True)
greet(person, initials_only=False, show_title=True)
Hello
Hello
Hello
Hello
Take Note:
All keyword arguments (optional inputs) MUST come after the required arguments
(required inputs) when creating a function otherwise Python will give an error.
Page 246
247
More Info:
See http://en.wikibooks.org/wiki/Non-Programmer%27s Tutorial for Python 2.6
for more examples on defining functions.
11.4
Ill revisit the Gauss elimination program to further illustrate the use of functions. As
the programmer, you can decide how to organise your program. In this case, Ive decided
to make use of two functions. The first, called reduction, is a function that performs
the forward reduction step of the Gauss elimination process. The second function, called
backsub, performs the back-substitution step. The inputs that the forward reduction
step requires is the numpy matrix (mat a in this case) and the numpy vector (vec a in
this case). The outputs that the forward reduction step produces are the modified matrix
mat a and modified vector vec a. Hence the syntax of the reduction function is
The backsub function takes the reduced matrix mat a and the vector vec a as input
and it produces the solution to the linear system as output (x vals in this case). Hence
the syntax of the backsub function is
The final version of the Gauss elimination program listed in Section 10.10 now reads:
import time
import numpy as np
248
249
stop = time.time()
print stop - start
The performance of the new version of the Gauss elimination program is similar to
the original program. This example is useful as an educational example, but is probably
not realistic. I cant imagine a scenario where we would like to only perform forward
reduction or only back-substitution. Rather, a single function gauss makes more sense.
It takes the matrix mat a and vector vec a as input and it produces the vector x vals as
output i.e.
import time
import numpy as np
250
return solution
stop = time.time()
print stop - start
# Starts stopwatch third and final time
start = time.time()
stop = time.time()
print stop - start
Page 250
11.5
251
I conclude this section on functions by giving a final example. Consider the numerical
integration program in Section 8. I propose that each of the four methods of integration
can become a function. So all we have to determine is the inputs and outputs of these
functions. As the programmer, I decide that as outputs I want the numerical value of the
integral as well as the required number of intervals. Regarding the inputs, here we have
no choice. A function that must perform numerical integration needs i) the function to be
integrated, ii) the lower and upper bounds of integration iii) the start number of intervals
and iv) the required accuracy. All other values can be defined within the function. So
the syntax of the integration functions is
where the inputs are the function func, the lower bound lower, upper bound upper,
and the required accuracy acc. The initial number of intervals num is created as an
optional input to the functions. The function will then provide the integral integral
and the required number of intervals num. Heres the listing of the modified numerical
integration program and only the left point function. The other functions only differ
in the details of the area computation.
252
def main():
# Get user inputs
f_string = input("Please enter function to be integrated: ")
func = eval("lambda x: " + f_string)
lower = input("Enter the lower bound of integration: ")
upper = input("Enter the upper bound of integration: ")
acc = input("Enter the required accuracy: ")
253
# Display answer
print ("Integrating " + f_string + " between " +
str(lower) + " and " + str(upper) + " = " +
str(integral))
print (str(num) +
" intervals required for an accuracy of " +
str(acc))
# Display options again
print
print "1: Left-point 2: Right-point 3: Mid-point"
print "4: Trapezium 5: Quit program"
method = input("Please enter your option: ")
if __name__ == "__main__":
main()
Page 253
11.6
254
def pi_taylor_series(accuracy=1E-3):
"""
Taylor series approximation to pi using a while loop
Optional Input:
accuracy (default = 1E-3)
Returns:
k: number of terms needed to compute pi
taylor_pi: taylor series approximation of pi
"""
# Initialize k to zero
k = 0
# Compute 1st term in Taylor series
taylor_term = (4.0 * (-1)**k) / (2*k + 1)
taylor_pi = taylor_term
# Start of while loop
while abs(taylor_term) > accuracy:
k += 1
taylor_term = (4.0 * (-1)**k) / (2*k + 1)
taylor_pi += taylor_term
return k, taylor_pi
# return approximation
The second type of information you can add to your code is called docstrings (documentation strings), this is shown in the example above in lines 211. Docstrings are used
to add documentation to your modules and functions which will help you (or anyone else
who is using your code) remember how to use that function. The information you have
Page 254
255
been seeing in the Object inspector window of Spyder is exactly this, docstrings added to
the code. The information you have been seeing in the IPython Console, when you type
object?, is docstrings added to the code.
You add a docstring to your functions by enclosing the information in 3 doublequotation marks. This docstring must be the first thing appearing in the function, after
you define the function (i.e. starts directly after the function definition). Save the above
example in a file (in my case it is saved in test.py) and import the pi taylor series
function into the IPython Console. If you now type pi taylor series? you should see
the following output:
In [#]: pi_taylor_series?
.
.
.
Taylor series approximation to pi using a while loop
Optional Input:
accuracy (default = 1E-3)
Returns:
k: number of terms needed to compute pi
taylor_pi: taylor series approximation of pi
In [#]: pi_taylor_series()
Out[#]: (2000, 3.1420924036835256)
In [#]:
11.7
Exercises
1. Write a function mysphere which takes the radius r of a sphere as input. The
function must then calculate the surface area and volume of the sphere. The surface
area and volume of the sphere must then be returned as outputs of the function.
The surface area of a sphere is given by:
4r2 .
Page 255
(11.1)
256
4 3
r .
3
(11.2)
2. Consider a rectangular section with the x-axis aligned with the width b of the section
and the y-axis aligned with the height h of the section.
Write a function rectmi that takes the width b and height h of a rectangular section
as input. The function must calculate the moment of inertia around the centroidal
y-axis Iy , x-axis Ix and polar Iz with
Ix =
Iy =
Iz =
1
bh3
12
1
hb3
12
bh 2
(b +
12
(11.3)
h2 )
3. Write a function twodice that takes as input the number of double dice throws. For
every dubbel throw (two dices that are thrown simultaneously) the function must
then generate two random integers dice1 and dice2 between 1 and 6. The sum of
values of the two dices namely dice1+dice2 must then be calculated. The function
must then keep count of how many times every possible sum i.e. between 2 and 12
was thrown using a vector. The first entry of the vector must contain the number of
times a 2 was thrown, the second entry the number of times a 3 was thrown etc. The
function must then return the vector containing the number of counts as output.
4. Write a function my multiply that takes as input a matrix A and a matrix B.
The function must then calculate the matrix-matrix product A B and return the
matrix-matrix product A B as output.
5. Write a function vector rotate that accepts a vector x of length 2 and an angle
as input. The function must then compute and return the rotated vector xrotated as
output.
In two dimensions the rotation matrix Q is given by:
cos() sin()
Q=
sin()
cos()
(11.4)
(11.5)
Use function my multiply from the previous question to compute the matrix-vector
product.
Plot the un-rotated vector and then the rotated vector. The tail of the each vector
is given by the origin [0, 0] and the head of each vector by the components given by
x or xrotated .
For interesting sake: A rotation matrixs is an orthogonal matrix (i.e. square matrix
of which the transponent is equal to the inverse) whose determinant is equal to 1.
A rotation matrix does not change the length of a vector but only rotates it.
Page 256
257
6. Revisit as many of the previous chapter exercises as possible and change the programs to work as functions. Instead of specifying a object or vector or asking the
user to enter a number, string or vector; specify it as inputs of a function and
determine the appropriate outputs.
7. In 1972 Marsaglia derived an elegant method for generating random points uniformly
on the surface of a unit sphere. The method consists of picking x1 and x2 from
independent uniform distributions between -1 and 1. The points x1 and x2 are
accepted if x21 + x22 < 1 otherwise the points x1 and x2 are rejected and picked again.
The x, y and z coordinates of a point randomly distributed on a unit sphere are
then
q
x = 2x1 1 x21 x22
q
(11.6)
y = 2x2 1 x21 x22
z = 1 2 x21 + x22
when x1 and x2 have been accepted.
Write a function rnd sphere that takes the number of points N as input argument.
The function must then return a N 3 matrix that contains the x-coordinates in
the first column, y-coordinates in the second column and z-coordinates in the third
column of the N randomly distributed points on the unit sphere.
8. Winograd introduced an algorithm for multiplying two n n matrices A and B
C = AB
for n an even number.
The Winograd algorithm works as follows: The product of the ith row and jth
column is given by:
n/2
X
a(i) =
A(i, 2k 1) A(i, 2k)
k=1
b(i) =
n/2
X
k=1
C(i, j) =
n/2
X
k=1
B(2k 1, j) B(2k, j)
Write a function winomult that takes two matrices A and B as input. The function
must then return C = AB using the Winograd algorithm. Assume the matrices A
and B are square and n even.
Page 257
258
Page 258
Chapter 12
File handling
The world of computers will be much simpler if we could do all our work using a single
application. But this is not the case, so we have to make sure that whatever programs
we are using, we can exchange information with other programs. Python is no exception.
Even though we might be very accomplished Python programmers, we at times require
ways to access data from other sources, or generate data for other applications.
Also once a computer is switched off all the computed results are lost as everything is
stored in temporary memory, except for the programs we have written. Every time you
switch your computer on and want to look at the results you would have to run a program.
Can you imagine doing that if the calculations require a day or two of computations
e.g. computational mechanics (CFD or FEM) or the optimization of a large cities road
network.
There are so many ways to read data into Python and write data from Python that I
cant even begin to cover them all. In this section Ill briefly cover a few commonly used
methods to read data into Python, or to generate files so that other programs (e.g. Open
Office Calc) can get access to data that we generated in Python. The data would then
still be available once a computer is switched off and on.
12.1
The numpy.save command is used to save a numpy.array to a .npy file that can later
be retrieved using the numpy.load command. This is useful for when your program takes
days to execute and you want to make sure the information is not lost if the power fails
or your PC shuts off during the execution of your program.
Page 259
260
The numpy.load command can be used to load a numpy.array from a .npy file into
Pythons memory. The requirements are that the file must have been created using the
numpy.save function. The syntax of the numpy.load command is:
my_array = load("filename.npy")
You have to specify both the filename and the .npy extension. The content of the
file is then saved in the variable my array.
Take Note:
In the example above, it is assumed that the file "filename.npy" is saved in the
same location as your Python (.py) file executing this example. See section ?? on
how to load files from a different directory.
Suppose instead you wrote a Python program that produced the answers to some
problem. If you want to produce a .npy output file that contains the output of your
program, you can use the numpy.save command. The syntax of the numpy.save command
is:
You dont need to specify the .npy extension after the filename when using the
numpy.save function. Lets look at the following examples
....:
....:
261
[2, 5, 2],
[9, 2, 8]], dtype=float)
In [#]: mat
Out[#]:
array([[ 1.,
[ 2.,
[ 9.,
2.,
5.,
2.,
3.],
2.],
8.]])
In [#]: load("tempfile.npy")
Out[#]:
array([[ 1., 2., 3.],
[ 2., 5., 2.],
[ 9., 2., 8.]])
In [#]: new_mat
Out[#]:
array([[ 1., 2.,
[ 2., 5.,
[ 9., 2.,
3.],
2.],
8.]])
In [#]:
12.2
Unfortunately output files generated using the numpy.save are not accessible by any other
program other than Python + numpy. A slightly more general method to read data into
Python is the numpy.loadtxt statement. The syntax of the numpy.loadtxt statement
is:
Page 261
262
Take Note:
In the example above, it is assumed that the file "filename.extension" is saved in
the same location as your Python (.py) file executing this example. See section ??
on how to load files from a different directory.
where the delimiter optional input is the special type of character that is present
between the columns of data and any delimiter is allowed. Common delimiters are the
space , a comma , or the tab \t. As an example, if data is available in a file
data.txt and columns are delimited with commas, this data can be read into a variable
input data with the statement:
Similarly, Python can generate an output file using the numpy.savetxt statement. As
before, any delimiter is valid, but the usual ones are the space, comma or tab. The syntax
of the numpy.savetxt statement is
The above statement will save the variable my array into the file filename.txt and
will use the specified delimiter between the columns of data. Lets look at the following
examples
263
In [#]: mat
Out[#]:
array([[ 1.,
[ 2.,
[ 9.,
2.,
5.,
2.,
3.],
2.],
8.]])
In [#]: new_mat
Out[#]:
array([[ 1., 2.,
[ 2., 5.,
[ 9., 2.,
3.],
2.],
8.]])
In [#]:
The format of the data (save in the text output file) can also be modified using the
optional input fmt="", as shown in the syntax below.
The text output file (from the example above, using fmt="%.3e") contains the following information, which can now easily be read into another program (e.g. Open Office
Calc):
12.3
264
The problem with the numpy methods, discussed above, is that the data needs to be stored
in a numpy array object. If you have both string and numerical information, stored in
either a list or tuple object, then you need to use the csv module to read and write the
information from and to a file. The following example shows how to write information to
a file using the csv module:
import csv
And this example following will show how to read that same information from the
specified file.
import csv
265
12.4
The standard built-in method in Python to read and write data is to use the open command in Python. The open command has various functions associated to it, like the
readline and write functions. Ill now show one example of how you can write a string
template (populated with data) to a output file using this built-in method:
TEMPLATE = \
"""-----------------------------------------This log file contains:
x1 \t x2 \t x3 \t f(x)
-----------------------------------------%s
------------------------------------------"""
def write_file(data):
# initialize the string that contains the values from
# the data array
body = ""
# loop over each row vector in the data array (matrix)
for vector in data:
# use the LINE_ENTRY string template to format the
Page 265
266
def main():
# create a data array for testing
data = array([[0.001, 0.011, 2.001, 10.123],
[0.002, 0.021, 3.001, 21.103],
[0.003, 0.031, 4.001, 32.123],
[0.004, 0.041, 5.001, 43.123],
[0.005, 0.051, 6.001, 54.120],
[0.006, 0.061, 7.001, 65.123],
[0.007, 0.071, 8.001, 76.123],
[0.008, 0.081, 9.001, 87.023]], dtype=float)
# call the function to write the data array to a file
write_file(data)
if __name__ == "__main__":
main()
And now this example that follows shows how to read the same information from the
specified file:
def read_file():
Page 266
267
if __name__ == "__main__":
data = read_file()
print data
12.5
Exercises
1. Write a function that writes a matrix to a file. The function inputs are the name of
the file and the matrix. Use the numpy.save function to save the matrix to a file.
2. Redo the previous exercise using the numpy.savetxt function to save the matrix to
a file.
Page 267
268
3. Write a function that reads a matrix to a file. The function inputs are the name of
the file. The function must then return the matrix as output.
4. Write a function that writes the contents of a list of dictionaries to a text file. The
text file must contain as headings the common keywords of the dictionaries. The
file must be a comma separated file (CSV). Each following row must then contain
the corresponding information of each respective keyword.
Hence for a list of dictionaries the output of the file would be:
Name,Age,Gender
Piet,25,M
Thabo,23,M
Tanya,24,F
John,30,M
Thandi,27,F
5. Write a function that reads the contents of the CSV as created above and stores it
in a list of dictionaries.
6. Write a function randomdata that takes an integer number n and a string filename
as input. The function randomdata must then generate a n 1 vector of normally
(uniform) distributed random numbers (type help(numpy.random) for additional
help on normally distributed random numbers) and save it to an ASCII file. The
name of the ASCII file is the user specified filename with a .data extension i.e.
filename.data.
Write a second function processdata that takes only a string as input which specifies the name of a file. The function processdata must then load the data from the
user specified file. The output of the function is the following descriptive statistical
measures,
average (type help(numpy.mean))
Page 268
Chapter 13
Graphs
13.1
2D Graphs
Before you can draw a graph of any data, the data must be available. The data can either
be available is some file (maybe it is experimental data you obtained from a colleague) or
it is described by some mathematical function. Regardless of where the data comes from,
before you can plot it, you must have it.
Ill usually illustrate plotting by using simple analytical functions. As a first example,
lets plot the function
f (x) = sin(x) x [0; 2]
(13.1)
For this example, the independent variable is x. So lets generate data points along the
x-axis evenly spaced between 0 and 2. Youve already seen the two methods available
in Python:
1. If you know the number of data points you wish to generate, use the numpy.linspace
command. The following example creates N evenly spaced data points between the
specified lower bound LB and upper bound UB:
The first entry in the vector x will equal x lower and the N-th entry will equal
x upper.
Page 269
270
2. If instead you know the required spacing between data points, lets say delta x,
rather use the command numpy.arange:
In this case, the first entry of x will equal x lower, the second entry will equal
x lower + delta x and so on. The last entry of x will either be less than or equal
to x upper.
Instead of writing the program one line at a time, I present the complete program
below and then Ill explain the program lines:
def plot_sin():
x_vals = linspace(0, 2*pi, 200)
y_vals = sin(x_vals)
plot.plot(x_vals, y_vals)
def main():
plot.figure(1)
plot_sin()
plot.show()
if __name__ == "__main__":
main()
The module used for plotting in Python is the matplotlib module, specifically the
pyplot module from the matplotlib package. In program line 3 I import pylab as plot.
In program line 7 I define the vector x vals. The corresponding y vals vector is generated in program line 8. Since x vals is a vector, sin(x vals) results in a vector of the
same length as x vals.
Page 270
271
Take Note:
When doing more advanced computations of numpy arrays, such as sin, cos, etc.
you need to import these functions from the numpy module (as shown in line 2 in the
above example).
Program line 14 opens a new plotting window. This statement is not absolutely
necessary, but I prefer to explicitly decide in which figure window I want my plot to
appear. If you want to plot many figures simultaneously, the figure command becomes
a necessity. Program line 10 completes the program but plotting the x val vector vs. the
y val vector. The plot command can only be used for 2D plotting. Also, the two vectors
that you are plotting against each other must have the same length. In this example this
will be the case since program line 8 will always create the y val such that it has the
same length as vector x val. In program line 16, the show command is used to display
all the figure windows with the respective plots. The program listed above produces the
graph depicted in Figure 13.1.
1.0
0.5
0.0
0.5
1.0
0
Figure 13.1: 2D graph of f (x) = sin(x) for x [0; 2] produced using the plot command.
13.1.1
Graph annotation
In my opinion, the graph seems incomplete. We need to describe the x and y axes and
maybe add a title. This is achieved by the xlabel, ylabel and title statements.
plot.xlabel("x")
plot.ylabel("f(x)")
Page 271
272
As the programmer, you decide on the descriptions you want to add to your graph.
Remember to include the descriptions in single inverted quotes. Another feature I dont
particularly like in Figure 13.1 is the x and y axes limits i.e. x [0; 7] and y [1; 1].
The axis command is used to explicitly define new limits i.e. the statement
will only show the graph for x [x min; x max] and y [y min; y max]. Program line
8 for the example program now reads
If you make these changes and view the graph on the computer screen, it is acceptable.
However, if you attempt to include this graph in a document (such as these notes), you
might find that the text appears too small compared to the graph itself. In such a case,
you can change the font size by specifying an additional argument for the xlabel, ylabel
and title statements. Ill illustrate below, where I present the complete program:
def plot_sin():
x_vals = linspace(0, 2*pi, 200)
y_vals = sin(x_vals)
plot.plot(x_vals, y_vals)
plot.xlabel("x", fontsize=14)
plot.ylabel("f(x)", fontsize=14)
plot.title("Graph of the function f(x)=sin(x)", fontsize=14)
plot.axis([0, 2*pi, -1.1, 1.1])
Page 272
273
def main():
plot.figure(1)
plot_sin()
plot.show()
if __name__ == "__main__":
main()
Do you see how I used the fontsize optional input to increase the size of the text in
the figure? After these changes, the graph now appears as in Figure 13.2. A significant
improvement if you ask me.
Graph of the function f(x)=sin(x)
1.0
f(x)
0.5
0.0
0.5
1.0
0
plot.text(x_coord, y_coord,
"The text string you want to add to your graph.")
Page 273
274
where x coord and y coord are the x and y coordinates of the point where the text
string must appear within your graph. As an example, if we add the statements
plot.text(0.5, -0.8,
"I can add text wherever I want.",
fontsize=14)
plot.text(1.5, 0.9, "Top")
plot.text(4.5, -0.9, "Bottom")
to the previous program, it produces the graph depicted in Figure 13.3. As before,
setting the font size is optional. The first sentence uses a font size of 14pt, while the last
two text statements uses the default of 12pt.
Graph of the function f(x)=sin(x)
1.0
Top
f(x)
0.5
0.0
0.5
Bottom
4
13.1.2
This was just the beginning. matplotlib is capable of much more. Import pylab from
matplotlib and type help(pylab.plot) to see all the additional arguments for the plot
command. Heres the help output as displayed in the Command Window:
.
.
.
Page 274
275
plot(x, y)
plot(x, y, bo)
plot(y)
plot(y, r+)
#
#
#
#
#
================
character
================
-
--
-.
:
.
,
o
v
^
<
>
1
2
3
4
s
===============================
description
===============================
solid line style
dashed line style
dash-dot line style
dotted line style
point marker
pixel marker
circle marker
triangle_down marker
triangle_up marker
triangle_left marker
triangle_right marker
tri_down marker
tri_up marker
tri_left marker
tri_right marker
square marker
Page 275
p
*
h
H
+
x
D
d
|
_
================
276
pentagon marker
star marker
hexagon1 marker
hexagon2 marker
plus marker
x marker
diamond marker
thin_diamond marker
vline marker
hline marker
===============================
==========
character
==========
b
g
r
c
m
y
k
w
==========
.
.
.
========
color
========
blue
green
red
cyan
magenta
yellow
black
white
========
All the information in the above help file may not be useful (or understandable), but all
youll need often is the different line colours, line types and marker symbols. To illustrate
the use of different line colours, line styles and marker symbols, Ill plot the functions
f (x) = 10e0.2x sin( 2 x)
g(x) = tan( 4 x)
h(x) = 5(1 e0.5x )
(13.2)
(13.3)
(13.4)
on the same axes for x [0; 10] and y [10; 10]. Ill plot the function f (x) using a
blue line with x-marks at every data point. The function g(x) will be plotted using a red
line with points while h(x) will be drawn with a dash-dot line. The following program
produces the graph depicted in Figure 16.6:
Page 276
from
from
from
from
277
math import pi
numpy import linspace, exp, sin, tan
matplotlib import rc
matplotlib import pyplot as plot
def plot_functions():
x_vals = linspace(0, 10, 100)
f_vals = 10 * exp(-0.2 * x_vals) * sin(0.5 * pi * x_vals)
g_vals = tan(0.25 * pi * x_vals)
h_vals = 5 * (1 - exp(-0.5 * x_vals))
def add_plot_info():
plot.axis([0, 10, -10, 10])
legend_list = [r"$10e^{-0.2x} sin(0.5 \pi x)$",
r"$tan(0.25 \pi x)$",
r"$5(1-e^{-0.5x})$"]
plot.xlabel("x", fontsize=14)
plot.ylabel("Function value", fontsize=14)
plot.legend(legend_handle, loc=4, prop={size: 14})
def main():
plot.figure(1)
rc(text, usetex=True)
plot_functions()
add_plot_info()
plot.grid()
plot.show()
if __name__ == "__main__":
main()
Page 277
278
10
Function value
10e0.2x sin(0.5x)
tan(0.25x)
10
5(1 e0.5x )
0
10
Program line 8 defines the x vals vector. Using the x vals- vector, program lines 911
define the variables f vals, g vals and h vals that contain the corresponding function
values. Again remember that you need to import the the advanced mathematical function
from the numpy module in order to compute, for example, sin of a numpy array.
Program line 29 opens a plotting window. The three curves are produced with program
line 13. The first pair of vectors are plotted with the option b-x, which results in a
solid blue line with x-marks at data points. The second pair of vectors are plotted with
the option r.-, which uses a red solid line with points to plot the curve. The last
pair of vectors are plotted with the option m-., which plots the curve using a magenta
dash-dot line. Program line 19 sets the axes limits to x [0; 10] and y [10; 10].
Program line 25 adds a legend to the graph. A legend is only necessary if more than
one graph is plotted in a single figure. Use the legend statement after the plot statement
used to generate the curves. The usage is usually
279
and it automatically generates a square box with the line types and curve descriptions
within. I used the name legend list to store the string descriptions for each of the
curves. If you need equations in any of your labels, title or legend, then you need to
add a r in front of the string description and you need to enclose the equation in dollar
signs ($) as shown in lines 2020. The explanation for why the r is needed in front of the
string description is outside the scope of these notes, just remember that it is needed. The
dollar ($) signs are needed to tell matplotlib the string (equation) in between the dollar
($) signs needs to be interpreted by LaTeX and converted to a neat looking equation.
Program line 30 is also needed to tell matplotlib to turn the LaTeX interpretor on. This
unfortunately is just boiler plate code that you need to remember.
Program lines 23 and 24 labels the x and y axes respectively. The fontsize is also set
to 14pt in these lines. Program line 35 adds grid lines to the graph.
An alternative method to draw multiple graphs in a single plotting window is to replace
the program line
plot.plot(x, f, "b-x",
x, g, "r.-",
x, h, "m-.")
plot.plot(x, f, "b-x")
plot.plot(x, g, "r.-")
plot.plot(x, h, "m-.")
13.1.3
If you view Figure 16.6, youll see that the curve descriptions contain superscripts and
Greek letters (the letter ). Superscripts are created by typing the symbol followed by
the required superscript enclosed in curly braces i.e. e-0.2x is created by
r"$e^{-0.2x}$"
Subscripts are created by the underline symbol( ), followed by the subscript enclosed
in curly braces. As an example, xk+1 is created by
Page 279
280
r"$x_{k+1}$"
You can also create the complete Greek alphabet (lowercase and uppercase) by typing
the backslash symbol (\) followed by the Greek letter spelt out e.g. is created by the
statement
r"$\pi$"
Superscripts, subscripts and Greek letters can be used in any of the following commands: xlabel, ylabel, zlabel, title, legend, text and gtext. To illustrate the use
of superscripts, subscripts and Greek letters, consider the following Octave program:
def add_text():
plot.text(0.1, 0.90,
r"$X_{k+1} = X_k - 2 Y_k^2$",
fontsize=14)
plot.text(0.1, 0.75,
r"$Y_{k+1} = Y_k + X_{k+1}^{0.5}$",
fontsize=14)
plot.text(0.1, 0.60,
("Some upper case Greek letters : " +
r"$\Gamma \Delta \Xi \Pi \Sigma$"),
fontsize=14)
plot.text(0.1, 0.45,
("Some lower case Greek letters : " +
r"$\gamma \delta \rho \sigma \xi \eta \nu$"),
fontsize=14)
plot.text(0.1, 0.30,
"An example of a formula :",
fontsize=14)
Page 280
281
plot.text(0.3, 0.20,
(r"$A x = \lambda x \quad where \quad " +
r"\lambda = \omega^2$"),
fontsize=14)
def main():
plot.figure(1)
rc(text, usetex=True)
add_text()
plot.show()
if __name__ == "__main__":
main()
which produces the graph in Figure 13.5. Again take note that you need to add a r
in front of the string description and you need to enclose the equation in dollar signs and
you need to add line 36 to your program.
1.0
Xk +1 = Xk
0.8
0.6
0.4
Yk +1 = Yk
2Yk2
5
+ Xk0.+1
0.2
0.0
0.0
0.2
0.4
where
= 2
0.6
0.8
1.0
Figure 13.5: Illustrating the use of superscripts, subscripts and Greek letters in Octave.
Page 281
13.1.4
282
Other 2D graphs
matplotlib has many other built-in 2D graphs but I wont discuss these in detail. Rather
use the help and ? statements and look at all the various examples presented therein.
2D graphs that I have used include
13.2
3D Graphs
matplotlib has extensive 3D graphical capability. This topic wont be tested in the
exam, but I include it for completeness.
Page 282
13.2.1
283
The first type of 3D graph Ill discuss is produced by the plot3D statement. This statement is used to plot lines and/or points in 3D space. As with any other plotting statement,
first make sure you have the data you want to plot. As an example, Ill show you how to
plot the parametric curve described by
x = r cos()
y = r sin()
z=t
where r = t, = 23 t and t [0; 4].
def plot_3d_func(axis):
t_vals = linspace(0, 4, 200)
theta = 3 * pi/2 * t_vals
x_vals = t_vals * cos(theta)
y_vals = t_vals * sin(theta)
def add_plot_info(axis):
axis.set_xlabel("x")
axis.set_ylabel("y")
axis.set_zlabel("z")
def main():
fig = plot.figure(1)
axis = Axes3D(fig)
plot_3d_func(axis)
add_plot_info(axis)
plot.show()
Page 283
(13.5)
(13.6)
(13.7)
284
if __name__ == "__main__":
main()
For 3D plotting we have to make use of the Axes3D function (line 23), which takes
the figure object as an input. In program line 12 the plot3D function from the Axes3D
object is used to create the 3D plot. The graph is depicted in Figure 13.6.
4.0
3.5
3.0
2.5
2.0
1.5
1.0
0.5
0.0
0
x
13.2.2
The plot3D command was used to plot points and lines in 3D space. If instead you
need to plot surfaces in 3D space, you can use the plot wireframe and plot surface
statements. The plot wireframe statement produces a wire-frame representation of the
surface while the plot surface statement produces a coloured-in representation of the
surface.
Again, Ill illustrate these commands via an example. Consider the surface described
by
z(x, y) = sin(x) cos(y) for x [2.5; 2.5] and y [2.5; 2.5]
(13.8)
Before we can plot this surface, the data must be generated. In order to generate
a rectangular grid of (x, y) data points where we can evaluate the function z(x, y), we
need the meshgrid statement. The meshgrid statement is the 2D equivalent of the 1D
Page 284
285
linspace command. Instead of generating a single vector containing evenly spaced data
points, a regular 2D grid is created by the meshgrid command. The syntax is:
x = array([1, 2, 3])
y = array([4, 5, 6, 7])
X, Y = meshgrid(x, y)
from
from
from
from
def set_axis_properties(axis):
axis.set_xlim3d(-3, 3)
axis.set_ylim3d(-3, 3)
axis.set_zlim3d(-1, 1)
def add_plot_info(axis):
axis.set_xlabel("x")
axis.set_ylabel("y")
Page 285
286
axis.set_zlabel("z")
axis.set_title("Graph of z(x,y) = cos(x)cos(y)")
def main():
# ----Figure 1---fig = plot.figure(1)
axis = Axes3D(fig)
plot_3d_func(axis, mesh=True)
add_plot_info(axis)
set_axis_properties(axis)
if __name__ == "__main__":
main()
1
x
1.0
1.0
0.5
0.5
0.0 z
0.0 z
0.5
0.5
1.0
3
1.0
3
1
x
The plot depicted in Figure 13.7 is created by the plot wireframe command while
the graph in Figure 13.8 is created by the plot surface statement. These graphs are
Page 286
287
good examples of when it is necessary to increase the fontsize. The following example
shows the changes made to increase the font sizes, not only of the labels and title, but
also of the axis tick labels.
from
from
from
from
gca = fig.gca()
gca.set_xticklabels(gca.get_xticks(), fontsize=18)
gca.set_yticklabels(gca.get_yticks(), fontsize=18)
gca.set_zticklabels(gca.get_zticks(), fontsize=18)
def add_plot_info(axis):
axis.set_xlabel("x", fontsize=18)
axis.set_ylabel("y", fontsize=18)
axis.set_zlabel("z", fontsize=18)
axis.set_title("Graph of z(x,y) = cos(x)cos(y)",
fontsize=18)
def main():
Page 287
288
if __name__ == "__main__":
main()
1.0
0.5
0.5
0.0 z
0.0 z
-0.5
-0.5
-1.0
3.0
2.0
1.0
-3.0 -2.0
0.0y
-1.0 0.0
-1.0
-2.0
x 1.0 2.0
-3.0
3.0
-1.0
3.0
2.0
1.0
-3.0 -2.0
0.0y
-1.0 0.0
-1.0
-2.0
x 1.0 2.0
-3.0
3.0
After I make these changes, the resulting graphs are presented in Figures 13.9 and
13.10.
Page 288
289
101
10
x0.5
x0.5
x0.5
6
100
100
10-1
10-1
10-2
10-1
100
101
102
10-2
10-1
100
101
102
20
40
60
80
100
1000
1
cos
0.4
0.5
cos(x)
Frequency
0.2
500
-0.2
-0.5
-0.4
0
-1
0
0.2
0.4
0.6
0.8
bins
-5
0
x
-0.4
-0.2
0.2
0.4
13.3
Subplots
So far, whenever we wanted to plot more than one curve, we simply plotted all of them on
the same axes. However, this approach doesnt cater for all scenarios. Lets say we want
to plot a vector of complex numbers. It is customary to plot such a vector as two separate
plots: the first plot shows the magnitude while the second depicts the phase angle. Or we
want to plot a 3D curve as three distinct plots: the 2D projection in the x-y plane, the
2D projection in the x-z plane and the 2D projection in the y-z plane. To cater for these
scenarios, we use the subplot statement.
The subplot statement divides a single plotting window into m by n plotting areas.
We can then plot any graph we like in each of these smaller areas. The syntax of the
subplot statement is
plot.subplot(mnp)
which divides the plotting window into m by n smaller plotting areas. These smaller
plotting areas are counted from left to right, top to bottom. Any subsequent plotting
commands will then generate a plot in the p-th position. Some examples of subplot
layouts are depicted in Figure 13.11.
Page 289
290
Ill give a single example of how to produce 4 figures in one window by using the
subplot command. Ill draw the graphs of
f (x) = sin(x)
h(x) = tan(x)
g(x) = cos(x)
p(x) = sin2 (x)
(13.9)
(13.10)
for x [0; 4]. Heres the Python code that produces the required graphs, as depicted in
Figure 13.12:
from
from
from
from
math import pi
numpy import linspace, cos, sin, tan
matplotlib import pyplot as plot
matplotlib import rc
def plot_functions():
x_vals = linspace(0, 4*pi, 200)
f_vals = sin(x_vals)
g_vals = cos(x_vals)
h_vals = tan(x_vals)
p_vals = sin(x_vals) ** 2
plot.subplot(221)
plot.plot(x_vals, f_vals, "b")
plot.axis([0, 4*pi, -1.1, 1.1])
add_plot_info("sin(x)")
plot.subplot(222)
plot.plot(x_vals, g_vals, "r")
plot.axis([0, 4*pi, -1.1, 1.1])
add_plot_info("cos(x)")
plot.subplot(223)
plot.plot(x_vals, h_vals, "g")
plot.axis([0, 4*pi, -10, 10])
add_plot_info("tan(x)")
plot.subplot(224)
plot.plot(x_vals, p_vals, "m")
plot.axis([0, 4*pi, -0.1, 1.1])
add_plot_info(r"sin^2(x)")
def add_plot_info(func_str):
Page 290
291
plot.xlabel("x")
plot.ylabel("f(x)")
plot.title(r"$f(x) = %s$" % func_str)
def main():
plot.figure(1)
rc(text, usetex=True)
plot.subplots_adjust(wspace=0.4, hspace=0.4)
plot_functions()
plot.show()
if __name__ == "__main__":
main()
1.0
1.0
0.0
0.5
1.0
0
10
0.5
f(x)
0.5
f(x)
0.0
0.5
6
x
10
12
1.0
0
1.0
6
x
10
12
0.8
f(x)
f(x)
0.6
0.4
0.2
10
0
0.0
0
6
x
10
12
6
x
10
12
Page 291
13.4
292
Exercises
1. Use Python to plot the following functions, all on one graph. Set the range for the yaxis between -10 and 10. Add appropriate descriptions (use the Legend command).
f (x) = 10sin(x)cos(x)
x [2, 2]
2 0.1x
2 0.1x
f (x) = x e
2x e
x [2, 2]
f (x) = tan(x)
x [2, 2], = 0.5
(13.11)
(13.12)
(13.13)
2. A nice picture can be constructed by plotting the points (xk , yk ) generated from the
following difference equations
p
(13.14)
xk+1 = yk [1 + sin(0.7xk )] 1.2 |xk |
yk+1 = 0.21 xk
(13.15)
starting with x0 = y0 = 0. Write a program to plot the picture of the individual
points.
0.8
0.6
0.4
0.2
0
0
0.5
1.5
Page 292
293
3. The Sierpi
nski triangle is a fractal. There are various ways to compute the Sierpi
nski
triangle. The method described below is called an iterated fractal system and starts
with a point at the origin x0 = 0 and y0 = 0. The next xn+1 and yn+1 points for
n = 0, 1, 2, . . . N are then computed by randomly selecting one of the three equations
below. Each equation has equal probability to be selected:
xn+1 = 0.5xn and yn+1 = 0.5yn with the point plotted in yellow.
xn+1 = 0.5xn + 0.5 and yn+1 = 0.5yn + 0.5 with the point plotted in red.
xn+1 = 0.5xn + 1 and yn+1 = 0.5yn with the point plotted in blue.
The program must ask the user to enter the number of points N required. Each
point is plotted as a dot without any lines connecting the points.
Figure 13.13 is an example of the Sierpi
nski triangle using 5000 points. To increase
the speed of the plotting only plot once all the points have been generated. To do
so you would have to store the points for each colour in seperate vectors.
101
10
x0.5
x0.5
x0.5
6
0
10
10
-1
-1
10
10-2
10-1
100
101
102
10
10-2
10-1
100
101
102
20
40
60
80
100
1000
1
cos
0.4
0.5
cos(x)
Frequency
0.2
500
-0.2
-0.5
-0.4
0
-1
0
0.2
0.4
0.6
bins
0.8
-5
0
x
-0.4
-0.2
0.2
0.4
(a) Loglog plot of x with x between 102 and 102 using 10 points. Use logspace
to generate 10 equally spaced points on a log scale. Add appropriate labels and
a figure title. Use the line style (dash-dot), line color (blue) and marker type
(circle) as displayed.
Page 293
294
(b) Semilogx plot of x with x between 102 and 102 using 10 points. Use
logspace to generate 10 equally spaced points on a log scale. Add appropriate labels and a figure title. Use the line style (dot-dot), line color (green)
and marker type (upwards pointing triangle) as displayed.
(c) Semilogy plot of x with x between 102 and 102 using 10 points. Use
linspace to generate 10 equally spaced points on a linear scale. Add appropriate labels and a figure title. Use the line style (solid line), line color
(magenta) and marker type (square) as displayed.
(d) Histogram plot of the product of two uniform random numbers r1 and r2 between 0 and 1 using 10000 product pairs r1 r2 . Use 50 bins for your histogram
plot and add appropriate labels and a title.
(e) Symbolic plot of cos(x) between 2 and 2. Scale the axis between 2 and
2 using the axis command. Use the line style (dash-dash), line color (red)
and marker type (none) as displayed.
(f) Polar plot of sin(2x)cos(2x) for x between 0 and 2 in increments of 0.01. Add
appropriate labels and a figure title. Use the line style (dash-dot), line color
(magenta) and marker type (none) as displayed.
xk+1 = i(x2+i
- x+i)
k
-1
0
295
(13.16)
sin(x)
x
x-x3/3!
x-x3/3!+x5/5!
x-x3/3!+x5/5!-x7/7!
-5
x-x3/3!+x5/5!-x7/7!+x9/9!
-5
+
(13.17)
3!
5!
7!
9!
Generate Figure 13.16 with the same color scheme as indicated and place the legend
in the same location. Use axis to define the x and y boundaries of the figure.
sin(x) x
Page 295
296
Note how the accuracy of the Taylor approximation improves as the number of terms
increase.
7. A potentional energy function is given by
h
x i2
Vm (x) = V0 1 exp
where we choose V0 = = 1.
A quadratic approximation for the above is
Vn2 (x) = a2 x2
and the eight-order approximation is
Vn8 (x) =
8
X
i=0
ai x i = a0 + a1 x + a2 x + + a8 x
= 1.015 104
= 0.995
= 0.611
= 0.061
= 5.249 104
a1
a3
a5
a7
= 0.007
= 1.025
= 0.243
= 0.009
(13.18)
(13.19)
(13.20)
(13.21)
(13.22)
Write a program plot the quadratic and 8th-order approximations on the same
axis. Use appropriate label descriptions, line styles and function descriptions in the
legend.
8. Write a program that generates 1000 points (x, y) of the fractal fern picture (FFP).
The FFP consists of two series x1 , x2 , . . . , xn and y1 , y2 , . . . , yn . The starting values
of the two series are x1 = 0 and y1 = 0. During each of the 1000 iterations, one of
the following four equations is randomly selected for generation of the next point
(xn+1 , yn+1 )
A
1
100
7
100
chance to choose
xn+1 = 0
yn+1 = 0.16yn
(13.23)
(13.24)
(13.25)
(13.26)
chance to choose
Page 296
7
100
85
100
297
chance to choose
xn+1 = 0.15xn + 0.28yn
yn+1 = 0.26xn + 0.24yn + 0.44
(13.27)
(13.28)
(13.29)
(13.30)
chance to choose
n=1
X
bn sin(nx)
f (x)sin(nx)dx, n = 1, 2, 3, . . .
Plot the Fourier approximations for n = 1, n = 2 and n = 3 on the same figure with
x between and using 50 equally spaced points. Plot each of the graphs with
a different line style or line colour and add an appropriate legend. Appropriately
label the x-axis and y-axis and add a title for the figure.
[Hint: You must use numpys numerical integration function quad to compute the
constants bn ]
Page 297
298
(13.31)
dy(t)
= vy (t)
dt
where (t) indicates a dependency on time in years (yr) and Gm = 4 2 in units
AU 3
. One AU is the yr distance from the earth to the sun, also known as the
yr2
astronomical unit. Compute the position (x, y) and velocity (vx , vy ) of earth for 1
year in increments of 0.01 years. The initial conditions are x(0) = 1 AU, y(0) = 0
AU, vx (0) = 0 AU/yr and vy (0) = 2 AU/yr.
Plot the x and y positions as well as the vx and vy velocities as functions of time
on two separate plots on the same figure. The first plot must depict the x and y
positions while the second plot must depict the vx and vy velocities. Give appropriate
annotations for the x-axis, y-axis, title and line descriptors of each plot.
[Hint: You can use scipy.integrates function odeint to solve the system of
ordinary differential equations]
Page 298
Chapter 14
Exception Handling
14.1
Before we jump into exception handling lets first consider where we would possibly need
to handle exceptions. The one scenario that jumps to my mind is in the case of user
input. We often expect the user to input a certain answer (like a number), but what
happens when the user enters some unexpected value? In these case we need to handle
any exception in our program, should the user have entered the incorrect value. Lets start
by developing a generic user menu.
Whenever the user of one of your program needs to choose between a number of
options, you can create a little menu that is printed to the screen. So far we have utilised
the print and raw input statements in such cases. Examples include the numerical
differentiation program where the user could choose between the forward, backward and
central difference method, or the numerical integration program where the user could
choose between the left-point, right-point, mid-point or trapezium methods.
Let us look at how we can program a generic menu function that we can use in any
program. Firstly we need to identify the inputs and outputs for this functions. For the
inputs I chose the title sting of the menu and a list of string options the user can chose
from. For the outputs I chose to return a numerical representation of the option number
selected.
Page 299
300
Inputs:
title: title of the menu - string
options: list of menu options - [str, str, str, ...]
Returns:
user_input: the users selections as an integer
1 = first option
2 = second option
etc.
"""
line = "-" * len(title)
n_opts = len(options)
print title
print line
for cnt, option in enumerate(options):
print "[%2d] " % (cnt+1) + option
This statement automatically generates a text menu with the specified title at the top
and all the options listed below. The user then selects a number, if the first option is
selected, the output name(user input in this example) is assigned a value of 1. Similarly,
the second choice assign the value 2 to the output name and so on.
Ill now illustrate the use of the menu command by plotting the function f (x) = sin(x)
for x [0; 2]. The line colour will be chosen by the user, using the menu command.
Just to make things interesting, Ill let the program repeat until the user selects the Exit
option. This is achieved by using a while loop. The Python program follows:
Page 300
301
if choice == 4:
break
line_col = "k"
if choice == 1:
line_col = "r"
if choice == 2:
line_col = "g"
if choice == 3:
line_col = "b"
figure(1)
plot(x, y, line_col)
axis([0, 2*pi, -1.1, 1.1])
show()
The text menu that is generated by the menu command in program line 9 is given
by:
Choose a colour:
---------------[ 1] Red
[ 2] Green
[ 3] Blue
[ 4] Exit
14.2
In the example above, if the user had entered any integer value the program would have
work perfectly, however if the user entered a floating point number or a string then the
program would have crashed giving a trace back similar to the following:
Page 301
302
This error occurs in line 25 of the menu function when Python tries to convert the users
input into an integer object. Secondly if the user had entered any integer greater than
4 the program also would have run, but any number greater than 4 is not a valid menu
option. The following revision of the menu function, we created earlier, uses exception
handling and additional logic to account for these unexpected inputs from the user
Inputs:
title: title of the menu - string
options: list of menu options - [str, str, str, ...]
Returns:
user_input: the users selections as an integer
1 = first option
2 = second option
etc.
"""
while True:
line = "-" * len(title)
n_opts = len(options)
print title
print line
for cnt, option in enumerate(options):
print "[%2d] " % (cnt+1) + option
303
Program exceptions are handled using the try: and then except: commands as
shown in lines 2734. Python first attempts to execute the code in the try: statement
(lines 2832), if any error is found in this code (lines 2832) then Python immediately
jumps to line 33 and executes the except: statement. If the error encountered in lines 28
32 is in the tuple (given in the except statement then the code in the except statement
is executed (line 34) otherwise Python will give a trace back with the error type found.
This logic is coupled with a while loop statement to ensure the program continues to
execute until the user has made a valid selection.
Page 303
304
Page 304
Chapter 15
More ways to loop
15.1
We can also loop using functions instead of for loop or while loop statements. The
answer lies in creating a function where the output of the function requires the function
itself to be evaluated again. How do we then stop? To stop we just have to make the
output equal to something (e.g. some constant or value) which does not involve the
function itself to be evaluated. To achieve this the function will have to make a decision
on whether the output calls the function itself (when we want to loop) or whether the
output is equal to some constant (when we want to stop).
How would we go about computing the factorial of a number n. We could write it out
as n! = n (n 1)! = n (n 1) (n 2)!, keeping in mind that 0! = 1. Let us see what
happens when we write a function my fact that takes a number n as input and returns as
output the number n multiplied with my fact(n-1) unless n = 0 then the output must
equal 1. The my fact(n-1) with which we multiply n becomes n 1 multiplied with
my fact(n-2) similarly to the mathematical expansion given above. Given below is the
function my fact which computes the factorial of a number recursively,
def my_fact(n):
if n == 0:
return 1
return n * my_fact(n-1)
print my_fact(4)
Page 305
306
Let us see if we can add the n integers using recursive functions given by
n
X
i=1
=n+
n1
X
i=1
= n + (n 1) +
n2
X
(15.1)
i=1
We require a function of which the output is to equal the sum of the input value of
the function and the function itself called with 1 less the input value, unless the input
value is zero then the output must also be zero.
def my_sum(num):
if num == 0:
return 0
return num + my_sum(num-1)
print my_sum(4)
We could also make use of lists for instance if we want to generate a list storing the
sum of the integers, as shown below
my_list = []
sum_int = my_sum(4, my_list)
print
print sum_int
Look how the list store list grows inside the function. Run it and see how store list
grows from an empty list to [0, 1, 3, 6, 10]. What will happen if we change line 6 from
store list.append(total sum) to store list.insert(0, total sum)? Do it and see
how the output changes when you type myvec(5) from
Page 306
[0,
[0,
[0,
[0,
[0,
1]
1,
1,
1,
1,
307
3]
3, 6]
3, 6, 10]
3, 6, 10, 15]
15
to
[1, 0]
[3, 1, 0]
[6, 3, 1, 0]
[10, 6, 3, 1, 0]
[15, 10, 6, 3, 1, 0]
15
Let us consider another recursive example. Remember the bisection method which we
used to find the root of a function. I list the method below to refresh our memory.
Recall that we have to start with a function f (x) as well as an interval x [xl , xu ] such
that the function is either negative at xl and positive at xu or positive at xl and negative
at xu which can be written as f (xl ) < 0 and f (xu ) > 0, or f (xl ) > 0 and f (xu ) < 0. xl
refers to the lower bound of the interval and xu refers to the upper bound of the interval.
If the function is continuous and it changes sign somewhere between xl and xu there
must be a point x between xl and xu where f (x) = 0. The aim is to find f (x) = 0,
we do so by dividing the interval [xl , xu ] into two equal parts by inserting a midpoint
xm = 21 (xl + xu ). The function is also evaluated at this midpoint xm = 12 (xl + xu ). All
that we have to do now is to decide in which of the two new intervals the solution lies.
Either the solution lies between the lower bound xl and the midpoint xm , or the solution
lies between the midpoint xm and the upper bound xu . We repeat this operation until the
absolute difference between xl and xu is less than a specified tolerance and then return
the midpoint of xl and xu .
The inputs required for our bisection function are therefore xl ,xu , the function f and
a tolerance. To write a recursive function that will use the bisection method to find a zero
of f we need to identify when to stop. As stated we above we stop when our interval is
very small otherwise we continue to bisect our interval. This is exactly what the recursive
example computes,
Page 307
308
The function bisection takes has the four required inputs. We then compute the
midpoint. We then check whether our interval distance is less than the user specified
tolerance. If it is we return our computed midpoint which is just a scalar. If it is still
bigger than the user specified tolerance, we determine in which interval the sign change
occurs and bisect that interval using our function bisection.
Let us compute the root of cos(x) using our function bisection with an initial interval
between 0 and ,
x =
1.5707967013
Note how I used an anonoumys function to send cos(x) as an input argument to the
function bisection.
15.2
Exercises
1. Write a function fib that takes an integer N as input. The function must then
compute the N th term of the Fibonacci sequence using recurssion. The Fibonacci
sequence is given by
0, 1, 1, 2, 3, 5, 8, 13, 21, . . .
[Hint: The definition of the Fibonacci sequence is as follows, the first term N = 1
is defined as 0 and the second term N = 2 is defined as 1. The N th Fibonacci term
Page 308
309
fib(N) is defined as the sum of the (N 1)th term fib(N-1) plus the (N 2)th
term fib(N-2).]
Why is the recursive version of computing the Fibonacci sequence so slow. Consider
as an example N = 25? Can you explain why?
Page 309
310
Page 310
Chapter 16
numpy and scipy capabilities
16.1
16.1.1
Recall that we have already solved a linear system of equations Ax = b using the Gauss
elimination. We were able to solve an arbitrary N N system with our Gauss elimination
function. Before we were able to solve a linear system of equations we had to write
Gauss elimination from scratch. We started by figuring out the logic of the forward
reduction step and then by coding it in Python. Thereafter we figured out the logic of
the back substitution step, coding it and only then we were able to solve a linear system
of equations.
Well, numpy.linalg has the built in function solve that enables us to solve a linear
system of equations in a very fast and efficient way. Recall the system
2 3 4 1
x1
10
1 1 2 1 x2 5
(16.1)
2 4 5 2 x3 = 13
1 2 3 4
x4
10
of which the solution is
x1
x2
x=
=
x3
x4
1
1
1
1
(16.2)
Let us define matrix A and vector b in Python and use numpy.linalg.solve to solve
Page 311
312
mat_A = array([[2,
[1,
[2,
[1,
3,
1,
4,
2,
4,
2,
5,
3,
1],
1],
2],
4]])
x =
[[ 1.]
[ 1.]
[ 1.]
[ 1.]]
16.1.2
2 3 4
1 1 2
2 4 5
1 2 3
4 3 1
10
x1
x2
13
=
x3
10
x4
Page 312
(16.3)
313
that has five equations and only four unknowns. Because there are too many equations
and too few variables to satisfy each equation exactly, we only satisfy the equations
approximately Ax b, resulting in an error. To minimize the error a least squares
method is used, which is given by
x = (AT A)1 AT b
This overdetermined system can be solved using the numpy.linalg.lstsq function:
mat_A = array([[2,
[1,
[2,
[1,
[4,
3,
1,
4,
2,
3,
4,
2,
5,
3,
1,
1],
1],
2],
4],
5]])
x =
[[-0.09529277]
[ 0.46268657]
[ 1.96440873]
[ 0.80711825]]
[[ 9.86222732]
[ 5.10332951]
[ 13.09644087]
[ 9.95177956]
[ 7.00688863]]
Page 313
314
We can see there is an error when we multiply Ax we do not obtain the right hand
side vector b exactly but only approximately as shown above,
16.1.3
Let us see what happens when is a A M N system, with M < N . The linear system
of equations is then referred to as underdetermined as there are too few equations for the
number of unknowns. For example if we remove the equation
x1 + 2x2 + 3x3 + 4x4 = 10
of the system given in (16.1) we obtain the following underdetermined system of linear
equations
x1
2 3 4 1
10
1 1 2 1 x2
5
=
(16.4)
x3
2 4 5 2
13
x4
that has only three equations and four unknowns. Because there are too few equations
and too many variables, we have an infinite number of solutions each of which satisfy the
equations exactly. How do we distinguish between the infinite number of solutions to
obtain an unique solution? When we apply the following least squares method
x = AT (AAT )1 b
to an underdetermined system of equation to obtain the solution of x that minimises
the length (norm) of x.
numpy.linalg.lstsq computes the minimum norm solution of the underdetermined
system
Page 314
315
x =
[[ 0.6]
[ 0.8]
[ 1.4]
[ 0.8]]
[[ 10.]
[ 5.]
[ 13.]]
We can see that Ax satisfies the right hand side vector b exactly as shown above. To
show you that other solution exist, consider one of the infinite solutions
1
x1
x2
0
=
(16.5)
x=
x3
x4
0
which also solves the system exactly, as shown below
316
[[10]
[ 5]
[13]]
16.1.4
To solve non-linear systems of equations we are required to iterate on the solution until
our solution is within a specified accuracy. For example consider the equation below,
sin(x) + cos(x) = 1.25
(16.6)
Which values of x would satisfy (16.6)? Are there more than one x that would satisfy
(16.6)? How could we go about finding an x that would satisfy (16.6)? We could rewrite
(16.6) by subtracting 1.25 on both sides,
sin(x) + cos(x) 1.25 = 0
(16.7)
and find a root of (16.7). Recall the bisection and Newtons method which we used to
find roots. But instead of writing our own program to find a root of (16.7), we are going
to use the function scipy.optimize.fsolve. Type help(fsolve) (after importing it)
and the following is displayed,
.
.
.
Find the roots of a function.
Parameters
---------func : callable f(x, *args)
A function that takes at least one (possibly vector) argument.
x0 : ndarray
The starting estimate for the roots of func(x) = 0.
args : tuple
Any extra arguments to func.
fprime : callable(x)
Page 316
317
Returns
------x : ndarray
The solution (or the result of the last iteration for
an unsuccessful call).
infodict : dict
A dictionary of optional outputs with the keys::
*
*
*
*
nfev:
njev:
fvec:
fjac:
ier : int
An integer flag. Set to 1 if a solution was found, otherwise refer
to mesg for more information.
mesg : str
If no solution is found, mesg details the cause of failure.
.
.
.
def my_func(x):
return sin(x) + cos(x) - 1.25
Page 317
318
def main():
x_start = 0
solution = fsolve(my_func, x_start)
print "root = ", solution[0]
print "f(x) = ", my_func(solution[0])
if __name__ == "__main__":
main()
To use fsolve we need to define a function f (x). The function fsolve, solves f (x)
such that f (x) = 0 i.e. find a root of f (x). I create a function myf unc of (16.7) in
program lines 56. Before we can find a root of (16.7), we need to guess a starting point,
let us guess x0 = 0 (line 10) and use fsolve to find x (line 11). The following output is
displayed
root =
f(x) =
0.298703208323
-3.5527136788e-15
which shows that f (x) with x = 0.29870 satisfies the equation f (x) = 0. Let us re-run
this example with an initial guess x0 = 1, program line 10 changes to x start = 1 and
we then obtain
root =
f(x) =
1.27209311847
-6.66133814775e-16
which is another root of (16.7) and as shown x = 1.2721 satisfies (16.7). In the
examples above we only specified a function f (x) and starting point x0 when using fsolve.
The function fsolve uses the derivative of f (x) to help it locate a root of the equation.
When the derivative of f (x) is not specified it is computed numerically. However, we
(x)
together with the function f (x). Consider the example
can specify the derivative dfdx
below
Page 318
319
def my_func(x):
return sin(x) + cos(x) - 1.25
def deriv_func(x):
return [cos(x) -sin(x)]
def main():
x_start = 1
solution = fsolve(my_func, x_start, fprime=deriv_func)
print "root = ", solution[0]
print "f(x) = ", my_func(solution[0])
if __name__ == "__main__":
main()
which gives
root =
f(x) =
1.27209311847
-8.881784197e-16
Up to now, when dealing with non-linear systems, we only solved a single equation
that depends on a single variable. But can we solve a system of equations i.e. many
non-linear equations where each non-linear equation may depend on many variables. The
function fsolve also allows us to solve systems of non-linear equations F (x) = 0,
F1 (x1 , x2 , . . . , xn )
0
F2 (x1 , x2 , . . . , xn ) 0
f (x) = ..
(16.8)
= .. = 0
.
.
Fn (x1 , x2 , . . . , xn )
0
where each Fi , i = 1, 2, . . . , n corresponds to an equation. For example consider the
following non-linear system of n = 2 equations
x21 + cos(x2 ) = 3
sin(x1 ) x2 = 2
The system is non-linear since x1 and x2 is a function of sin, cos, square root and
squares. We cant write the system as a constant matrix A multiplied with a vector of x
Page 319
320
as we did for solving linear systems. To use fsolve we have to rewrite the above system
x21 + cos(x2 ) 3 = 0
sin(x1 ) x2 + 2 = 0
such that each equation is equal to zero. We can then find a root of the n = 2 system
of equations. The function fsolve requires the system to be written in a vector form
where each component of the vector corresponds to an equation. We have to construct a
function that takes a vector x as input and returns a vector F (x) as output.
def my_func(x):
return [x[0]**2 + cos(x[1]) - 3,
sin(x[0]) - x[1]**0.5 + 2]
def main():
x_start = [1, 2]
solution = fsolve(my_func, x_start)
print "root = ", solution
print "f(x) = ", my_func(solution)
if __name__ == "__main__":
main()
Our initial guess therefore also needs to be a vector as we have to guess an initial
value for both x1 and x2 . We can then use fsolve to solve for the vector valued function
using our initial guess vector. The function fsolve will then return a vector containing
the solution x .
The initial guess will determine the time fsolve takes to solve the non-linear system
of equations and if multiple solutions are present it will determine to which solution you
converge. Let us start with an initial guess x = [1; 2] and see what solution we obtain
when we use fsolve
root =
f(x) =
[ 1.92420943 8.63300101]
[-8.0447204453548693e-11, -8.2178708282754087e-12]
Page 320
321
Let us change our starting position to x = [0.5; 1] and see if we can find another
solution to the system of equations. Program line 11 changes to x start = [0.5, 1]
and the output is:
On the way to the solution fsolve encountered had to evaluate the square root of
negative values as stated in the warning message and could not solve out system of
equations starting from this given starting point.
As with the solution of single non-linear equation we can also specify the derivative
(Jacobian) of the system w.r.t. each of the variables xi , i = 1, 2, . . . , n i.e. n n matrix
of equations. Type help fsolve for additional help on computing the Jacobian.
16.1.5
F4
Spring A
Spring B
Spring C
Spring D
Figure 16.1: Four spring connected in series with a force F4 applied at the right hand
side.
Our aim is to find the positions of points 1, 2, 3 and 4 as shown in Figure 16.1 when
the system is in equilibrium after applying F4 . Normally springs are modelled as linear
springs i.e. the force F displacement x relationship is given by
F = kx
where k is a constant. However, in our case the springs are non-linear and the force
F displacement x relationship is given by
F = k(sign(x)x2 )
Page 321
322
where k is a constant and sign(x) is +1 when x > 0, 1 when x < 0 and 0 when x = 0.
The sign is required to distinguish between tension and compression since x2 is always
bigger or equal to 0 for x a real number. Therefore x2 does not allow us to distinguish
between tension and compression of the spring.
As the spring stretches you need to pull much harder than is the case with the linear
spring. For the linear spring you need to pull twice as hard 2F when you want to double
the stretch distance 2x. For the non-linear spring you need to pull 4 times as hard 4F
when you want to double the stretch distance 2x. Let us write down the equilibrium
equations of each of the four points of which we want to know the displacement after the
force F4 is applied.
When F4 is equal to zero and the system is in equilibrium we consider the displacements
ui , i = 1, 2, 3, 4 of the four points 1,2,3 and 4 to be zero i.e. our reference configuration.
When we move point 1 to the right (that is u1 is positive) then spring A pulls it to the
left FA = kA (sign(u1 )(u1 )2 ). The change in length of spring B is u2 u1 which pushes
point 1 to the left when u1 > u2 and pulls point 1 to the right when u1 < u2 . The force
of spring B on point 1 is given by FB = kB ((u2 u1 ) + sign(u2 u1 )(u2 u1 )2 ). Since we
want to determine the displacements of the positions when the system is in equilibrium
the sum of the forces at point 1 must be equal to zero
FA + FB = 0
to give
kA (sign(u1 )(u1 )2 ) + kB (sign(u2 u1 )(u2 u1 )2 ) = 0
Let us consider point 2. When u2 > u1 spring B pulls point 2 to the left. The the
length of spring B is given by u2 u1 to give FB = kB (sign(u2 u1 )(u2 u1 )2 ). Similarly,
the length of spring C is given by u3 u2 . When u3 > u2 then spring C pulls point 2 to
the right. The force is therefore given by FC = kC (sign(u3 u2 )(u3 u2 )2 ). Again, the
sum of the forces at point 2 must be equal to zero
FB + FC = 0
to give
kB (sign(u2 u1 )(u2 u1 )2 ) + kC (sign(u3 u2 )(u3 u2 )2 ) = 0
Similarly, the forces at point 3 is given by
FC + FD = 0
Page 322
323
to give
kC (sign(u3 u2 )(u3 u2 )2 ) + kD (sign(u4 u3 )(u4 u3 )2 ) = 0
and lastly the sum of the forces at point 4 are the force in spring D and the external
force F applied to the system,
FD + F = 0
to give
kD (sign(u4 u3 )(u4 u3 )2 ) + F4 = 0
Let us consider a system where kA = kB = kC = kD = 1 with an external force F4 = 1
which is represented in the function spring system below
def sign_x(disp):
"""
returns the sign of disp multiplied by disp^2
"""
if disp > 0:
return disp**2
elif disp < 0:
return -(disp**2)
else:
return 0.0
def spring_system(disp):
system = []
force = 1.0
k_vals = (1.0, 1.5, 0.5, 3.0)
system.append(-k_vals[0] * sign_x(disp[0]) + \
k_vals[1] * sign_x(disp[1] - disp[0]))
324
def main():
x_start = [0., 1., 0., 1.]
solution = fsolve(spring_system, x_start)
print "root = ", solution
print "f(x) = ", spring_system(solution)
if __name__ == "__main__":
main()
note the use of the function sign x which returns +x2 when the input is positive, x 2
for negative input and 0 when the input to sign x is zero.
Let us solve the system using an initial guess of u = [0 1 0 1]
root =
f(x) =
[ 1.
1.81649658 3.23071014 3.80806041]
[1.1025402812947505e-11, -1.2604928212311961e-10,
2.8097080218003612e-11, 8.1849083066742878e-11]
and when we set the external force F4 = 4 in springsystem we double the displacement
as expected,
root =
f(x) =
[ 2.
3.63299317 6.46142029 7.61612083]
[1.447645381347229e-08, -3.1230538155568865e-08,
4.3613482603177545e-08, -2.9053676797730077e-08]
using an initial guess of u = [0 1 0 1]. From the above results the solution to the
system seems trivial, since all the springs see the same force F4 we applied at the tip of
the system. Due to the simple load case superposition holds and we could have computed
the displacement of one spring and the rest follows from supper position. For example,
the first spring extends by 1, then the second spring also extends by 1 plus the extension
of the previous springs etc.
Page 324
325
y
x
F2
F4
Spring A
Spring B
Spring C
Spring D
Figure 16.2: Four spring connected in series with two applied forces F2 and F4 .
However, if we consider a system an additional force F2 to the system as shown in
Figure 16.2.
The system is given below,
def sign_x(disp):
"""
returns the sign of disp multiplied by disp^2
"""
if disp > 0:
return disp**2
elif disp < 0:
return -(disp**2)
else:
return 0.0
def spring_system(disp):
system = []
force2 = 1.0
force4 = 1.0
k_vals = (1.0, 1.0, 1.0, 1.0)
system.append(-k_vals[0] * sign_x(disp[0]) + \
k_vals[1] * sign_x(disp[1] - disp[0]))
326
return system
def main():
x_start = [0., 1., 0, 1.]
solution = fsolve(spring_system, x_start)
print "root = ", solution
print "f(x) = ", spring_system(solution)
if __name__ == "__main__":
main()
with F2 added to the second equation of our system system(2). Let us solve the
system with initial guess u = [0 1 0 1]
root =
f(x) =
16.2
Polynomials in Python
To represent polynomials in Python we already have various ways at our disposal. For
example, to define the polynomial x4 + 3x2 + 1 in Python we can create a function
def my_poly(x):
return x**4 + 3 * x**2 + 1
327
help(poly)
.
.
.
Objects for dealing with polynomials.
Constants
--------- polydomain -- Polynomial default domain, [-1,1].
- polyzero -- (Coefficients of the) "zero polynomial."
- polyone -- (Coefficients of the) constant polynomial 1.
- polyx -- (Coefficients of the) identity map polynomial, f(x) = x.
Arithmetic
---------- polyadd
- polysub
- polymul
- polydiv
- polypow
- polyval
-------
Calculus
-------- polyder -- differentiate a polynomial.
- polyint -- integrate a polynomial.
Misc Functions
-------------- polyfromroots -- create a polynomial with specified roots.
Page 327
.
.
.
328
.
.
.
Evaluate a polynomial.
Parameters
---------x : array_like, ring_like
If x is a list or tuple, it is converted to an ndarray. Otherwise
it must support addition and multiplication with itself and the
elements of cs.
cs : array_like
1-d array of Chebyshev coefficients ordered from low to high.
Returns
------values : ndarray
The return array has the same shape as x.
.
.
.
Page 328
329
poly_coeffs = [1, 0, 3, 0, 1]
We need to remember to include the coefficients lower than the non-zero coefficient
of the highest order of x that are zero. For example 0x3 and 0x are included since the
highest order polynomial that is non-zero is x4 .
If we had forgotten to include the zeros of the third and first order polynomials and
we defined poly coeffs as [1, 3, 1] we would have obtained the polynomial 1 + 3x + x2
which is only a second order polynomial. Therefore the length of our coefficient vector
minus one is the highest order of the polynomial.
The function polyval is then handy, it takes two input arguments. Firstly, a vector
containing the x values where to evaluate the polynomial and secondly a polynomial
coefficient vector:
[ 0.-1.61803399j
0.-0.61803399j
Page 329
330
0.+0.61803399j
0.+1.61803399j]
We can compute the product of two polynomials by using polymul which stands for
polynomial multiple. The function polymul takes two polynomial coefficient vectors as
input and computes the product of the polynomials which it returns as output. Therefore,
if we want to multiply x 1 with x + 1 to obtain x2 1 we can simply type
[ -1.,
0., 1.]
To factor x1 out of x2 1 we can use the polydiv function which computes the quotient
and also the remainder. Therefore
gives
(array([ 1.,
331
we then obtain
(array([-4.,
.
.
.
Least-squares fit of a polynomial to data.
Parameters
---------x : array_like, shape (M,)
x-coordinates of the M sample (data) points (x[i], y[i]).
y : array_like, shape (M,) or (M, K)
y-coordinates of the sample points. Several sets of sample points
sharing the same x-coordinates can be (independently) fit with one
call to polyfit by passing in for y a 2-d array that contains
one data set per column.
deg : int
Degree of the polynomial(s) to be fit.
.
Page 331
332
.
.
The function polyfit allows you to specify the order (degree) of the polynomial that
you would like to fit through the data. The function polyfit then returns the coefficients
of the polynomial in a vector e.g. when polyfit returns the vector [1 0 2 4 0 2] then
it represents the following 5th order polynomial
y = 2x5 + 0x4 + 4x3 + 2x2 + 0x + 1
(16.9)
0.5
0.4
1.4
2.1
2.0
4.2
x=
y=
2.7
7.1
3.2
10.4
def main():
x_vals = [0.5, 1.4, 2.0, 2.7, 3.2]
y_vals = [0.4, 2.1, 4.2, 7.1, 10.4]
plot.show()
Page 332
(16.10)
333
if __name__ == "__main__":
main()
I start off by define the vectors in lines 7 and 8. Then I fit a linear line (order 1)
through the data (program line 10) as well as a quadratic polynomial (order 2)(program
line 11). I then use polyval to evaluate the each of the polynomials at various values of
x. I used 50 linearly spaced x values between 0 and 5 to evaluate each of the polynomials.
I then plot the data as well as our linear and quadratic polynomials.
30
Data
Linear
Quadratic
25
20
15
10
5
0
50
Figure 16.3: Experimental data fitted with a linear and a quadratic polynomial.
To compute the derivative of a polynomial we simply use polyder which takes a
polynomial as input and return the derivative of a polynomial. For example, the derivative
of 2x4 + 0.5x2 x + 3 is 8x3 + x 1 which we can compute as follows
334
to give
[-1.
1.
0.
8.]
In turn, we can also integrate a polynomial by simply using polyint. The integral of
2x + 0.5x2 x + 3 is 25 x5 + 16x3 12 x2 + 3x + c where c is the integration constant. The
function polyint has two inputs. The first input is the polynomial we want to integrate
and the second input specifies the integration constant c (optional). For example we
compute the integral of 2x4 + 0.5x2 x + 3 with an integration constant of c = 4 as
follows
4
to give
[ 4.
16.3
3.
-0.5
0.16666667
0.
0.4 ]
Numerical integration
Recall that we have written our own function to numerically compute the integral of a
function, remember the left-point, right-point and midpoint methods. We divided the
area under a curve into rectangular section which we summed together. Well, Python has
its own built in function to integrate an arbitrary function of one variable, namely quad.
For instance how would we go about integrating cos(x) between 0 and with quad. Let
us start by typing from scipy.integrate import quad and then help(quad) to see the
following
.
.
.
Compute a definite integral.
Page 334
335
Parameters
----------
func : function
A Python function or method to integrate.
a : float
Lower limit of integration (use -scipy.integrate.Inf for -infinity).
b : float
Upper limit of integration (use scipy.integrate.Inf for +infinity).
args : tuple, optional
extra arguments to pass to func
full_output : int
Non-zero to return a dictionary of integration information.
If non-zero, warning messages are also suppressed and the
message is appended to the output tuple.
Returns
-------
y : float
The integral of func from a to b.
abserr : float
an estimate of the absolute error in the result.
infodict : dict
a dictionary containing additional information.
Run scipy.integrate.quad_explain() for more information.
message :
a convergence message.
explain :
appended only with cos or sin weighting and infinite
integration limits, it contains an explanation of the codes in
infodict[ierlst]
.
Page 335
336
.
.
Therefore we have to specify cos(x) as an function object, as well as the lower and
upper bound over which we want to compute the numerical integral. The following
example integrates cos(x) between 0 and ,
4.9225526349740854e-17
which is correct from inspection. Now that we understand how quad works we can
compute a more challenging integral. Consider for example the integral of
Z 5 x3
e
dx
(16.11)
2 ln(x)
which we can compute using quad. In the example below I define the function that
we want to compute (16.11)
def func(x):
return exp(x-3) / log(x)
def main():
print quad(func, 2, 5)
Page 336
337
if __name__ == "__main__":
main()
to give
5.114501864734196
from
from
from
from
ex3
ln(x)
def func(x):
return exp(x-3) / log(x)
def main():
print quad(func, 2, 5)
x = linspace(2, 5, 100)
plot.plot(x, func(x))
plot.show()
if __name__ == "__main__":
main()
Form Figure 16.4 we can approximate the area under the curve by the area of a
triangle with base of 3 and height of 4. From Figure 16.4 it is clear that the area under
the actual curve is less than the area of the triangle. The area of the triangle works out
to be 12 base height = 12 3 4 = 6.
Page 337
338
5.0
4.5
4.0
3.5
3.0
2.5
2.0
1.5
1.0
0.5
2.0
2.5
3.0
3.5
16.4
4.5
4.0
ex3
ln(x)
5.0
between 2 and 5.
SPRING
MASS
f(t)
DAMPER
x
Figure 16.5: Spring-mass-damper system of a mass that move frictionless only in the
x-direction.
which contains a mass m. Imagine that the mass m can only move in the x-direction,
frictionless. The position of the mass m is given by x. Connected to the mass m is a
spring and a damper which in turn is connected to a wall. There is some applied force
f (t) applied to the mass that can vary with time. If the mass is moving around then the
position x is a function of time t therefore x(t). Our aim is to determine the position x(t)
of the mass m for various values of t. But before we solve the system let us look at the
mathematical model that describes the position of the mass m.
Page 338
339
Let us start by considering the system in equilibrium with no external force f (t) = 0
applied and let our coordinate axis start at that point i.e. x is zero at that point. If we
move the mass to the right (in the positive x-direction) then the spring wants to pull the
mass back (exerts a force to the left) to equilibrium. When we move the mass to the left
(in the negative x-direction) then the spring wants to push the mass (exerts a force to
the right) to equilibrium. We see that displacing the mass m in one direction results in
a force on the mass in the opposite direction. We can therefore see that the force Fs the
spring exerts on the mass is given by
Fs (t) = kx(t)
(16.12)
where k is the spring stiffness given in Newton per meter. Damping is a force that
is proportional to the velocity of a mass m and works in the opposite direction of the
velocity v(t) at which a mass m is travelling. Think of closing the boot of your car or
pushing and pulling on a door with a damper at the top. Let us use a linear damper where
the damping force Fd is linearly proportional to the velocity by some damping coefficient
c. The damping force Fd can then be written as
Fd (t) = cv(t) = cx(t)
(16.13)
where x(t)
is the time derivative of position which is simply the velocity v(t) of the
mass.
The external forces on the mass are the force exerted by the spring Fs , the force
exerted by the damper Fd and also the time varying force f (t) applied to the the mass
whichPwe made equal to zero in the discussion above. If we now apply Newtons second
law,
F = dtd (mv) which states that the sum of the external forces F is equal to the
change of momentum dtd (mv) of the
Psystem. But if the mass remains constant then the
equation becomes the well known
F = ma = m
x. Applying Newtons second law to
our mass and summing the forces in the x-direction we obtain
Fs (t) + Fd (t) + f (t) = kx(t) cv(t) + f (t) = ma(t)
(16.14)
which we can rewrite by taking the spring force and damping force to the right hand
side to obtain
f (t) = ma(t) + cv(t) + kx(t) = m
x(t) + cx(t)
+ kx(t)
(16.15)
Let us consider a spring mass damper system given by the following second order
differential equation (second order since the second time derivative of x(t) occurs in the
differential equation)
m
x(t) + cx(t)
+ kx(t) = f (t)
(16.16)
Page 339
340
is the velocity with damping coefficient c and x(t) is the position of mass m relative to the free length of the spring with
a spring constant of k. A time varying force f (t) is applied to mass m.
We can rewrite the second order differential equation as two first order differential
equations which we can solve simultaneously. We do so by considering the position x(t)
and velocity v(t) as independent variables. The first linear differential equation, v(t) =
x(t)
relates x(t) and v(t). The second linear differential equation, mv(t)
+ cv(t) + kx(t) =
f (t) is obtained by appropriately substituting v(t) and x(t) into (16.16). The system of
differential equations is then given by
x(t)
= v(t)
mv(t)
+ cv(t) + kx(t) = f (t)
(16.17)
scipy.integrate has the function odeint which solves systems of linear ordinary
differential equations. We can use the function odeint to solve for the system of linear
ordinary differential equations given in (16.17). The function requires the system to be
written such that only the first time derivatives appear on the left hand side of the equal
sign and the rest appears on the right hand side as shown below
x(t)
= v(t)
(t)
v(t)
= fm
cv(t)
m
kx(t)
m
(16.18)
Whatever appears on the right hand side of the equal sign is the required input for
odeint as you will shortly see. In addition the function odeint requires the equation to
be written in a vector form. We do so by substituting y1 (t) = x(t) and y2 (t) = v(t) in the
system below
y1 (t) = y2 (t)
(16.19)
(t)
2 (t)
1 (t)
cym
kym
y2 (t) = fm
where the subscript indicates the component of the vector y(t). To solve the system
we need an initial conditions for x(t) = y1 (t) and v(t) = y2 (t), the times at which we
want to solve the system and the time varying force f (t) that acts on the system. We
also need to specify the spring stiffness k, the linear damping coefficient c as well as the
mass of the system. In the examples that follow I chose the spring stiffness 1 N/m, the
linear damping coefficient 0.2 Ns/m and the mass 1 kg.
Let us consider the scenario where the initial velocity v(0) = y2 (0) is 0 and the initial
position x(0) = y1 (0) is 10 with no time varying force acting on the system i.e. f (t) = 0.
Let us solve the system for the time interval from 0 to 100 seconds in intervals of 0.5
seconds.
The function odeint takes the system as described by only the right hand side of
the equal sign (see (16.19)) as the first input argument function object. The second
Page 340
341
argument is a vector containing the initial conditions and the third argument is a vector
containing the times at which to solve the system. The function then return a matrix
with the dimensions of the rows equal to the number of time steps and the dimensions
of the columns equal to the number of variables. The following code computes and plots
the position and velocity of the mass
def main():
time = linspace(1, 100, 200)
results = odeint(func, [10, 0], time)
plot.show()
if __name__ == "__main__":
main()
16.5
Optimization
In most cases in Engineering many solutions exist to a problem where some of the solutions
are considered better than others. The formal process of finding the best solution is
referred to as optimization. For example, if we want to design a cooldrink can that holds
330 ml of cooldrink. What should the radius r and height h of the can be? Before we can
start we need a criteria to judge whether a design is good or bad. In optimization this is
referred to as a cost (or design) function.
Page 341
342
10
position
velocity
10
0
20
40
time
60
80
100
Figure 16.6: Response of the spring-mass-damper after displacing it 10 units from the
equilibrium position.
For our can example we could say that we want to minimize the material used to make
a can. If we assume the required thickness of the can to be independent of the dimension
of the can, we can calculate the amount of the material used to make a can by calculating
the surface area times the thickness of the can plate. So let us start by minimizing the
surface area of a cylindrical can that holds 330 ml of cooldrink. The surface area of a
cylindrical can is given by two times r2 for the top and bottom parts and 2rh for the
cylindrical section,
f (r, h) = 2r2 + 2rh
(16.20)
The volume of the can has to be equal to 330 ml. For simplicity let us work in units
of cm for which 330 ml is 330 cm3 . The volume V (r, h) of the can is therefore
V = r2 h = 330cm3
(16.21)
for r and h given in cm. We can write both (16.20) and (16.21) in a vector form
which is required for the optimization function fmin slsqp in scipy.optimize. Type
help(fmin slsqp) to see
.
.
.
Minimize a function using Sequential Least SQuares Programming
Page 342
Parameters
---------func : callable f(x,*args)
Objective function.
x0 : 1-D ndarray of float
Initial guess for the independent variable(s).
eqcons : list
A list of functions of length n such that
eqcons[j](x0,*args) == 0.0 in a successfully optimized
problem.
f_eqcons : callable f(x,*args)
Returns a 1-D array in which each element must equal 0.0 in a
successfully optimized problem. If f_eqcons is specified,
eqcons is ignored.
ieqcons : list
A list of functions of length n such that
ieqcons[j](x0,*args) >= 0.0 in a successfully optimized
problem.
f_ieqcons : callable f(x0,*args)
Returns a 1-D ndarray in which each element must be greater or
equal to 0.0 in a successfully optimized problem. If
f_ieqcons is specified, ieqcons is ignored.
bounds : list
A list of tuples specifying the lower and upper bound
for each independent variable [(xl0, xu0),(xl1, xu1),...]
fprime : callable f(x,*args)
A function that evaluates the partial derivatives of func.
fprime_eqcons : callable f(x,*args)
A function of the form f(x, *args) that returns the m by n
array of equality constraint normals. If not provided,
the normals will be approximated. The array returned by
fprime_eqcons should be sized as ( len(eqcons), len(x0) ).
fprime_ieqcons : callable f(x,*args)
A function of the form f(x, *args) that returns the m by n
array of inequality constraint normals. If not provided,
the normals will be approximated. The array returned by
fprime_ieqcons should be sized as ( len(ieqcons), len(x0) ).
args : sequence
Additional arguments passed to func and fprime.
iter : int
The maximum number of iterations.
Page 343
343
344
acc : float
Requested accuracy.
.
.
.
def main():
x_start = array([3, 12], dtype=float)
results = fmin_slsqp(costfunc,
x_start,
f_eqcons=equalcon,
f_ieqcons=inequalcon)
print
print
print
print
"\n",
"f(x)
"g(x)
"h(x)
if __name__ == "__main__":
main()
The function fmin slsqp requires the cost function given by (16.20) to take a vector
as input. We do so by relating the first entry of the vector x(1) to r and the second
entry x(2) to h as shown in program lines 67. The volume of the can must equal 330cm
Page 344
345
and must be written in the form h(x) = 0 which is shown in program lines 1011.
In addition the the radius x(1) and height x(2) has to be greater or equal to zero, as
negative distances does not make sense. It is important to include these considerations as
optimization algorithms may otherwise exploit non physical solutions in order to minimize
the value of the cost function. The inequality constraint has to be written in the form
g(x) 0, which is shown in program lines 1415. We then require an initial guess for x,
330
lets guess a radius x(1) of 3 cm for which the height x(2) is then h = x(2) = x(1)
2 12
cm. We can then use fmin slsqp to compute the optimal dimensions for the 330 cm3 as
shown in program lines 1923, which gives the following solution:
When we plug our solution back into our equality constraint function equalcon we
see that we get an answer of very close to zero, thus the optimal dimensions of the can are
feasible and clearly satisfies all the constraints. The optimal beverage can therefore has
a radius that is half of the height of the can, or a diameter which is equal to the height
of the can.
However the dimensions above do not correspond to the physical dimensions that we
see in cans we buy in the shops, this tells that the amount of material used is not the only
consideration when designing beverage cans, e.g. ergonomics and different materials used
in cans as top parts are made from Aluminium whereas the rest is tin plated steel. For
example, let us assume aluminium is twice as expensive as steel. If we consider designing
a can of which the top part is made of aluminium and the rest of the can is steel we have
the following
346
cost_alu = 2.0
cost_steel = 1.0
return (cost_alu * pi * x[0]**2 +
cost_steel * (2 * pi * x[0] * x[1] + pi * x[0]**2))
def main():
x_start = array([3, 12], dtype=float)
results = fmin_slsqp(costfunc,
x_start,
f_eqcons=equalcon,
f_ieqcons=inequalcon)
print
print
print
print
"\n",
"f(x)
"g(x)
"h(x)
if __name__ == "__main__":
main()
when we conduct another optimization run with our new cost function and we obtain
347
a more realistic beverage can design. Just by considering the different materials a
beverage can is made from significantly influences the optimal design, now the diameter
our beverage can is two third of the height of our can.
Careful consideration should be taken when constructing your cost and constraint
functions in optimization.
16.6
Exercises
(16.22)
(16.23)
x3 1
(16.24)
3. Divide
by x + 2. What is the remainder?
4. Which polynomial has the factors x 3, x + 4 and x 1?
5. Write a function polyfromroots where polyfromroots takes a list of numbers as
the input argument. This list contains the roots of the polynomial, i.e. x3 x =
x(x 1)(x + 1) has the roots [-1, 1, 0] from x + 1 = 0, x 1 = 0 and x = 0
respectively. The function must return the polynomial coefficients created from the
list of roots (without using scipys polyfromroots function). You may check your
answer by using scipys polyfromroots function.
[Hint: use scipys polymul function]
6. Solve the following differential equation using scipys odeint function,
f (t, y) = 3y(t) + 6t + 5
for y(0) = 3 and t(0) = 0.
7. What is the solution x of the following system of linear equations?
x1 2x3
= 1
x1 + x 2
= 2
x1 x2 + x3 = 5
with x = [x1 x2 x3 ]T .
Page 347
(16.25)
348
8. Background: Buoyancy driven flow driven by natural convection along a heated vertical plate is described by the following two coupled non-linear differential equations
d3 f
d 3
d2 T
d 2
df 2
+ 3f ddf2 2( d
) +T =0
dT
+ 3Prf d = 0
(16.26)
2
df
is the velocity, ddf2 relates
where Pr is the Prandtl number, f is a stream function, d
to the shear stress in the fluid stream, T is the temperature and dT
is the heat flux.
d
We can decompose the above system into five first-order differential equations by
using the following set of dependent variables
y1 () = f
df
y2 () = d
2
y3 () = ddf2
y4 () = T
y5 () = dT
d
(16.27)
The five first order differential equations given in terms of the above variables are
dy1
d
dy2
d
dy3
d
dy4
d
dy5
d
= y2
= y3
= 2y22 3y1 y3 y4
= y5
= 3Pry1 y5
(16.28)
Solve the above system of first order differential equations for 0 10 in increments
of 0.1 using Pr = 0.7 and the following initial conditions
y1 (0) = 0
y2 (0) = 0
y3 (0) = 0.68
y4 (0) = 1
y5 (0) = 0.5
(16.29)
Plot then y1 ,y2 ,y3 and y4 as function of on the same figure but each as an individual
plot using subplot.
9. Depicted in Figure 16.7 is a four-bar linkage with L1 , L2 , L3 and L4 the length of
the links. Link 1 is fixed in a horizontal position. For a given 1 , we can solve for
2 and 3 using the following two non-linear equations
L2 cos(1 ) + L3 cos(2 ) L4 cos(3 ) L1 = 0
L2 sin(1 ) + L3 sin(2 ) L4 sin(3 ) = 0
Page 348
(16.30)
x1 y1
L3
349
x2 y2
2
L4
L2
y
1
3
x
L1
Page 349
Page 350
350
Chapter 17
Whats Next
So far, what has been covered in these notes is just the tip of the iceberg. As mentioned
Python was created to be lightweight and easily extendible by other modules and packages,
and it has been. Python has hundreds (if not thousands) of available packages, each
tailored for a specific task or set of tasks. It is because of this that Python has become
quite a popular programming language and it is used in all industries. To list a just a few
applications:
1. Internet and Web Development
(modules: Django, Flask, many more)
2. Database access and management
(modules: SQLAlchemy,
3. GUIs and Graphics
(modules: TKinter, wxWidgets, pyQT, many more)
4. Numerical and Scientific Computing
(modules: Numpy, Scipy, Sympy, PIL, many more)
5. Education
6. Network Programming
7. Software Development
8. Gaming
Python is thus tool that you can continuously invest time in and learn to use it
as an effective engineering tool. A list of available packages can be found at http:
Page 351
352
17.1
Python(x,y) Packages
As mentioned Python(x,y) comes with many pre-installed Python packages. You can get
additional information about these packages from http://code.google.com/p/pythonx
y/wiki/StandardPlugins. Clicking on the package link will take you to that packages
official website where you can get more information.
17.2
If you need to install new packages, that dont come with Python(x,y), you can follow
these easy steps:
Make sure your PC is connected to the internet
Open the windows command prompt: push the Windows button, and while holding it in push the r button.
Type cmd in the run menu and hit enter
in the windows command prompt type pip install <package name> and hit enter.
Wait for the installation to finish and you are done.
Page 352