## Polynomial Dict1.py

Exercise 7.2. Make a very simple class.

Make a class Simple with one attribute i, one method double, which replaces the value of i by i+i, and a constructor that initializes the attribute. Try out the following code for testing the class:

s1.double() print s1.i s2 = Simple('Hello') s2.double(); s2.double() print s2.i s2.i = 100 print s2.i

Before you run this code, convince yourself what the output of the print statements will be. Name of program file: Simple.py. o

### Exercise 7.3. Extend the class from Ch. 7.2.1.

Add an attribute transactions to the Account class from Chapter 7.2.1. The new attribute counts the number of transactions done in the deposit and withdraw methods. The total number of transactions should be printed in the dump method. Write a simple test program to demonstrate that transaction gets the right value after some calls to deposit and withdraw. Name of program file: Account2.py. o

Exercise 7.4. Make classes for a rectangle and a triangle.

The purpose of this exercise is to create classes like class Circle from Chapter 7.2.3 for representing other geometric figures: a rectangle with width W, height H, and lower left corner (x0,y0); and a general triangle specified by its three vertices (x0,y0), (xi,yi), and (x2,y2) as explained in Exercise 2.17. Provide three methods:__init__(to initialize the geometric data), area, and circumference. Name of program file: geometric_shapes.py. o

### Exercise 7.5. Make a class for straight lines.

Make a class Line whose constructor takes two points pi and p2 (2-tuples or 2-lists) as input. The line goes through these two points (see function line in Chapter 2.2.7 for the relevant formula of the line). A value (x) method computes a value on the line at the point x. Here is a demo in an interactive session:

>>> from Line import Line >>> line = Line((0,-1), (2,4))

>>> print line.value(0.5), line.value(0), line.value(1) 0.25 -1.0 1.5

Name of program file: Line.py. o

### Exercise 7.6. Improve the constructor in Exer. 7.5.

The constructor in class Line in Exercise 7.5 takes two points as arguments. Now we want to have more flexibility in the way we specify a straight line: we can give two points, a point and a slope, or a slope and the line's interception with the y axis. Hint: Let the constructor take two arguments pi and p2 as before, and test with isinstance whether the arguments are float or tuple/list to determine what kind of data the user supplies:

if isinstance(p1, (tuple,list)) and isinstance(p2, (float,int)): self.a = p2 # p2 is slope self.b = p1 - p2*p1 # pi is a point elif ...

Name of program file: Line2.py. o

### Exercise 7.7. Make a class for quadratic functions.

Consider a quadratic function f (x; a, b, c) = ax2 + bx + c. Make a class Quadratic for representing f, where a, b, and c are attributes, and the methods are

1. value for computing a value of f at a point x,

2. table for writing out a table of x and f values for n x values in the interval [L, R],

3. roots for computing the two roots.

Name of program file: Quadratic.py. o

### Exercise 7.8. Make a class for linear springs.

To elongate a spring a distance x, one needs to pull the spring with a force kx. The parameter k is known as the spring constant. The corresponding potential energy in the spring is i kx2.

Make a class for springs. Let the constructor store k as a class attribute, and implement the methods force(x) and energy(x) for evaluating the force and the potential energy, respectively.

The following function prints a table of function values for an arbitrary mathematical function f(x). Demonstrate that you can send the force and energy methods as the f argument to table.

Write out f(x) for x in [a,b] with steps h=(b-a)/n

print heading h = (b-a)/float(n) for i in range(n+1): x = a + i*h print 'function value = °/.10.4f at x = /g' / (f(x), x) Name of program file: Spring.py. o

Exercise 7.9. Implement Lagrange's interpolation formula.

Given n + 1 points (xo, yo), (xi, yi),..., (xn, yn), the following polynomial of degree n goes through all the points:

Make a class Lagrange whose constructor takes the n+1 points as input in the form of a list of points. A special method__call__evaluates the polynomial pL(x) at a point x.

To verify the program, we observe that Lk(xk) = 1 and that Lk(xj) = 0 for i = k such that pL (xk) equals yk. Write a method _verify in class Lagrange that computes |pL(xk) — yk | at all points and checks that the value is approximately zero (e.g., less than 10-14). Generate 5 points along the curve y = sin(x) for x € [0, n] and call _verify.

