This chapter is taken from the book A Primer on Scientific Programming with Python by H. P. Langtangen, 5th edition, Springer, 2016.
Make a program that asks the user for a temperature
in Fahrenheit degrees and reads the number; computes the corresponding
temperature in Celsius degrees; and prints out the temperature
in the Celsius scale.
Filename: f2c_qa
.
Modify the program from Exercise 1: Make an interactive program such that
the Fahrenheit temperature is read from the command line.
Filename: f2c_cml
.
Modify the program from Exercise 1: Make an interactive program such that the Fahrenheit temperature is read from a file with the following content:
Temperature data
----------------
Fahrenheit degrees: 67.2
Create a sample file manually. In the program, skip the first three lines, split the fourth line into words and grab the third word.
Filename: f2c_file_read
.
This is a variant of Exercise 3: Read a number from a file where we have several Fahrenheit degrees in a file and want to read all of them into a list and convert the numbers to Celsius degrees. Thereafter, we want to write out a file with two columns, the left with the Fahrenheit degrees and the right with the Celsius degrees.
An example on the input file format looks like
Temperature data
----------------
Fahrenheit degrees: 67.2
Fahrenheit degrees: 66.0
Fahrenheit degrees: 78.9
Fahrenheit degrees: 102.1
Fahrenheit degrees: 32.0
Fahrenheit degrees: 87.8
A sample file
is Fdeg.dat.
Filename: f2c_file_read_write
.
Extend the program from Exercise 2: Read a number from the command line with a
try-except
block to handle the potential error that the
Fahrenheit temperature is missing on the command line.
Filename: f2c_cml_exc
.
Make a program that asks for input from the user, applies
eval
to this input, and prints out the type of the resulting
object and its value. Test the program by providing five
types of input: an integer, a real number, a complex number,
a list, and a tuple.
Filename: objects_qa
.
a)
Let a program store the result of applying
the eval
function to the first command-line
argument. Print out the resulting object and its type.
b) Run the program with different input: an integer, a real number, a list, and a tuple.
On Unix systems you need to surround the tuple expressions in quotes on the command line to avoid error message from the Unix shell.
c)
Try the string
"this is a string"
as a command-line argument. Why does this string
cause problems and what is the remedy?
Filename: objects_cml
.
The purpose of this exercise is to tell you how hard it may be to write Python programs in the standard programs that most people use for writing text.
a) Type the following one-line program in either MSWord or LibreOffice:
print "Hello, World!"
Both Word and LibreOffice are so "smart" that they automatically edit "print" to "Print" since a sentence should always start with a capital. This is just an example that word processors are made for writing documents, not computer programs.
b)
Save the program as a .docx
(Word) or .odt
(LibreOffice) file.
Now try to run this file as a Python program. What kind of error
message do you get? Can you explain why?
c)
Save the program as a .txt
file in Word or LibreOffice and
run the file as a Python program. What happened now?
Try to find out what the problem is.
Consider the simplest program for evaluating the formula \( y(t)=v_0t - \frac{1}{2}gt^2 \):
v0 = 3; g = 9.81; t = 0.6
y = v0*t - 0.5*g*t**2
print y
Modify this code so that
the program asks the user questions t=?
and v0=?
, and then
gets t
and v0
from the user's input through the keyboard.
Filename: ball_qa
.
Modify the program listed in Exercise 9: Prompt the user for input to a formula such that
v0
and t
are read from the command line.
Filename: ball_cml
.
The program from Exercise 10: Read parameters in a formula from the command line reads input from the
command line. Extend that program with exception handling such that
missing command-line arguments are detected. In the except IndexError
block, use the raw_input
function to ask the user for missing input
data.
Filename: ball_cml_qa
.
Test if the t
value read in the program from
Exercise 10: Read parameters in a formula from the command line lies between \( 0 \) and \( 2v_0/g \).
If not, print a message and abort the execution.
Filename: ball_cml_tcheck
.
Instead of printing an error message and aborting the program explicitly,
raise a ValueError
exception in the if
test on legal
t
values in
the program from Exercise 12: Test validity of input data.
Notify the user about the legal interval for \( t \) in the exception message.
Filename: ball_cml_ValueError
.
We consider the formula \( y(t)=v_0t - 0.5gt^2 \) and want to evaluate \( y \) for a range of \( t \) values found in a file with format
v0: 3.00
t:
0.15592 0.28075 0.36807889 0.35 0.57681501876
0.21342619 0.0519085 0.042 0.27 0.50620017 0.528
0.2094294 0.1117 0.53012 0.3729850 0.39325246
0.21385894 0.3464815 0.57982969 0.10262264
0.29584013 0.17383923
More precisely, the first two lines are always present, while the next lines contain an arbitrary number of \( t \) values on each line, separated by one or more spaces.
a) Write a function that reads the input file and returns \( v_0 \) and a list with the \( t \) values. A sample file is ball.dat
b) Make a test function that generates an input file, calls the function in a) for reading the file, and checks that the returned data objects are correct.
c) Write a function that creates a file with two nicely formatted columns containing the \( t \) values to the left and the corresponding \( y \) values to the right. Let the \( t \) values appear in increasing order (note that the input file does not necessarily have the \( t \) values sorted).
Filename: ball_file_read_write
.
A common software development technique in the IT industry is to write the test function before writing the function itself.
a)
We want to write a function halve(x)
that returns the half
of its argument x
. The test function is
def test_halve():
assert halve(5.0) == 2.5 # Real number division
assert halve(5) == 2 # Integer division
Write the associated function halve
. Call test_halve
(or run
pytest or nose) to verify that halve
works.
b)
We want to write a function add(a, b)
that returns the sum of its
arguments a
and b
. The test function reads
def test_add():
# Test integers
assert add(1, 2) == 3
# Test floating-point numbers with rounding error
tol = 1E-14
a = 0.1; b = 0.2
computed = add(a, b)
expected = 0.3
assert abs(expected - computed) < tol
# Test lists
assert add([1,4], [4,7]) == [1,4,4,7]
# Test strings
assert add('Hello, ', 'World!') == 'Hello, World!'
Write the associated function add
. Call test_add
(or run
pytest or nose) to verify that add
works.
c)
We want to write a function equal(a, b)
for determining if two
strings a
and b
are equal. If equal, the function returns
True
and the string a
. If not equal, the function returns False
and a string displaying the differences. This latter string contains
the characters common in a
and b
, but for every difference,
the character from a
and b
are written with a pipe symbol '|'
in between. In case a
and b
are of unequal length, pad the string
displaying differences with a *
where one of the strings lacks content.
For example, equal('abc', 'aBc')
would return
False, 'ab|Bc
', while equal('abc', 'aBcd')
would return
False, 'ab|Bc*|d'
. Here is the test function:
def test_equal():
assert equal('abc', 'abc') == (True, 'abc')
assert equal('abc', 'aBc') == (False, 'ab|Bc')
assert equal('abc', 'aBcd') == (False, 'ab|Bc*|d')
assert equal('Hello, World!', 'hello world') == \
(False, 'H|hello,| |wW|oo|rr|ll|dd|*!|*')
Write the equal
function (which is handy to detect very small differences
between texts).
Filename: testfunc2func
.
A car driver, driving at velocity \( v_0 \), suddenly puts on the brake. What braking distance \( d \) is needed to stop the car? One can derive, using Newton's second law of motion or a corresponding energy equation, that $$ \begin{equation} d = \frac{1}{2}{v_0^2\over \mu g}\tp \tag{7} \end{equation} $$
Make a program for computing \( d \) in (7) when the initial car velocity \( v_0 \) and the friction coefficient \( \mu \) are given on the command line. Run the program for two cases: \( v_0=120 \) and \( v_0=50 \) km/h, both with \( \mu=0.3 \) (\( \mu \) is dimensionless).
Remember to convert the velocity from km/h to m/s before inserting the value in the formula.
Filename: stopping_length
.
The purpose of this exercise is to make a program that takes a date,
consisting of year (4 digits), month (2 digits), and day (1-31) on the
command line and prints the corresponding name of the weekday (Monday,
Tuesday, etc.). Python has a module calendar
, which makes it easy
to solve the exercise, but the task is to find out how to use this
module.
Filename: weekday
.
Make the program integrate.py
from the section The magic exec function
shorter by using the convenient StringFunction
tool from
the section Turning string expressions into functions.
Write a test function for verifying this new implementation.
Filename: integrate2
.
The simplest way of writing a try-except
block is to test
for any exception, for example,
try:
C = float(sys.arg[1])
except:
print 'C must be provided as command-line argument'
sys.exit(1)
Write the above statements in a program and test the program. What is the problem?
The fact that a user can forget to supply a command-line argument when
running the program was the
original reason for using a try
block. Find out what kind of
exception that is relevant for this error and test for this specific
exception and re-run the program. What is the problem now?
Correct the program.
Filename: unnamed_exception
.
a)
Make six conversion functions between temperatures in Celsius, Kelvin,
and Fahrenheit: C2F
, F2C
, C2K
, K2C
, F2K
,
and K2F
.
b)
Collect these functions in a module convert_temp
.
c) Import the module in an interactive Python shell and demonstrate some sample calls on temperature conversions.
d) Insert the session from c) in a triple quoted string at the top of the module file as a doc string for demonstrating the usage.
e)
Write a function test_conversion()
that verifies the implementation.
Call this function from the test block if the first command-line
argument is verify
.
Check that C2F(F2C(f))
is f
, K2C(C2K(c))
is c
, and
K2F(F2K(f))
is f
- with tolerance. Follow the conventions
for test functions outlined in the sections Verification of the module code
and Example: Bisection root finding with a boolean variable that
is False
if a test failed, and True
if all test are passed, and
then an assert
statement to abort the program when any test fails.
f)
Add a user interface to the module such that the user can write a
temperature as the first command-line argument and the corresponding
temperature scale as the second command-line argument, and then get
the temperature in the two other scales as output. For example, 21.3
C
on the command line results in the output 70.3 F 294.4 K
.
Encapsulate the user interface in a function, which is called from the
test block.
Filename: convert_temp
.
The
exercise named "Approximate a function by a sum of sines" in the
document Functions and branching
[10]
asks you to write two functions called f
and S
. Now collect these
functions
in a separate file such that this file becomes a module.
Put the statements making the table
in a separate function table(n_values, alpha_values, T)
.
Make a test block in the module to read
\( T \) and a series of \( n \) and \( \alpha \) values as positional
command-line arguments
and make a corresponding call to table
.
Filename: sinesum2
.
Let the input to the program in Exercise 21: Organize a previous program as a module be
option-value pairs with the options
--n
, --alpha
, and --T
. Provide sensible default values
in the module file.
Apply the argparse
module to read the command-line arguments.
Do not copy code from the sinesum2
module, but make a new file
for reading option-value pairs from the command and import the
table
function from the sinesum2
module.
Filename: sinesum3
.
Because of rounding errors, it could happen that a mathematical rule
like \( (ab)^3 = a^3b^3 \) does not hold exactly on a computer. The idea
of testing this potential problem is to check such identities for a
large number of random numbers. We can make random numbers using the
random
module in Python:
import random
a = random.uniform(A, B)
b = random.uniform(A, B)
Here, a
and b
will be random numbers, which are always larger than or equal to A
and smaller than B
.
a)
Make a function power3_identity(A=-100, B=100, n=1000)
that tests the identity (a*b)**3 == a**3*b**3
a large number
of times, n
. Return the fraction of failures.
Inside the loop over n
, draw
random numbers a
and b
as described above and count the
number of times the test is True
.
b) We shall now parameterize the expressions to be tested. Make a function
equal(expr1, expr2, A=-100, B=100, n=500)
where expr1
and expr2
are strings containing the two
mathematical expressions to be tested. More precisely, the
function draws random numbers a
and b
between A
and B
and tests if eval(expr1) == eval(expr2)
.
Return the fraction of failures.
Test the function on the identities \( (ab)^3 = a^3b^3 \), \( e^{a+b}=e^ae^b \), and \( \ln a^b = b\ln a \).
Make the equal
function robust enough to handle illegal \( a \) and
\( b \) values in the mathematical expressions (e.g., \( a\leq 0 \) in
\( \ln a \)).
c) We want to test the validity of the following set of identities on a computer:
equal
function. Make a nicely formatted table
with a pair of equivalent expressions at each line followed by the
failure rate. Write this table to a file. Try out A=1
and B=2
as
well as A=1
and B=100
. Does the failure rate seem to depend on
the magnitude of the numbers \( a \) and \( b \)?
Filename: math_identities_failures
.
Consider an uncertain event where there are two outcomes only, typically success or failure. Flipping a coin is an example: the outcome is uncertain and of two types, either head (can be considered as success) or tail (failure). Throwing a die can be another example, if (e.g.) getting a six is considered success and all other outcomes represent failure. Such experiments are called Bernoulli trials.
Let the probability of success be \( p \) and that of failure \( 1-p \). If
we perform \( n \) experiments, where the outcome of each experiment does
not depend on the outcome of previous experiments, the probability of
getting success \( x \) times, and consequently failure \( n-x \) times, is given by
$$
\begin{equation}
B(x,n,p) = {n!\over x! (n-x)!} p^x(1-p)^{n-x}\tp
\tag{8}
\end{equation}
$$
This formula (8) is called the binomial
distribution. The expression \( x! \) is the factorial of \( x \):
\( x!=x(x-1)(x-2)\cdots 1 \) and math.factorial
can do this computation.
a)
Implement (8) in a function binomial(x, n, p)
.
b) What is the probability of getting two heads when flipping a coin five times? This probability corresponds to \( n=5 \) events, where the success of an event means getting head, which has probability \( p=1/2 \), and we look for \( x=2 \) successes.
c) What is the probability of getting four ones in a row when throwing a die? This probability corresponds to \( n=4 \) events, success is getting one and has probability \( p=1/6 \), and we look for \( x=4 \) successful events.
d) Suppose cross country skiers typically experience one ski break in one out of 120 competitions. Hence, the probability of breaking a ski can be set to \( p=1/120 \). What is the probability \( b \) that a skier will experience a ski break during five competitions in a world championship?
This question is a bit more demanding than the other two. We are looking for the probability of 1, 2, 3, 4 or 5 ski breaks, so it is simpler to ask for the probability \( c \) of not breaking a ski, and then compute \( b=1-c \). Define success as breaking a ski. We then look for \( x=0 \) successes out of \( n=5 \) trials, with \( p=1/120 \) for each trial. Compute \( b \).
Filename: Bernoulli_trials
.
Suppose that over a period of \( t_m \) time units, a particular uncertain event happens (on average) \( \nu t_m \) times. The probability that there will be \( x \) such events in a time period \( t \) is approximately given by the formula $$ \begin{equation} P(x,t, \nu) = {(\nu t)^x\over x!}e^{-\nu t}\tp \tag{9} \end{equation} $$ This formula is known as the Poisson distribution. (It can be shown that (9) arises from (8) when the probability \( p \) of experiencing the event in a small time interval \( t/n \) is \( p=\nu t/n \) and we let \( n\rightarrow\infty \).) An important assumption is that all events are independent of each other and that the probability of experiencing an event does not change significantly over time. This is known as a Poisson process in probability theory.
a)
Implement (9) in a function Poisson(x, t,
nu)
, and make a program that reads \( x \), \( t \), and \( \nu \) from the
command line and writes out the probability \( P(x,t,\nu) \). Use this
program to solve the problems below.
b) Suppose you are waiting for a taxi in a certain street at night. On average, 5 taxis pass this street every hour at this time of the night. What is the probability of not getting a taxi after having waited 30 minutes? Since we have 5 events in a time period of \( t_m=1 \) hour, \( \nu t_m= \nu = 5 \). The sought probability is then \( P(0, 1/2, 5) \). Compute this number. What is the probability of having to wait two hours for a taxi? If 8 people need two taxis, that is the probability that two taxis arrive in a period of 20 minutes?
c) In a certain location, 10 earthquakes have been recorded during the last 50 years. What is the probability of experiencing exactly three earthquakes over a period of 10 years in this area? What is the probability that a visitor for one week does not experience any earthquake? With 10 events over 50 years we have \( \nu t_m = \nu \cdot 50 \hbox{ years} = 10 \hbox{ events} \), which implies \( \nu = 1/5 \) event per year. The answer to the first question of having \( x=3 \) events in a period of \( t=10 \) years is given directly by (9). The second question asks for \( x=0 \) events in a time period of 1 week, i.e., \( t=1/52 \) years, so the answer is \( P(0,1/52,1/5) \).
d) Suppose that you count the number of misprints in the first versions of the reports you write and that this number shows an average of six misprints per page. What is the probability that a reader of a first draft of one of your reports reads six pages without hitting a misprint? Assuming that the Poisson distribution can be applied to this problem, we have "time" \( t_m \) as 1 page and \( \nu \cdot 1 = 6 \), i.e., \( \nu=6 \) events (misprints) per page. The probability of no events in a "period" of six pages is \( P(0,6,6) \).
Filename: Poisson_processes
.