The formula (7.14) is called Lagrange's interpolation formula, because given n + 1 points, the formula makes it possible to evaluate function values between the points10, and at the points we recover the yk values. Sometimes the polynomial pl exhibit a strange behavior, which we now want to explore. Generate n + 1 points for n = 3, 5, 7,11 along the curve y = |x|, with x € [—2,2], and plot pL(x) for each n value (use many more than n + 1 points to plot the polynomials - otherwise you just get straight lines between the n + 1 points). Also plot the n + 1 points as circles. Observe from the plot that pL always goes through the points, but as the number of points increases, pL oscillates more and more, and the approximation of pl to the curve |x| is getting poorer. Make an additional plot of pL corresponding to n = 13 and n = 20 and observe how the amplitude of the oscillations increase with increasing n. These oscillations are undesired, and much research has historically been focused on methods that do not result in "strange oscillations" when fitting a polynomial to a set of points.

Name of program file: Lagrange_polynomial. py. O

Exercise 7.10. A very simple "Hello, World!" class.

Make a class that can only do one thing: print a writes "Hello, World!" to the screen, when a is an instance of the class. Name of program file: HelloWorld.py. O

Exercise 7.11. Use special methods in Exer. 7.1.

Modify the class from Exercise 7.1 such that the following code works:

f = F2(1.0, 0.1) print f(pi) f.a = 2 print f(pi) print f

Name of program file: F2.py. o

10 This is called interpolation.

Exercise 7.12. Modify a class for numerical differentiation.

Make the two attributes h and f of class Derivative from Chapter 7.3.2 protected as explained in Chapter 7.2.1. That is, prefix h and f with an underscore to tell users that these attributes should be accessed directly. Add two methods get_precision() and set_precision(h) for reading and changing the h parameter in the numerical differentiation formula. Apply the modified class to make a table of the approximation error of the derivative of f (x) = ln x for x = 1 and h = 2-k, k = 1, 5, 9,13 ..., 45. Name of program file: geometric_shapes.py. o

### Exercise 7.13. Make a class for nonlinear springs.

This exercise is a generalization of Exercise 7.8. To elongate a spring a distance x, one needs a force f (x). If f is linear, as in Exercise 7.8 (f (x) = kx), we speak of a linear spring, otherwise the spring is nonlinear. The potential energy stored in the elongated spring, arising from the work done by the force, is given by the integral JQx f (t)dt.

Make a class for nonlinear springs which has the methods force(x) and energy(x) for computing the force and potential energy respectively. Make use of class Integral from Chapter 7.3.3 in the energy method to compute the integral numerically.

Demonstrate the class for a linear spring f (x) = kx. Also demonstrate the class for a nonlinear spring f (x) = a sin(x). Implement f in both cases as a class with k or a as attribute. Name of program file: Spring_nonlinear.py. o

### Exercise 7.14. Extend the class from Ch. 7.2.1.

As alternatives to the deposit and withdraw methods in class Account class from Chapter 7.2.1, we could use += for deposit and -= for withdraw. The special methods__iadd__and__isub__implement the

+= and -= operators, respectively. For instance, a -= p implies a call to a.__isub__(p). One important feature of__iadd__and__isub__is that they must return self to work properly, cf. the documentation of these methods in the Python Language Reference (not to be confused with the Python Library Reference).

Implement the += and -= operators, a__str__method, and preferably a __repr__ method. Provide, as always, some code to test that the new methods work as intended. Name of program file: Account3.py. o

Exercise 7.15. Implement a class for numerical differentation.

A widely used formula for numerical differentiation of a function f(x) takes the form f '(x)

This formula usually gives more accurate derivatives than (7.1) because it applies a centered, rather than a one-sided, difference.

The goal of this exercise is to use the formula (7.15) to automatically differentiate a mathematical function f (x) implemented as a Python function f(x). More precisely, the following code should work:

df = Central(f) # make function-like object df # df(x) computes the derivative of f(x) approximately: for x in (1, 5, 10):

df_value = df(x) # approx value of derivative of f at point x exact = x**3 # exact value of derivative print "f'(/d)=/g (error=/.2E)" / (x, df_value, exact-df_value)

Implement class Central and test that the code above works. Include an optional argument h to the constructor in class Central so that one can specify the value of h in the approximation (7.15). Apply class Central to produce a table of the derivatives and the associated approximation errors for f(x) = ln x, x = 10, and h = 0.5,0.1,10-3,10-5,10-7,10-9, 10-11. Collect class Central and the two applications of the class in the same file, but organize the file as a module so that class Central can be imported in other files. Name of program file: Central.py. o

Exercise 7.16. Verify a program.

Consider this program file for computing a backward difference approximation to the derivative of a function f(x):

from math import *

class Backward:

h, f = self.h, self.f return (f(x) - f(x-h))/h # finite difference dsin = Backward(sin)

e = dsin(0) - cos(0); print 'error:', e dexp = Backward(exp, h=e-7) e = dexp(0) - exp(0); print 'error:', e

The output becomes error: -1.00023355634 error: 371.570909212

Is the approximation that bad, or are there bugs in the program? o

Exercise 7.17. Test methods for numerical differentation.

Make a function table(f, x, hlist, dfdx=None) for writing out a nicely formatted table of the errors in the numerical derivative of a function f(x) at point x using the two formulas (7.1) and 7.15 and their implementations in classes Derivative (from Chapter 7.3.2), and Central (from Exercise 7.15). The first column in the table shows a list of h values (hlist), while the two next columns contain the corresponding errors arising from the two numerical approximations of the first derivative. The dfdx argument may hold a Python function that returns the exact derivative. Write out an additional column with the exact derivative if dfdx is given (i.e., not None).

Call table for each of the functions x2, sin6(nx), and tanh(10x), and the x values 0 and 0.25. Can you see from the errors in the tables which of the three approximations that seems to have the overall best performance in these examples? Plot the three functions on [—1, 1] and try to understand the behavior of the various approximations from the plots. Name of program file: Derivative_comparisons.py. O

Exercise 7.18. Make a class for summation of series.

Our task in this exercise is to calculate a sum S(x) = ^^=M fk (x), where fk(x) is a term in a sequence which is assumed to decrease in absolute value. In class Sum, for computing S(x), the constructor requires the following three arguments: fk(x) as a function f(k, x), M as an int object M, and N as an int object N. A__call__method computes and returns S(x). The next term in the series, +1(x), should be computed and stored as an attribute first_neglected_term. Here is an example where we compute S(x) = ^fc=0(—x)k:

# print the value of the first neglected term from last S(x) comp.: print S.first_neglected_term

Calculate by hand what the output of this test becomes, and use it to verify your implementation of class Sum.

Apply class Sum to compute the Taylor polynomial approximation for sin x at x = n, 30n and N = 5,10, 20. Compute the error and compare with the first neglected term +1(x). Present the result in nicely formatted tables. Repeat such calculations for the Taylor polynomial for e-x at x = 1,3, 5 and N = 5,10,20. Also demonstrate how class Sum can be used to calculate the sum (2.1) on page 78 (choose x = 2, 5,10 and N = 5,10,20). Formulas for the Taylor polynomials can be looked up in Exercise 4.18. Name of program file: Sum.py. o

Exercise 7.19. Apply the differentiation class from Ch. 7.3.2.

Use class Derivative from page 358 to calculate the derivative of the function v on page 230 with respect to the parameter n, i.e., dfv. Choose = 50 and r/R = 0.5, and compare the result with the exact derivative. Hint: Make a class similar to VelocityProfile on page 346, but provide r as a parameter to the constructor, instead of n, and let __call__take n as parameter. Exercise 9.6 suggests a more advanced technique that can also be applied here to implement v as a function of n. Name of program file: VelocityProfile_deriv.py. o

Exercise 7.20. Use classes for computing inverse functions.

Chapter 5.1.10 describes a method and implementation for computing the inverse function of a given function. The purpose of the present exercise is to improve the implementation in Chapter 5.1.10 by introducing classes. This allows a program that is more flexible with respect to the way we can specify the function to be inverted.

Implement the F and dFdx functions from Chapter 5.1.10 as classes to avoid relying on global variables for h, xi, etc. Also introduce a class InverseFunction to run the complete algorithm and store the g array (from Chapter 5.1.10) as an array attribute values. Here is a typical use of class InverseFunction:

>>> from InverseFunction import InverseFunction as I

Check, in the constructor, that f is monotonically increasing or decreasing over the set of coordinates (x). Errors may occur in the computations because Newton's method might divide by zero or diverge. Make sure sensible error messages are reported in those cases.

A __call__ in class InverseFunction should should evaluate the inverse function at an arbitrary point x. This is somewhat challenging, however, since we only have the inverse function at discrete points along its curve. With aid of a function wrap2callable from scitools.std one can turn (x, y) points on a curve, stored in arrays x and y, into a (piecewise) continuous Python function q(x) by:

In a sense, the wrap2callable draws lines between the discrete points to form the resulting continuous function. Use wrap2callable to make __call__ evaluate the inverse function at any point in the interval from x to x[-1]. Name of program file: InverseFunction.py. o

Exercise 7.21. Vectorize a class for numerical integration.

Use a vectorized implementation of the Trapezoidal rule in class Integral from Chapter 7.3.3. Name of program file: Integral_vec.py. o

Exercise 7.22. Speed up repeated integral calculations.

The observant reader may have noticed that our Integral class from Chapter 7.3.3 is very inefficient if we want to tabulate or plot a function

F(x) = /a f (x) for several consecutive values of x, say x0 < x1 < ■ ■ ■ < xn. Requesting F(xk) will recompute the integral computed as part of F(xk-1), and this is of course waste of computer work.

Modify the __call__ method such that if x is an array, assumed to contain coordinates of increasing value: x0 < x1 < ■ ■ ■ < xn, the method returns an array with F(x0), F(x1),..., F(xn). Make sure you are not doing any redundant calculations. (Hint: The F values can be efficiently calculated by one pass through the x array if you store intermediate integral approximations in the Trapezoidal algorithm.) Name of program file: Integral_eff.py. o

### Exercise 7.23. Solve a simple ODE in two ways.

The purpose of this exercise is to solve the ODE problem u' = u/10, u(0) = 0.2, for t € [0, 20]. Use both the ForwardEuler function and the ForwardEuler class from Chapter 7.4, both with ^t = 1. Check that the results produced by the two equivalent methods coincide (you may use the float_eq function, see Exercise 2.51). Plot the solution. Name of program file: simple_ODE.py. o

Use the ForwardEuler class from Chapter 7.4 to solve the ODE (B.36) described in Exercise 9.25 on page 554. Use the parameters as described in Exercise 9.25 and simulate for 200 seconds. Plot the solution. Name of program file: tank_ODE_FEclass.py. o

Exercise 7.25. Simulate a falling or rising body in a fluid.

A body moving vertically through a fluid (liquid or gas) is subject to three different types of forces:

1. the gravity force Fg = —mg, where m is the mass of the body and g is the acceleration of gravity;

2. the drag force11 Fd = — 2CDqA|v|v (see also Exercise 1.10), where CD is a dimensionless drag coefficient depending on the body's shape, q is the density of the fluid, A is the cross-sectional area (produced by a cutting plane y = const through the thickest part of the body), and v is the velocity;

3. the uplift or buoyancy force ("Archimedes force") Fb = QgV, where V is the volume of the body.

Newton's second law applied to the body says that the sum of these forces must equal the mass of the body times its acceleration a:

11 Roughly speaking, the Fd formula is suitable for medium to high velocities, while for very small velocities, or very small bodies, Fd is proportional to the velocity, not the velocity squared, see .

The unknowns here are v and a, i.e., we have two unknowns but only one equation. From kinetmatics in physics we know that the acceleration is the time derivative of the velocity: a = dv/dt. This is our second equation. We can easily eliminate a and get a single differential equation for v:

1 dv

A small rewrite of this equation is handy: We express m as QbV, where Qb is the density of the body, and we isolate dv/dt on the left-hand side, dv = -»(1 — I) — 2 CD QbVlvlv. (7.16)

This differential equation must be accompanied by an initial condition: v(0) = vo. Make a program for solving (7.16) numerically, utilizing tools from Chapter 7.4. Implement the right-hand side of (7.16) in the

__call__method of a class where the parameters g, q, Qb, CD, A, and

### V are attributes.

To verify the program, assume a heavy body in air such that the Fb force can be neglected, and assume a small velocity such that the air resistance Fd can also be neglected. Setting q = 0 removes both these terms from the equation. The motion is then described by (1.1), and the exact velocity is v(t) = y'(t) = v0 — gt. See how well the program reproduces this simple solution.

After the program is verified, we are ready to run two real examples and plot the evolution of v:

1. Parachute jumper. The point is to compute the motion of a parachute jumper in free fall before the parachute opens. We set the density of the human body as Qb = 1003 kg/m3 and the mass as m = 80 kg, implying V = m/Qb = 0.08 m3. We can base the cross-sectional area A on the height 1.8 m and a width of 50 cm, giving giving A nR2 = 0.9 m2. The density of air decreases with height, and we here use the value 0.79 kg/m3 which is relevant for about 5000 m height. The CD coefficient can be set as 0.6. Start with v0 = 0.

2. Rising ball in water. A ball with the size of a soccer ball is placed in deep water, and we seek to model its motion upwards. Contrary to the former example, where the buoyancy force Fb is very small, Fb is now the driving force, and the gravity force Fg is small. Set A = na2 with a = 11 cm, the mass of the ball is 0.43 kg, the density of water is 1000 kg/m3, and CD is 0.2. Start with v0 = 0 and see how the ball rises.

Name of program file: body_in_fluid.py. o

Exercise 7.26. Check the solution's limit in Exer. 7.25.

The solution of (7.16) often tends to a constant velocity, called the terminal velocity. This happens when the sum of the forces, i.e., the right-hand side in (7.16) vanishes. Compute the formula for the terminal velocity by hand. Modify class ForwardEuler from Chapter 7.4 so that the time loop is run as long as k < N and a user-defined function terminate(u, t, k) is False, where u is the list of ui values for i = 0,1,..., k, t is the corresponding list of time points, and k is the current time step counter. The terminate function can be given as an extra argument to solve. Here is an outline of the modified class ForwardEuler:

class ForwardEuler:

def solve(self, N, terminate=None): if terminate is None:

not terminate(self.u, self.t, self.k): unew = self.advance()

In your implementation of terminate, stop the simulation when a constant velocity is reached, that is, when |v(tn) — v(tn-1)| < e, where e is a small number. Run a series of ^t values and make a graph of the terminal velocity as a function of ^t for the two cases in Exercise 7.25. Plot a horizontal line with the exact terminal velocity. Name of program file: body_in_fluid_termvel.py. o

Exercise 7.27. Implement the modified Euler method; function.

The following numerical method for solving U = f (u, t), u(0) = Uo, is known as the modified Euler method:

At each time level, we run the formula (7.17) N times, and the nth value vN is the new value of u, i.e., uk+1. Setting N = 1 recovers the Forward Euler scheme, while N corresponds to a 2nd-order Runge-Kutta scheme. We can either fix the value of N, or we can repeat (7.18) until the change in vq is small, that is, until |vq — vq-1| < e, where e is a small value. Fixing N is sufficient in this exercise. Implement (7.17)-(7.18) as a function

EulerMidpoint_method(f, dt, u0, T, N)

where f is a Python implementation of f (u, t), dt is the time step ^t, u0 is the initial condition u(0), T is the final time of the simulation, and N is the parameter N in the method (7.17). The EulerMidpoint_method should return two arrays: u0,..., uN and t0,..., tN. To verify the implementation, calculate by hand ui and u2 when N = 2 for the ODE u' = —2u, u(0) = 1, with ^t = 1/2. Compare your hand calculations with the results of the program. Thereafter, run the program for the same ODE problem but with ^t = 0.1 and T = 2. Name of program file: ModifiedEuler_func.py. O

Exercise 7.28. Implement the modified Euler method; class.

The purpose of this exercise is to implement the modified Euler method, defined in Exercise 7.28, in a class like the ForwardEuler class from Chapter 7.4. Create a module containing the class and a test function demonstrating the use:

method = ModifiedEuler(f, dt, N) method.set_initial_condition(1) T = 1.5

u, t = method.solve(T) from scitools.std import plot plot(t, u)

Call the _test function from the test block in the module file. Name of program file: ModifiedEulerl.py. O

Exercise 7.29. Increase the flexibility in Exer. 7.28.

The constructor of class ModifiedEuler in Exercise 7.28 takes an N parameter with a fixed value. We can, as explained in Exercise 7.27, either fix N or introduce an e and iterate until the change in |vq — vq-1| is less than e. Modify the constructor so that we can give both N and e. We compute a new vq as long as q < N or |vq — vq-1| > e. Let N = 20 and e = 10-6 by default. Name of program file: ModifiedEuler2.py. o

Exercise 7.30. Solve an ODE specified on the command line.

To solve an ODE, we want to make a program odesolver.py which accepts an PDE problem to be specified on the command line. The typical syntax of running the programs looks like odesolver.py f u0 dt T

where f is the right-hand side f(u, t) specified as a string formula (to be converted to a StringFunction object), u0 is the initial condition, dt is the time step, and T is the final time of the simulation. A curve plot of the solution versus time should be produced and stored in a file plot.png. Make the odesolver.py program, using any numerical method that you like. Hint: You may start with the programs in Chapter 7.4 and add a command-line user interface. Name of program file: odesolver.py. O

Exercise 7.31. Apply a polynomial class.

The Taylor polynomial of degree N for the exponential function ex is given by

Use class Polynomial from page 365 to represent this p(x) polynomial for a given N. Choose x = 2 and N = 5,25, 50,100, and compare the accuracy of the result with math.exp(2). Name of program file: Polynomial_exp.py. O

Exercise 7.32. Find a bug in a class for polynomials.

Go through this alternative implementation of class Polynomial from page 365 and explain each line in detail:

class Polynomial:

return sum([c*x**i for i, c in enumerate(self.coeff)])

maxlength = max(len(self), len(other)) # extend both lists with zeros to this maxlength: self.coeff += *(maxlength - len(self.coeff)) other.coeff += *(maxlength - len(other.coeff)) sum_coeff = self.coeff for i in range(maxlength):

sum_coeff[i] += other.coeff[i] return Polynomial(sum_coeff)

Look up what the enumerate function does (use the Python Library Reference). Write this code in a file, and demonstrate that adding two polynomials does not work. Find the bug and correct it. Name of program file: Polynomial_error.py. O

Exercise 7.33. Subtraction of polynomials.

Implement the special method__sub__in class Polynomial from page 365. Name of program file: Polynomial_sub.py. O

### Exercise 7.34. Represent a polynomial by an array.

Introduce a Numerical Python array for self.coeff in class Polynomial from page 365. Go through the class code and run the statements in the _test method in the Polynomial.py file to locate which statements that need to be modified because self.coeff is an array and not a list. Name of program file: Polynomial_array1.py. O

### Exercise 7.35. Vectorize a class for polynomials.

Introducing an array instead of a list in class Polynomial, as suggested in Exercise 7.34, does not enhance the implementation. A real enhancement arise when the code is vectorized, i.e., when loops are replaced by operations on whole arrays.

First, vectorize the__add__method by adding the common parts of the coefficients arrays and then appending the rest of the longest array to the result (appending an array a to an array b is done by concatenate(a, b)).

Second, vectorize the__call__method by observing that evaluation of a polynomial, ^"=-0 c^x1, can be computed as the inner product of two arrays: (c0,..., cn-1) and (x0, x1,..., xn-1). The latter array can be computed by x**p, where p is an array with powers 0,1,..., n — 1 made by linspace or arange.

Third, the differentiate method can be vectorized by the statements n = len(self.coeff)

self.coeff[:-1] = linspace(1, n-1, n-1)*self.coeff[1:] self.coeff = self.coeff[:-1]

Show by hand calculations in a case where n is 3 that the vectorized statements produce the same result as the original differentiate method.

The __mul__ method is more challenging to vectorize so you may leave this unaltered. Check that the vectorized versions of __add__ , __call__ , and differentiate work by comparing with the scalar code from Exercise 7.34 or the original, list-based Polynomial class. Name of program file: Polynomial_array2.py. o

Exercise 7.36. Use a dict to hold polynomial coefficients; add.

Use a dictionary for self.coeff in class Polynomial from page 365. The advantage with a dictionary is that only the nonzero coefficients need to be stored. Let self.coeff[k] hold the coefficient of the xk term. Implement a constructor and the __add__ method. Exemplify the implementation by adding x — 3x100 and x20 — x + 4x100. Name of program file: Polynomial_dict1.py. o

Exercise 7.37. Use a dict to hold polynomial coefficients; mul.

Extend the class in Exercise 7.36 with a __mul__ method. First, study the algorithm in Chapter 7.3.7 for the __mul__ method when the coefficients are stored in lists. Then modify the algorithm to work with dictionaries. Implement the algorithm and exemplify it by multiplying x — 3x100 and x20 — x + 4x100. Name of program file: Polynomial_dict2.py. o

Exercise 7.38. Extend class Vec2D to work with lists/tuples.

The Vec2D class from Chapter 7.5 supports addition and subtraction, but only addition and subtraction of two Vec2D objects. Sometimes we would like to add or subtract a point that is represented by a list or a tuple:

That is, a list or a tuple must be allowed in the right or left operand. Use ideas from Chapters 7.6.3 and 7.6.4 to implement this extension. Name of program file: Vec2D_lists.py. O

### Exercise 7.39. Use NumPy arrays in class Vec2D.

The internal code in class Vec2D from Chapter 7.5 can be valid for vectors in any space dimension if we represent the vector as a NumPy array in the class instead of seperate variables x and y for the vector components. Make a new class Vec where you apply NumPy functionality in the methods. The constructor should be able to treat all the following ways of initializing a vector:

a = array([1, -1, 4], float) # numpy array v = Vec(a)

v = Vec([1, -1, 4]) # list v = Vec((1, -1, 4)) # tuple v = Vec(1, -1) # coordinates

We will provide some helpful advice. In the constructor, use variable number of arguments as described in Appendix E.5. All arguments are then available as a tuple, and if there is only one element in the tuple, it should be an array, list, or tuple you can send through asarray to get a NumPy array. If there are many arguments, these are coordinates, and the tuple of arguments can be transformed by array to a NumPy array. Assume in all operations that the involved vectors have equal dimension (typically that other has the same dimension as self). Recall to return Vec objects from all arithmetic operations, not NumPy arrays, because the next operation with the vector will then not take place in Vec but in NumPy. If self.v is the attribute holding the vector as a NumPy array, the addition operator will typically be implemented as class Vec:

return Vec(selv.v + other.v)

Name of program file: Vec.py. o

Exercise 7.40. Use classes in the program from Ch. 6.6.2.

Modify the files/students.py program described in Chapter 6.6.2 by making the values of the data dictionary instances of class Student. This class contains a student's name and a list of the courses. Each course is represented by an instance of class Course. This class contains the course name, the semester, the credit points, and the grade. Make

__str__and/or__repr__write out the contents of the objects. Name of program file: Student_Course. py. o

### Exercise 7.41. Use a class in Exer. 6.28.

The purpose of this exercise is to make the program from Exercise 6.28 on page 333 more flexible by creating a class that runs and archives all the experiments. Here is a sketch of the class:

class GrowthLogistic:

self.experiments = []

self.show_plot_on_screen = show_plot_on_screen self.remove_plot_files()

def run_one(self, y0, q, N): """Run one experiment."""

plotfile = 'tmp_y0_°/„g_q_°/„g_N_°/„d.png' / (y0, q, N) self.experiments.append({'y0': y0, 'q': q, 'N': N,

'mean': mean(y[20:]), 'y': y, 'plotfile': plotfile})

def run_many(self, y0_list, q_list, N): """Run many experiments.""" for q in q_list:

for y0 in y0_list:

def remove_plot_files(self):

"""Remove plot files with names tmp_y0*.png.""" import os, glob for plotfile in glob.glob('tmp_y0*.png'): os.remove(plotfile)

def report(self, filename='tmp.html'): """

Generate an HTML report with plots of all experiments generated so far. """

# open file and write HTML header... for e in self.experiments:

html.write('<p><img src="°/„s">\n' % e['plotfile'])

# write HTML footer and close file...

Each time the run_one method is called, data about the current experiment is stored in the experiments list. Note that experiments contains a list of dictionaries. When desired, we can call the report method to collect all the plots made so far in an HTML report. A typical use of the class goes as follows:

g = GrowthLogistic() g.run_many(y0_list=[0.01, 0.3], q_list=[0.1, 1, 1.5, 1.8] + [2, 2.5, 3], N=N) g.run_one(y0=0.01, q=3, N=1000) g.report()

Make a complete implementation of class GrowthLogistic and test it with the small program above. The program file should be constructed as a module. Name of program file: growth_logistic5.py. o

Exercise 7.42. Apply the class from Exer. 7.41 interactively.

Class GrowthLogistic from Exercise 7.41 is very well suited for interactive exploration. Here is a possible sample session for illustration:

>>> from growth_logistic5 import GrowthLogistic >>> g = GrowthLogistic(show_plot_on_screen=True) >>> q = 3

>>> g.run_one(0.01, q, 100) >>> y = g.experiments[-1]['y'] >>> max(y) 1.3326056469620293 >>> min(y)