100% found this document useful (1 vote)
670 views

Introduction To Scientific Computing A Matrix Vector Approach Using Matlab 2nd Edition PDF

This chapter introduces some of the core tools in Matlab for scientific computing. It covers vectors and plotting, matrices, building exploratory environments, error analysis, designing functions, structure arrays and cell arrays, and refined graphics. The chapter uses examples to familiarize readers with the Matlab environment and preview themes that will be important in later chapters, focusing on vector and matrix operations, linear algebra concepts, and data visualization tools.

Uploaded by

Aisha Gurung
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
670 views

Introduction To Scientific Computing A Matrix Vector Approach Using Matlab 2nd Edition PDF

This chapter introduces some of the core tools in Matlab for scientific computing. It covers vectors and plotting, matrices, building exploratory environments, error analysis, designing functions, structure arrays and cell arrays, and refined graphics. The chapter uses examples to familiarize readers with the Matlab environment and preview themes that will be important in later chapters, focusing on vector and matrix operations, linear algebra concepts, and data visualization tools.

Uploaded by

Aisha Gurung
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 398

Table of Contents

Chapter 1. Power Tools of the Trade

1.1 Vectors and Plotting


1.2 More Vectors, More Plotting, and Now Matrices
1.3 Building Exploratory Environments
1.4 Error
1.5 Designing Functions
1.6 Structure Arrays and Cell Arrays
1.7 More Refined Graphics

Chapter 2. Polynomial Interpolation

2.1 The Vandermonde Approach


2.2 The Newton Representation
2.3 Properties
2.4 Special Topics

Chapter 3. Piecewise Polynomial Interpolation

3.1 Piecewise Linear Interpolation


3.2 Piecewise Cubic Hermite Interpolation
3.3 Cubic Splines

Chapter 4. Numerical Integration

4.1 The Newton-Cotes Rules


4.2 Composite Rules
4.3 Adaptive Quadrature
4.4 Special Topics
4.5 Shared Memory Adaptive Quadrature

Chapter 5. Matrix Computations

5.1 Setting Up Matrix Problems


5.2 Matrix Operations
5.3 Once Again, Setting Up Matrix Problems
5.4 Recursive Matrix Operations
5.5 Distributed Memory Matrix Multiplication

Chapter 6. Linear Systems

6.1 Triangular Problems


6.2 Banded Problems
6.3 Full Problems
6.4 Analysis

Chapter 7. The QR and Cholesky Factorizations

7.1 Least Squares Fitting


7.2 The QR factorization
7.3 The Cholesky Factorization
7.4 High-Performance Cholesky

Chapter 8. Nonlinear Equations and Optimization

8.1 Finding Roots


8.2 Minimizing a Function of One Variable
8.3 Minimizing Multivariate Functions
8.4 Solving Systems of Nonlinear Equations

Chapter 9. The Initial Value Problem

9.1 Basic Concepts


9.2 The Runge-Kutta Methods
9.3 The Adams Methods
Introduction to Scientific Computing
A Matrix-Vector Approach Using Matlab

2nd Edition

List of Errata
Page/Line Details
xi url should be http://www.cs.cornell.edu/cv
5, first script stray \\
8, second to last script y(k) = sin(2*pi*x(k)), add the 2*pi
p. 38, line 1 the "a" should be a "b"
p. 42 for k=1:nTerms in the script ExpTaylor
46, after proof Matlab seems to set eps = 2^(1-t).
53, second to last line "of" not "off"
60, line 8 case error: C1.long.d, not C1.Long.d
61, 3rd line in spec for
.. is the value of f.
convert
62 stray "is a " in spec for pretty
63, PadeCoeff comments The square brackets should be parens, i.e., R.num(1).
p.69 Color mnemonics for white, magenta, and black should be w, m, and k resp.
p. 78 In last scipt, superfluous V = zeros(n,n)
p.171, bottom c_{i-j+1} instead of c_{i-j}
81, line 2 case error, ...pVal requires...
In the spec for pwLAdapt, should be "delta and hmin" and "x and y are column n-
112
vectors"
120,line 1 case error, ... pwLEval ...
144, P4.1.2 Compare the output for NCweights and MyNCweights
169 2nd script for j=1:i not for j=1:n
171, last equation Should have c_{i-j+1} and r_{j-i+1}
p. 173, last displayed eqn the (4,5) entry should be a -1.
177 line -4 Ax not Az
210, line -5 ... so we can vectorize as follows (assuming that x is a column vector):
p. 233 At the bottom, should be [L,U,P] = LU(A).
246, P7.1.2 stray \ symbols and "\car" is "^ "
The (n,n-1) and the (n-1,n) entries in the matrix in Theorem 7 are e_{n} not
256
e_{n-1}
274 The function MakeScalar is missing from the list.
336 F(z) = z – h_n f(t_{n+1},z) – y_n
344, line 8 stray tt
361 cell, 59, not cell, 160
363 cell 59, not cell 160.
Preface to the First Edition

Matlab affects the way we do research in scientific computing because it encourages experimentation
with interesting mathematical ideas. Visualization and vector-level thinking are supported in a way that
permits focus on high-level issues. It is by clearing such a wide path from research to applications that
Matlab has been such an uplifting force in computational science. For exactly the same reasons,
Matlab can uplift the teaching of introductory scientific computing. Students need to play with the
mathematics that stands behind each and every new method that they learn. They need graphics to
appreciate convergence and error. They need a matrix-vector programming language to solidify their
understanding of linear algebra and to prepare for a world of advanced array-level computing. They
need a total problem solving environment tapping into the very latest algorithmic research that has a
bearing on science and engineering. In short, they need Matlab.

In this textbook I present all the topics that are usually covered in a one-semester introduction to
scientific computing. But graphics and matrix-vector manipulation have been folded into the
presentation in a way that gets students to appreciate the connection between continuous mathematics
and computing. Each of the nine chapters comes equipped with a theorem. Analysis is complemented
with computational experiments that are designed to bolster intuition. Indeed, the text revolves around
examples that are packaged in 200+ M-files. These codes are critical to the overall presentation.
Collectively they communicate all the key mathematical ideas and an appreciation for the subtleties of
numerical computing. They also illustrate many features of Matlab that are likely to be useful later on in
the student's computational career. Snapshots of advanced computing are given in sections that deal
with parallel adaptive quadrature and parallel matrix computations. Our treatment of recursion includes
divided differences, adaptive approximation, quadrature, the fast Fourier transform, Strassen matrix
multiplication, and the Cholesky factorization. Numerical linear algebra is not confined to the matrix
computation units. Because of the graphics thread throughout the text, it permeates the entire
presentation beginning in Chapter 1. That first chapter is yet another get-started-with-Matlab tutorial,
but it is driven by examples that set the stage for the numerical algorithms that follow.

I want to thank the students of CS 222 at Cornell University who inspired me to write this book. My
colleagues Yuying Li and Steve Vavasis were of immense help during the revision process. Cindy
Robinson has been my administrative assistant since 1987 and has seen me through the production of
five textbooks. Cindy's thoughtful support was essential during this period.

Finally, Cleve Moler has played a very critical role in my academic career ever since I first walked into
his office while an undergraduate at the University of Michigan. As teacher, Ph.D. advisor, and force
behind Matlab, Cleve has defined the way I look at mathematics and computing. I am extremely happy
to dedicate this textbook to him.
Preface to the Second Edition

With this revision I have upgraded the book to a \Matlab 5 level. There are about 60 new problems and
a few new topics.

In Chapter 1 there is a new section on structure arrays and cell arrays and a new section on how to
produce more informative plots. A brief treatment of trigonometric interpolation is now included in
Chapter 2. (There is a follow-up FFT solution to the problem in Chapter 5.)

Chapter 5 includes a brief discussion of sparse arrays and this permits a limited study of sparse
methods for linear equations and least squares in Chapters 6 and 7. In these chapters the block matrix
material is enriched with the use of cell arrays.

The Chapter 8 orbit problem solutions now make use of simple structures and this simplifies the
presentation.

The Chapter 9 treatment of ode23 is a little more detailed.

I am deeply indebted to Carl de Boor and Mike Overton for their numerous suggestions on how to
improve the First Edition. Rob Corliss, Tim Davis, and Dan Drucker have also been kind enough to
offer advice.

My staff assistant Cindy Robinson and my wife Marian continue to make things like book writing
possible. With two laugh-a-minute types like these I am never more than thirty seconds away from a
good joke, often about book writing!

Finally and with great pleasure, I rededicate the text to Cleve Moler. I am more thankful than ever for
Cleve's many contributions to scientific computing.
Chapter 1

Power Tools of the Trade

§1.1 Vectors and Plotting


§1.2 More Vectors, More Plotting, and Now Matrices
§1.3 Building Exploratory Environments
§1.4 Error
§1.5 Designing Functions
§1.6 Structure Arrays and Cell Arrays
§1.7 More Refined Graphics
Matlab is a matrix-vector-oriented system that supports a wide range of activity that is crucial to the
computational scientist. In this chapter we get acquainted with this system through a collection of examples
that sets the stage for the proper study of numerical computation. The Matlab environment is very easy
to use and you might start right now by running demo. Our introduction in this chapter previews the central
themes that occur with regularity in the following chapters.
We start with the exercise of plotting. Matlab has an extensive array of visualization tools. But even
the simplest plot requires setting up a vector of function values, and so very quickly we are led to the many
vector-level operations that Matlab supports. Our mission is to build up a linear algebra sense to the
extent that vector-level thinking becomes as natural as scalar-level thinking. Matlab encourages this in
many ways, and plotting is the perfect start-up topic. The treatment is spread over two sections.
Building environments that can be used to explore mathematical and algorithmic ideas is the theme of
§1.3. A pair of random simulations is used to illustrate how Matlab can be used in this capacity.
In §1.4 we learn how to think and reason about error. Error is a fact of life in computational science,
and our examples are designed to build an appreciation for two very important types of error. Mathematical
errors result when we take what is infinite or continuous and make it finite or discrete. Rounding errors arise
because floating-point representation and arithmetic is inexact.
§1.5 is devoted to the art of designing effective functions. The user-defined function is a fundamental
building block in scientific computation. More complicated data structures are discussed in §1.6, while in
the last section we point to various techniques that can be used to enrich the display of visual data.

1.1 Vectors and Plotting


Suppose we want to plot the function f(x) = sin(2πx) across the interval [0, 1]. In Matlab there are three
components to this task.
• A vector of x-values that range across the interval must be set up:

0 = x1 < x2 < · · · < xn = 1.

1
2 CHAPTER 1. POWER TOOLS OF THE TRADE

• The function must be evaluated at each x-value:

yk = f(xk ), k = 1, . . . , n.

• A polygonal line that connects the points (x1 , y1 ), . . . , (xn , yn ) must be displayed.
If we take 21 equally spaced x-values, then the result looks like the plot shown in Figure 1.1. The plot is
The Function y = sin(2*pi*x)
1

0.8

0.6

0.4

0.2

0
y

−0.2

−0.4

−0.6

−0.8

−1
0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1
x (in radians)

Figure 1.1 A crude plot of sin(2πx)

“crude” because the polygonal effect is noticeable in regions where the function is changing rapidly. But
otherwise the graph looks quite good. Our introduction to Matlab begins with the details of the plotting
process and the vector computations that go along with it. The sin(2πx) example is used throughout because
it is simple and structured. Exploiting that structure leads naturally to some vector operations that are well
supported in the Matlab environment.

1.1.1 Setting Up Vectors


When you invoke the Matlab system, you enter the command window and are prompted to enter commands
with the symbol “>>”. For example,

>> x = [10.1 20.2 30.3]

Matlab is an interactive environment and it responds with


x =

10.1000 20.2000 30.3000

>>

This establishes x as a length-3 row vector. Square brackets delineate the vector and spaces separate the
components. On the other hand, the exchange
>> x = [ 10.1; 20.2; 30.3]
x =
10.1000
20.2000
30.3000
1.1. VECTORS AND PLOTTING 3

establishes x as a length-3 column vector. Again, square brackets define the vector being set up. But this
time semicolons separate the component entries and a column vector is produced.
In general, Matlab displays the consequence of a command unless it is terminated with a semicolon.
Thus,
>> x = [ 10.1; 20.2; 30.3];
sets up the same column 3-vector as in the previous example, but there is no echo that displays the result.
However, the dialog
x = [10.1; 20.2; 30.3];
x

x =
10.1000
20.2000
30.3000
shows that the contents of a vector can be displayed merely by entering the name of the vector. Even if one
component in a vector is changed with no terminating semicolon, Matlab displays the whole vector:

x = [10.1; 20.2; 30.3];


x(2) = 21

x =

10.1000
21.0000
30.3000

It is clear that when dealing with large vectors, a single forgotten semicolon can result in a deluge of displayed
output.
To change the orientation of a vector from row to column or column to row, use an apostrophe. Thus,
x = [10.1 20.2 30.3]’
establishes x as a length-3 column vector. Placing an apostrophe after a vector effectively takes its transpose.
The plot shown in Figure 1.1 involves the equal spacing of n = 21 x-values across [0, 1]; that is

x = [0 .05 .10 .15 .20 .25 .30 .35 .40 .45 .50 ...
.55 .60 .65 .70 .75 .80 .85 .90 .95 1.0 ]

The ellipsis symbol “...” permits the entry of commands that occupy more than one line.
It is clear that for even modest values of n, we need other mechanisms for setting up vectors. Naturally
enough, a for-loop can be used:
n = 21;
h = 1/(n-1);
for k=1:n
x(k) = (k-1)*h;
end
This is a Matlab script. It assigns the same length-21 vector to x as before and it brings up an important
point.
In Matlab, variables are not declared by the user but are created on a
need-to-use basis by a memory manager. Moreover, from Matlab’s point
of view, every simple variable is a complex matrix indexed from unity.
4 CHAPTER 1. POWER TOOLS OF THE TRADE

Scalars are 1-by-1 matrices. Vectors are “skinny” matrices with either one row or one column. We have
much more to say about “genuine” matrices later. Our initial focus is on real vectors and scalars.
In the preceding script, n, h, k, and x are variables. It is instructive to trace how x “turns into” a vector
during the execution of the for-loop. After one pass through the loop, x is a length-1 vector (i.e., a scalar).
During the second pass, the reference x(2) prompts the memory manager to make x a 2-vector. During the
third pass, the reference x(3) prompts the memory manager to make x a 3-vector. And so it goes until by
the end of the loop, x has length 21. It is a convention in Matlab that this kind of vector construction
yields row vectors.
The Matlab zeros function is handy for setting up the shape and size of a vector prior to a loop that
assigns it values. Thus,

n = 21;
h = 1/(n-1);
x = zeros(1,n);
for k=1:n;
x(k) = (k-1)*h;
end

computes x as row vector of length-21 and initializes the values to zero. It then proceeds to assign the appro-
priate value to each of the 21 components. Replacing x = zeros(1,n) with the command x = zeros(n,1)
sets up a length-21 column vector. This style of vector set-up is recommended for two reasons. First, it
forces you to think explicitly about the orientation and length of the vectors that you are working with. This
reduces the chance for “dimension mismatch” errors when vectors are combined. Second, it is more efficient
because the memory manager does not have to “work” so hard with each pass through the loop.
Matlab supplies a length function that can be used to probe the length of any vector. To illustrate its
use, the script
u = [10 20 30];
n = length(u);
v = [10;20;30;40];
m = length(v);
u = [50 60];
p = length(u);
assigns the values of 3, 4, and 2 to n, m, and p, respectively.
This brings up another important feature of Matlab. It supports a very extensive help facility. For
example, if we enter
help length
then Matlab responds with
LENGTH Number of components of a vector.
LENGTH(X) returns the length of vector X. It is equivalent
to MAX(SIZE(X)).
So extensive and well structured is the help facility that it obviates the need for us to go into excessive
detail when discussing many of Matlab’s capabilities. Get in the habit of playing around with each new
Matlab feature that you learn, exploring the details via the help facility. Start right now by trying
help help
Here in Chapter 1 there are many occasions to use the help facility as we proceed to acquire enough familiarity
with the system to get started. Before continuing, you are well advised to try
help who
help whos
help clear
1.1. VECTORS AND PLOTTING 5

to learn more about the management of memory. We have already met a number of Matlab language
features and functions. You can organize your own mini-review by entering
help for
help zeros
help ;
help []

1.1.2 Regular Vectors


Regular vectors arise so frequently that Matlab has a number of features that support their construction.
With the colon notation it is possible to establish row vectors whose components are equally spaced. The
command
x = 20:24
is equivalent to
x = [ 20 21 22 23 24]
The spacing between the component values is called the stride and the vector x has unit stride. Nonunit
strides can also be specified. For example,
x = 20:2:29;
This stride-2 vector is the same as
x = [20 22 24 26 28]
Negative strides are also permissible. The assignment
x = 10:-1:1
is equivalent to
x = [ 10 9 8 7 6 5 4 3 2 1]
As seen from the examples, the general use of the colon notation has the form
hStarting Indexi:hStridei:hBounding Indexi
If the starting index is beyond the bounding index, then the empty vector is produced:
x = 3:2

x =
[]
The empty vector has length zero and is denoted with a square bracket pair with nothing in between.
The colon notation also works with nonintegral values. The command
x = 0:.05:1
sets up a length-21 row vector with the property that xi = (i − 1)/20, i = 1, . . . , 21. Alternatively, we could
multiply the vector 0:20 by the scalar .05:
x = .05*(0:20)
However, if nonintegral strides are involved, then it is preferable to use the linspace function. If a and b
are real scalars, then
x = linspace(a,b,n)
6 CHAPTER 1. POWER TOOLS OF THE TRADE

returns a row vector of length n whose kth entry is given by

xk = a + (k − 1) ∗ (b − a)/(n − 1).

For example,

x = linspace(0,1,21)

is equivalent to

x = [0 .05 .10 .15 .20 .25 .30 .35 .40 .45 .50 ...
.55 .60 .65 .70 .75 .80 .85 .90 .95 1.0 ]

In general, a reference to linspace has the form

linspace(hLeft Endpointi,hRight Endpointi,hNumber of Pointsi)

Logarithmic spacing is also possible. The assignment

x = logspace(-2,3,6);

is the same as x = [ .01 .1 1 10 100 1000]. More generally, x = logspace(a,b,n) sets

xk = 10a+(b−a)(k−1)/(n−1), k = 1, . . . , n
and is equivalent to

m = linspace(a,b,n);
for k=1:n
x(k) = 10^m(k);
end

The linspace and logspace functions bring up an important detail. Many of Matlab’s functions can
be called with a reduced parameter list that is often useful in simple, canonical situations. For example,
linspace(a,b) is equivalent to linspace(a,b,100) and logspace(a,b) is equivalent to logspace(a,b,50).
Make a note of these shortcuts as you become acquainted with Matlab’s many features.
So far we have not talked about how Matlab displays results except to say that if a semicolon is left off
the end of a statement, then the consequences of that statement are displayed. Thus, if we enter

x = .123456789012345*logspace(1,5,5)’

then the vector x is displayed according to the active format. For example,

x =
1.0e+04 *

0.0001
0.0012
0.0123
0.1235
1.2346

The preceding is the short format. The long, short e, and long e formats are also handy as depicted
in Figure 1.2. The short format is active when you first enter Matlab. The format command is used to
switch formats. For example,

format long

It is important to remember that the display of a vector is independent of its internal floating point repre-
sentation, something that we will discuss in §1.4.4.
1.1. VECTORS AND PLOTTING 7

short long short e long e

1.0e+14 * 1.0e+14 *

0.0000 0.00000000000001 1.2346e+00 1.234567890123450e+00


0.0000 0.00000000000012 1.2346e+01 1.234567890123450e+01
0.0000 0.00000000000123 1.2346e+02 1.234567890123450e+02
0.0000 0.00000000001235 1.2346e+03 1.234567890123450e+03
0.0000 0.00000000012346 1.2346e+04 1.234567890123450e+04

0.0000 0.00000000123457 1.2346e+05 1.234567890123450e+05


0.0000 0.00000001234568 1.2346e+06 1.234567890123450e+06
0.0000 0.00000012345679 1.2346e+07 1.234567890123450e+07
0.0000 0.00000123456789 1.2346e+08 1.234567890123450e+08
0.0000 0.00001234567890 1.2346e+09 1.234567890123450e+09

0.0001 0.00012345678901 1.2346e+10 1.234567890123450e+10


0.0012 0.00123456789012 1.2346e+11 1.234567890123450e+11
0.0123 0.01234567890123 1.2346e+12 1.234567890123450e+12
0.1235 0.12345678901234 1.2346e+13 1.234567890123450e+13
1.2346 1.23456789012345 1.2346e+14 1.234567890123450e+14

Figure 1.2 The display of .123456789012345*logspace(1,15,15)’

1.1.3 Evaluating Functions


We return to the task of plotting sin(2πx). Matlab comes equipped with a host of built-in functions
including sin. (Enter help elfun to see the available elementary functions.) The script

n = 21;
x = linspace(0,1,n);
y = zeros(1,n);
for k=1:n
y(k) = sin(2*pi*x(k));
end

sets up a vector of sine values that correspond to the values in x. But many of the built-in functions like
sin accept vector arguments, and the preceding loop can be replaced with a single reference as follows:

n = 21;
x = linspace(0,1,n);
y = sin(2*pi*x);

The act of replacing a loop in Matlab with a single vector-level operation will be referred to as vectorization
and has three fringe benefits:

• Speed. Many of the built-in Matlab functions provide the results of several calls faster if called once
with the corresponding vector argument(s).

• Clarity. It is often easier to read a vectorized Matlab script than its scalar-level counterpart.

• Education. Scientific computing on advanced machines requires that one be able to think at the vector
level. Matlab encourages this and, as the title of this book indicates, we have every intention of
fostering this style of algorithmic thinking.

As a demonstration of the vector-level manipulation that Matlab supports, we dissect the following script:
8 CHAPTER 1. POWER TOOLS OF THE TRADE

m = 5; n = 4*m+1;
x = linspace(0,1,n); a = x(1:m+1);
y = zeros(1,n);
y(1:m+1) = sin(2*pi*a);
y(2*m+1:-1:m+2) = y(1:m);
y(2*m+2:n) = -y(2:2*m+1);

which sets up the same vector y as before but with one-fourth the number of scalar sine evaluations. The
idea is to exploit symmetries in the table shown in Figure 1.3. The script starts by assigning to a a subvector

k xk sin(xk )
1 0 0.000
2 18 0.309
3 36 0.588
4 54 0.809
5 72 0.951
6 90 1.000
7 108 0.951
8 126 0.809
9 144 0.588
10 162 0.309
11 180 0.000
12 198 -0.309
13 216 -0.588
14 234 -0.809
15 252 -0.951
16 270 -1.000
17 288 -0.951
18 306 -0.809
19 324 -0.588
20 342 -0.309
21 360 -0.000

Figure 1.3 Selected values of the sine function (xk in degrees)

of x. In particular, the assignment to a is equivalent to

a = [0.00 0.05 0.10 0.15 0.20 0.25]

In general, if v is a vector of integers that are valid subscripts for a row vector z, then

w = z(v);

is equivalent to

for k=1:length(v)
w(k) = z(v(k));
end

The same idea applies to column vectors. Extracted subvectors have the same orientation as the parent
vector.
Assignment to a subvector is also legal provided the named subscript range is valid. Thus,

y(1:m+1) = sin(2*pi*a);

is equivalent to
1.1. VECTORS AND PLOTTING 9

for k=1:m+1
y(k) = sin(2*pi*a(k));
end

Now comes the first of two mathematical exploitations. The sine function has the property that
π  π 
sin + x = sin −x .
2 2
Thus,
   
sin(10h) sin(0h)
 sin(9h)   sin(h) 
   
 sin(8h)  =  sin(2h)  h = 2π/20.
   
 sin(7h)   sin(3h) 
sin(6h) sin(4h)

Note that the components on the left should be stored in reverse order in y(7:11), while the components
on the right have already been computed and are housed in y(1:5). (See Figure 1.3.) The assignment

y(m+1:2*m+1) = y(m:-1:1);

establishes the necessary values in y(7:11).


At this stage, y(1:2*m+1) contains the sine values from [0, π] that are required. To obtain the remaining
values, we exploit a second trigonometric identity:

sin(π + x) = − sin(x).

We see that this implies


   
sin(11h) sin(h)
 sin(12h)   sin(2h) 
   
 sin(13h)   sin(3h) 
   
 sin(14h)   sin(4h) 
   
 sin(15h)   sin(5h) 
  = −  h = 2π/20.
 sin(16h)   sin(6h) 
   
 sin(17h)   sin(7h) 
   
 sin(18h)   sin(8h) 
   
 sin(19h)   sin(9h) 
sin(20h) sin(10h)

The sine values on the left belong in y(12:21) while those on the right have already been computed and
occupy y(2:11). Hence, the construction of y(1:21) is completed with the assignment

y(2*m+2:n) = -y(2:2*m+1);

(See Figure 1.3.)


Why go though such contortions when y = sin(2*pi*linspace(0,1,21)) is so much simpler? The
reason is that more often than not, function evaluations are expensive and one should always be searching
for relationships that reduce their number. Of course, sin is not expensive. But the vector computations
detailed in this subsection above are instructive because we must learn to be sparing when it comes to the
evaluation of functions.

1.1.4 Displaying Tables


Any vector can be displayed by merely typing its name and leaving off the semicolon. However, sometimes a
more customized output is preferred, and for that a facility with the disp and sprintf functions is required.
10 CHAPTER 1. POWER TOOLS OF THE TRADE

But before we can go any further we must introduce the concept of a script file. Already, our scripts are
getting too long and too complicated to assemble line-by-line in the command window. The time has come
to enlist the services of a text editor and to store the command sequence in a file that can then be executed.
To illustrate the idea, we set up a script file that can be used to display the table in Figure 1.3. We start
by entering the following into a file named SineTable.m:

% Script File: SineTable


% Prints a short table of sine evaluations.
clc
n = 21;
x = linspace(0,1,n);
y = sin(2*pi*x);
disp(’ ’)
disp(’ k x(k) sin(x(k))’)
disp(’------------------------’)
for k=1:21
degrees = (k-1)*360/(n-1);
disp(sprintf(’ %2.0f %3.0f %6.3f ’,k,degrees,y(k)));
end
disp( ’ ’);
disp(’x(k) is given in degrees.’)
disp(sprintf(’One Degree = %5.3e Radians’,pi/180))

The .m suffix is crucial, for then the preceding command sequence is executed merely by entering SineTable
at the prompt:
>> SineTable
This displays the table shown in Figure 1.3, assuming that Matlab can find SineTable.m. This is assured
if the file is in the current working directory or if path is properly set. Review what you must know about
key file organization by entering help dir cd ls lookfor.
Focusing on SineTable itself, there are a number of new features that we must explain. The script begins
with a sequence of comments indicating what happens when it is run. Comments in Matlab begin with
the percent symbol “%”. Aside from enhancing readability, the lead comments are important because they
are displayed in response to a help enquiry. That is,
help SineTable
Use type to list the entire contents of a file, e.g.,
type SineTable
The clc command clears the command window and places the cursor in the home position. (This is
usually a good way to start a script that is to generate command window output.) The disp command has
the form
disp(hstringi)
Strings in Matlab are enclosed by single quotes. The commands
disp(’ ’)
disp(’ k x(k) sin(x(k))’)
disp(’------------------------’)
are used to print a blank line, a heading, and a dashed line.
The sprintf command is used to produce a string that includes the values of named variables. It has
the form
sprintf(hString with Format Specificationsi,hList-of-Variablesi)
1.1. VECTORS AND PLOTTING 11

A variable must be listed for each format. Sample format insertions include %5.0f, %8.3f, and %10.6e. The
first integer in a format specification is the total width of the field. The second number specifies how many
places are allocated to the fractional part. In the script, the command
disp(sprintf(’ %2d %3.0f %6.3f ’,k,degrees,y(k)));
prints a line with three numbers. The three numbers are stored in k, degrees, and y(k). The value of k is
printed as an integer while degrees is printed with a decimal point but with no digits to the right of the
decimal point. On the other hand, y(k) is printed with three decimal places. The e format is used to specify
mantissa/exponent style. For example,
disp(sprintf(’One Degree = %5.3e Radians’,pi/180))
This produces the output of the form
One Degree = 1.745e-02 Radians
If x is a vector then
disp(sprintf(’ %5.3e ’,x))
displays all the components of x on a single line, each with 5.3e format.

1.1.5 A Note About fprintf


It is sometimes handy to use fprintf instead of the combinations of disp and sprintf. Consider the
fragement
disp(’ ’)
disp(’ k x(k) sin(x(k))’)
disp(’------------------------’)
for k=1:21
degrees = (k-1)*360/(n-1);
disp(sprintf(’ %2.0f %3.0f %6.3f ’,k,degrees,y(k)));
end
disp( ’ ’);
disp(’x(k) is given in degrees.’)
disp(sprintf(’One Degree = %5.3e Radians’,pi/180))
taken from the script SinePlot above. This is equivalent to
fprintf(’\n k x(k) sin(x(k))\n------------------------\n’)
for k=1:21
degrees = (k-1)*360/(n-1);
fprintf(’ %2.0f %3.0f %6.3f \n’,k,degrees,y(k));
end
fprintf( ’ \nx(k) is given in degrees.\nOne Degree = %5.3e Radians’,pi/180)
The carriage return command “\n” effecively says “start a new line of output”.

1.1.6 A Simple Plot


We are now in a position to solve the plotting problem posed at the beginning of this section. The script

n = 21; x = linspace(0,1,n); y = sin(2*pi*x);


plot(x,y)
title(’The Function y = sin(2*pi*x)’)
xlabel(’x (in radians)’)
ylabel(’y’)
12 CHAPTER 1. POWER TOOLS OF THE TRADE

Plot of sin(2*pi*x) based upon n = 400 points.


1

0.8

0.6

0.4

0.2

−0.2

−0.4

−0.6

−0.8

−1
0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1

Figure 1.4 A smooth plot of sin(2πx)

reproduces Figure 1.1. It draws a polygonal line in a figure that connects the vertices (xk , yk ), k = 1:21 in
order. In its most simple form, plot takes two vectors of equal size and plots the second versus the first. The
scaling of the axes is done automatically. The title, xlabel, and ylabel functions enable us to “comment”
the plot. Each requires a string argument.
To produce a better plot with no “corners,” we increase n so that the line segments that make up the
graph are sufficiently short, thereby rendering the impression of a genuine curve. For example,

n = 200;
x = linspace(0,1,n);
y = sin(2*pi*x);
plot(x,y)
title(’The function y = sin(2*pi*x)’)
xlabel(’x (in radians)’)
ylabel(’y’)

produces the plot displayed in Figure 1.4. In general, the smoothness of a displayed curve depends on the
spacing of the underlying sample points, screen granularity, and the vision of the observer. Here is a script
file that produces a sequence of increasingly refined plots:

% Script File: SinePlot


% Displays increasingly smooth plots of sin(2*pi*x).
close all
for n = [4 8 12 16 20 50 100 200 400]
x = linspace(0,1,n);
y = sin(2*pi*x);
plot(x,y)
title(sprintf(’Plot of sin(2*pi*x) based upon n = %3.0f points.’,n))
pause(1)
end

There are four new features to discuss. The close all command closes all windows. It is a good idea to
begin script files that draw figures with this command so as to start with a “clean slate.” Second, notice
the use of a general vector in the specification of the for-loop. The count variable n takes on the values
in the specified vector one at a time. Third, observe the use of sprintf in the reference to title. This
1.2. MORE VECTORS, MORE PLOTTING, AND NOW MATRICES 13

enables us to report the number of points associated with each plot. Finally, the fragment makes use of
the pause function. In general, a reference of the form pause(s) holds up execution for approximately s
seconds. Because a sequence of plots is produced in the preceding example, the pause(1) command permits
a 1-second viewing of each plot.

Problems

P1.1.1 The built-in functions like sin accept vector arguments and return vectors of values. If x is an n vector, then

abs(x) |xi |
8 9 8 9
> > > √ >
sqrt(x) xi , xi ≥ 0
>
> >
> >
> >
>
>
> >
> >
> >
exi
>
exp(x)
>
> >
> >
> >
>
>
> >
> >
> >
>
log(x) log(xi ), xi > 0
>
> >
> >
> >
>
< = < =
y = sin(x) ⇒ yi = sin(xi ) , i = 1:n.
cos(x) cos(xi )
>
> >
> >
> >
>
>
> >
> >
> >
>
asin(x) arcsin(xi ), −1 ≤ xi ≤ 1
>
> >
> >
> >
>
>
> >
> >
> >
>
acos(x) arccos(xi ), −1 ≤ xi ≤ 1
>
> >
> >
> >
>
>
: >
; >
: >
;
atan(x) arctan(xi )

The vector x can be either a row vector or a column vector and y has the same shape. Write a script file that plots these
functions in succession with two-second pauses in between the plots.

P1.1.2 Define the function 8 p


> 1 − (x − 1)2 0≤x ≤2
< p
>
2
f (x) = p1 − (x − 3) 2<x ≤4
.
> 1 − (x − 5) 2 4<x ≤6
>
: p
1 − (x − 7)2 6<x ≤8
Set up a length-201 vector y with the property that yi = f (8 ∗ (i − 1)/200) for i = 1:201.

1.2 More Vectors, More Plotting, and Now Matrices


We continue to refine our vector intuition by considering several additional plotting situations. New control
structures are introduced and some of Matlab’s matrix algebra capabilities are presented.

1.2.1 Vectorizing Function Evaluations


Consider the problem of plotting the rational function

 8
x
1+
 24 
f(x) =  
x x2
1− +
12 384

across the interval [0, 1]. (This happens to be an approximation to the function ex .) Here is a scalar approach:

n = 200;
x = linspace(0,1,n);
y = zeros(1,n);
for k=1:n
y(k) = ((1 + x(k)/24)/(1 - x(k)/12 + (x(k)/384)*x(k)))^8;
end
plot(x,y)

However, by using vector operations that are available in Matlab, it is possible to replace the loop with a
single, vector-level command:
14 CHAPTER 1. POWER TOOLS OF THE TRADE

% Script File: ExpPlot


% Examines the function f(x) = ((1 + x/24)/(1 - x/12 + x^2/384))^8
% as an approximation to exp(z) across [0,1].
close all
x = linspace(0,1,200);
num = 1 + x/24;
denom = 1 - x/12 + (x/384).*x;
quot = num./denom;
y = quot.^8;
plot(x,y,x,exp(x))

The assignment to y involves the familiar operations of vector scale, vector add, and vector subtract, and
the not-so-familiar operations of pointwise vector multiply, pointwise vector divide, and pointwise vector
exponentiation. To clarify each of these operations, we break the script down into more elemental steps:

z = (1/24)*x;
num = 1 + z;
w = x/384;
q = w.*x;
denom = 1 - z/2 + q;
quot = num./denom;
y = quot.^8;

Matlab supports scalar-vector multiplication. The command


z = (1/24)*x;
multiplies every component in x by (1/24) and stores the result in z. The vector z has exactly the same
length and orientation as x. The command
num = 1 + z;
adds 1 to every component of z and stores the result in num. Thus num = 1 + [20 30 40] and num = [21
31 41] are equivalent. Strictly speaking, scalar-plus-vector is not a legal vector space operation, but it is a
very handy Matlab feature.
Now let us produce the vector of denominator values. The command
w = x/384
is equivalent to
w = (1/384)*x
It is also the same as w = x*(1/384). The command
q = w.*x
makes use of pointwise vector multiplication and produces a vector q with the property that each component
is equal to the product of the corresponding components in w and x. Thus
q = [2 3 4].*[20 30 50]
is equivalent to
q = [40 90 200]
The same rules apply when the two operands are column vectors. The key is that the vectors to be multiplied
have to be identical in length and orientation. The command
1.2. MORE VECTORS, MORE PLOTTING, AND NOW MATRICES 15

The Tangent Function


10

tan(x)
0

−2

−4

−6

−8

−10
0 2 4 6 8 10 12 14
x

Figure 1.5 A plot of tan(x)

denom = 1 - z/2 + q

sets denom(i) to 1 - z(i)/2 + q(i) for all i. Vector addition, like vector subtraction, requires both
operands to have the same length and orientation.
The pointwise division quotient = num./denom performs as expected. The ith component of quotient
is set to num(i)/denom(i). Lastly, the command

y = quotient.^8

raises each component in quotient to the 8th power and assembles the results in the vector y.

1.2.2 Scaling and Superpositioning


Consider the plotting of the function tan(x) = sin(x)/ cos(x) across the interval [−π/2, 11π/2]. This is
interesting because the function has poles at points where the cosine is zero. The script

x = linspace(-pi/2,11*pi/2,200);
y = tan(x);
plot(x,y)

produces a plot with minimum information because the autoscaling feature of the plot function must deal
with an essentially infinite range of y-values. This can be corrected by using the axis function:

x = linspace(-pi/2,11*pi/2,200);
y = tan(x);
plot(x,y)
axis([-pi/2 9*pi/2 -10 10])

The axis function is used to scale manually the axes in the current plot, and it requires a 4-vector whose
values define the x and y ranges. In particular,

axis([xmin xmax ymin ymax])

imposes the x-axis range xmin ≤ x ≤ xmax and a y-axis range ymin ≤ y ≤ ymax. In our example, the
[−10, 10] range in the y-direction is somewhat arbitrary. Other values would work. The idea is to choose the
range so that the function’s poles are dramatized without sacrificing the quality of the plot in domains where
it is nicely behaved. (See Figure 1.5.) We mention that the command axis by itself returns the system to
16 CHAPTER 1. POWER TOOLS OF THE TRADE

the original autoscaling mode.


Another way to produce the same graph is to plot the first branch and then to reuse the function
evaluations for the remaining branches:

% Script File: TangentPlot


% Plots the function tan(x), -pi/2 <= x <= 9pi/2
close all
x = linspace(-pi/2,pi/2,40); y = tan(x); plot(x,y)
ymax = 10;
axis([-pi/2 9*pi/2 -ymax ymax])
title(’The Tangent Function’), xlabel(’x’), ylabel(’tan(x)’)
hold on
for k=1:4
xnew = x+ k*pi;
plot(xnew,y);
end
hold off

This script has a number of new features that require explanation. The hold on command effectively tells
Matlab to superimpose all subsequent plots on the current figure window. Each time through the for-loop,
a different branch is plotted. The axis scaling is frozen during these computations. The xnew calculation
produces the required x-domain for each branch plot. During the kth pass through the loop, the expression
xnew + k*pi establishes a vector of equally spaced values across the interval

[−π/2 + kπ, −π/2 + (k + 1)π].

The same vector of tan-evaluations is used in each branch plot. Observe that with superpositioning we
produce a plot with only one-fifth the number of tan evaluations that our initial solution required.
The hold off command shuts down the superpositioning feature and sets the stage for “normal” plotting
thereafter.
Another way that different graphs can be superimposed in the same plot is by calling plot with an
extended parameter list. Suppose we want to plot the functions sin(2πx) and cos(2πx) across the interval
[0, 1] and to mark the point where they intersect. The script

x = linspace(0,1,200); y1 = sin(2*pi*x); y2 = cos(2*pi*x);


plot(x,y1)
hold on
plot(x,y2,’-’)
plot([1/8 5/8],[1/sqrt(2) -1/sqrt(2)],’*’)
hold off

accomplishes this task. (See Figure 1.6.) The first three-argument call to plot uses a dashed line to produce
the graph of cos(2πx). Other line designations are possible √ (e.g., ’–’,’-.’). The
√ second three-argument call
to plot places an asterisk at the intersection points (1/8, 1/ 2) and (5/8, −1/ 2). Other point designations
are possible (e.g., ’+’, ’.’, ’o’.) The key idea is that when plot is used to draw a graph, an optional third
parameter can be included that specifies the line style. This parameter is a string that specifies the “nature
of the pen” that is doing the drawing. Colors may also be specified. (See §1.7.6.) The superpositioning can
also be achieved as follows:

% Script File: SineAndCosPlot


% Plots the functions sin(2*pi*x) and cos(2*pi*x) across [0,1]
% and marks their intersection.
close all
x = linspace(0,1,200); y1 = sin(2*pi*x); y2 = cos(2*pi*x);
plot(x,y1,x,y2,’--’,[1/8 5/8],[1/sqrt(2) -1/sqrt(2)],’*’)
1.2. MORE VECTORS, MORE PLOTTING, AND NOW MATRICES 17

0.8

0.6

0.4

0.2

−0.2

−0.4

−0.6

−0.8

−1
0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1

Figure 1.6 Superpositioning

This illustrates plot’s “multigraph” capability. The syntax is as follows:


plot(hFirst Graph Specificationi,...,hLast Graph Specificationi)
where each graph specification has the form
hVectori,hVectori,hString (optional)i
If some of the string arguments are missing, then Matlab chooses them in a way that fosters clarity in the
overall plot.

1.2.3 Polygons
Suppose that we have a polygon with n vertices. If x and y are column vectors that contain the coordinate
values, then
plot(x,y)
does not display the polygon because (xn , yn ) is not connected to (x1 , y1 ). To rectify this we merely “tack
on” an extra copy of the first point:
x = [x;x(1)];
y = [y;y(1)];
plot(x,y)
Thus, the three points (1, 2), (4, −2), and (3, 7) could be represented with the three-vectors x = [1 4 3]
and y = [2 -2 7]. The x and y updates yield x = [1 4 3 1] and y = [2 -2 7 2]. Plotting the revised
y against the revised x displays the triangle with the designated vertices.
The preceding “concatenation” of a component to a vector is a special case of a general operation whereby
vectors can be glued together. If r1, r2,...,rm are row vectors, then
v = [ r1 r2 ... rm]
is also a row vector obtained by placing the component vectors r1,...,rm side by side. For example,
v = [linspace(1,10,10) linspace(20,100,9)];
is equivalent to
v = [ 1 2 3 4 5 6 7 8 9 10 20 30 40 50 60 70 80 90 100];
18 CHAPTER 1. POWER TOOLS OF THE TRADE

Similarly, if c1, c2,..., cm are column vectors, then

v = [ c1 ; c2 ; ... ; cm]

is also a column vector, obtained by stacking c1,...,cm.


Continuing with our polygon discussion, assume that we have executed the commands

t = linspace(0,2*pi,361);
c = cos(t);
s = sin(t);
plot(c,s)
axis off equal

The object displayed is a regular 360-gon with “radius” 1. The command axis equal ensures that the
x-distance per pixel is the same as the y-distance per pixel. This is important in this application because a
regular polygon would not look regular if the two scales were different.
With the preceding sine/cosine vectors computed, it is possible to display various other regular n-gons
simply by connecting appropriate subsets of points. For example,

x = [c(1) c(121) c(241) c(361)];


y = [s(1) s(121) s(241) s(361)];
plot(x,y)

plots the equilateral triangle whose vertices are at the 0o , 120o , and 240o points along the unit circle. This
kind of non-unit stride subvector extraction can be elegantly handled in Matlab using the colon notation.
The preceding triplet of commands is equivalent to

x = c(1:120:361);
y = s(1:120:361);
plot(x,y)

More generally, if sides is a positive integer that is a divisor of 360, then

x = c(1:(360/sides):361);
y = s(1:(360/sides):361);
plot(x,y)

plots a regular polygon with that number of sides. Here is a script that displays nine regular polygons in
nine separate subwindows:

% Script File: Polygons


% Plots selected regular polygons.
close all
theta = linspace(0,2*pi,361);
c = cos(theta);
s = sin(theta);
k=0;
for sides = [3 4 5 6 8 10 12 18 24]
stride = 360/sides;
k=k+1;
subplot(3,3,k)
plot(c(1:stride:361),s(1:stride:361))
axis equal
end

Figure 1.7 shows what is produced when this script is executed.


1.2. MORE VECTORS, MORE PLOTTING, AND NOW MATRICES 19

1 1 1

0.5 0.5 0.5

0 0 0

−0.5 −0.5 −0.5

−1 −1 −1
−1 0 1 −1 0 1 −1 0 1

1 1 1

0.5 0.5 0.5

0 0 0

−0.5 −0.5 −0.5

−1 −1 −1
−1 0 1 −1 0 1 −1 0 1

1 1 1

0.5 0.5 0.5

0 0 0

−0.5 −0.5 −0.5

−1 −1 −1
−1 0 1 −1 0 1 −1 0 1

Figure 1.7 Regular polygons

The key new feature in Polygons is subplot. The command subplot(3,3,k) says “break up the current
figure window into a 3-by-3 array of subwindows, and place the next plot in the kth one of these.” The
subwindows are indexed as follows:
1 2 3
4 5 6
7 8 9
In general, subplot(m,n,k) splits the current figure into an m-row by n-column array of subwindows that
are indexed left to right, top to bottom.

1.2.4 Some Matrix Computations


Let’s consider the problem of plotting the function

f(x) = 2 sin(x) + 3 sin(2x) + 7 sin(3x) + 5 sin(4x)

across the interval [−10, 10]. The scalar-level script

n = 200;
x = linspace(-10,10,n)’;
y = zeros(n,1);
for k=1:n
y(k) = 2*sin(x(k)) + 3*sin(2*x(k)) + 7*sin(3*x(k)) + 5*sin(4*x(k));
end
plot(x,y)
title(’f(x) = 2sin(x) + 3sin(2x) + 7sin(3x) +5sin(4x)’)

does the trick. (See Figure 1.8.) Notice that x and y are column vectors. The sin evaluations can be
vectorized giving this superior alternative:

n = 200;
x = linspace(-10,10,n)’;
y = 2*sin(x) + 3*sin(2*x) + 7*sin(3*x) + 5*sin(4*x);
plot(x,y)
title(’f(x) = 2sin(x) + 3sin(2x) + 7sin(3x) +5sin(4x)’)
20 CHAPTER 1. POWER TOOLS OF THE TRADE

f(x) = 2sin(x) + 3sin(2x) + 7sin(3x) +5sin(4x)


20

15

10

−5

−10

−15

−20
−10 −8 −6 −4 −2 0 2 4 6 8 10

Figure 1.8 A sum of sines

But any linear combination of vectors is “secretly” a matrix-vector product. That is,
         
3 5 8 1 3 5 8 1  
 1   0   3   6   1 0 3 6  2
         
 4   3   3   8   4 3 3 8  3 

2          
+3 8 +7 1 +5 7  =  7 8 1 7  7 .
 7         
 2   4   1   0   2 4 1 0  5
8 2 1 9 8 2 1 9

Matlab supports matrix-vector multiplication, and the script


A = [3 5 8 1; 1 0 3 6; 4 3 3 8; 7 8 1 7; 2 4 1 0; 8 2 1 9];
y = A*[2;3;7;5];
shows how to initialize a small matrix and engage it in a matrix-vector product. Note that the matrix is
assembled row by row with semicolons separating the rows. Spaces separate the entries within a row. An
ellipsis (...) can be used to spread a long command over more than one line, which is sometimes useful for
clarity:
A = [3 5 8 1;...
1 0 3 6;...
4 3 3 8;...
7 8 1 7;...
2 4 1 0;...
8 2 1 9];
y = A*[2;3;7;5];
In the sum-of-sines plotting problem, the vector y can also be constructed as follows:

n = 200; m = 4;
x = linspace(-10,10,n)’; A = zeros(n,m);
for j=1:m
for k=1:n
A(k,j) = sin(j*x(k));
end
end
y = A*[2;3;7;5];
plot(x,y)
title(’f(x) = 2sin(x) + 3sin(2x) + 7sin(3x) + 5sin(4x)’)
1.2. MORE VECTORS, MORE PLOTTING, AND NOW MATRICES 21

This illustrates how a matrix can be initialized at the scalar level. But a matrix is just an aggregation of its
columns, and Matlab permits a column-by-column synthesis, bringing us to the final version of our script:

% Script File: SumOfSines


% Plots f(x) = 2sin(x) + 3sin(2x) + 7sin(3x) + 5sin(4x)
% across the interval [-10,10].
close all
x = linspace(-10,10,200)’;
A = [sin(x) sin(2*x) sin(3*x) sin(4*x)];
y = A*[2;3;7;5];
plot(x,y)
title(’f(x) = 2sin(x) + 3sin(2x) + 7sin(3x) + 5sin(4x)’)

An expression of the form

[ hColumn 1i hColumn 2i ... hColumn mi ]

is a matrix with m columns. Of course, the participating column vectors must have the same length.
Another way to initialize A is to use a single loop whereby each pass sets up a single column:

n = 200;
m = 4;
A = zeros(n,m);
for j=1:m
A(:,j) = sin(j*x);
end

The notation A(:,j) names the jth column of A. Notice that the size of A is established with a call to zeros.
The size function can be used to determine the dimensions of any active variable. (Recall that all variables
are treated as matrices.) Thus, the script

A = [1 2 3;4 5 6];
[r,c] = size(A);

assigns 2 (the row dimension) to r and 3 (the column dimension) to c. Many Matlab functions return
more than one value and size is our first exposure to this. Note that the output values are enclosed with
square brackets.
Matrices can also be built up by row. In SumOfSines, the kth row of A is given by sin(x(k)*(1:4)) so
we also initialize A as follows:

n = 200;
m = 4;
A = zeros(n,m);
for k=1:n
A(k,:) = sin(x(k)*(1:m));
end

The notation A(k,:) identifies the kth row of A.


As a final example, suppose that we want to plot both of the functions

f(x) = 2 sin(x) + 3 sin(2x) + 7 sin(3x) + 5 sin(4x)


g(x) = 8 sin(x) + 2 sin(2x) + 6 sin(3x) + 9 sin(4x)

in the same window. Obviously, a double application of the preceding ideas solves the problem:
22 CHAPTER 1. POWER TOOLS OF THE TRADE

n = 200;
x = linspace(-10,10,n)’;
A = [sin(x) sin(2*x) sin(3*x) sin(4*x)];
y1 = A*[2;3;7;5];
y2 = A*[8;2;6;9];
plot(x,y1,x,y2)

But a set of matrix-vector products that involve the same matrix is “secretly” a single matrix-matrix product:
     
1 2 5 19 

= 

3 4 7 43 
     
1 2 5 6 19 22
≡ = .
      3 4 7 8 43 50
1 2 6 22 

= 

3 4 8 50

Since Matlab supports matrix-matrix multiplication, our script transforms to


n = 200;
x = linspace(-10,10,n)’;
A = [sin(x) sin(2*x) sin(3*x) sin(4*x)];
y = A*[2 8;3 2;7 6;5 9];
plot(x,y(:,1),x,y(:,2))
But the plot function can accept matrix arguments. The command
plot(x,y(:,1),x,y(:,2))
is equivalent to
plot(x,y)
and so we obtain
% Script File: SumOfSines2
% Plots the functions
% f(x) = 2sin(x) + 3sin(2x) + 7sin(3x) + 5sin(4x)
% g(x) = 8sin(x) + 2sin(2x) + 6sin(3x) + 9sin(4x)
% across the interval [-10,10].
close all
n = 200;
x = linspace(-10,10,n)’;
A = [sin(x) sin(2*x) sin(3*x) sin(4*x)];
y = A*[2 8;3 2;7 6;5 9];
plot(x,y)

In general, plotting a matrix against a vector is the same thing as plotting each of the matrix columns against
the vector. Of course, the row dimension of the matrix must equal the length of the vector.
It is also possible to plot one matrix against another. If X and Y have the same size, then the corresponding
columns will be plotted against each other with the command plot(X,Y).
Finally, we mention the “backslash” operator that can be invoked whenever the solution to a linear
system of algebraic equations is required. For example, suppose we want to find scalars α1 , . . . , α4 so that if

f(x) = α1 sin(x) + α2 sin(2x) + α3 sin(3x) + α4 sin(4x),

then f(1) = −2, f(2) = 0, f(3) = 1, and f(4) = 5. These four stipulations imply
1.2. MORE VECTORS, MORE PLOTTING, AND NOW MATRICES 23

α1 sin(1) + α2 sin(2) + α3 sin(3) + α4 sin(4) = −2


α1 sin(2) + α2 sin(4) + α3 sin(6) + α4 sin(8) = 0
α1 sin(3) + α2 sin(6) + α3 sin(9) + α4 sin(12) = 1
α1 sin(4) + α2 sin(8) + α3 sin(12) + α4 sin(16) = 5

That is,
    
sin(1) sin(2) sin(3) sin(4) α1 −2
 sin(2) sin(4) sin(6) sin(8)   α2   0 
  = 
 sin(3) sin(6) sin(9) sin(12)   α3   1 .
sin(4) sin(8) sin(12) sin(16) α4 5

Here is how to set up and solve this 4-by-4 linear system:

X = [1 2 3 4 ; 2 4 6 8 ; 3 6 9 12 ; 4 8 12 16];
Z = sin(X);
f = [-2; 0; 1; 5]
alpha = Z\f

Observe that sin applied to a matrix returns the matrix of corresponding sine evaluations. This is typical of
many of Matlab’s built-in functions. For linear system solving, the backslash operator requires the matrix
of coefficients on the left and the right hand side vector (as a column) on the right. The solution to the
preceding example is  
−0.2914
 −8.8454 
α= 
 −18.8706  .
−11.8279

Problems

P1.2.1 Suppose z = [10 40 20 80 30 70 60 90]. Indicate the vectors that are specified by z(1:2:7), z(7:-2:1), and z([3 1
4 8 1]).

P1.2.2 Suppose z = [10 40 20 80 30 70 60 90]. What does this vector look like after each of these commands?

z(1:2:7) = zeros(1,4)
z(7:-2:1) = zeros(1,4)
z([3 4 8 1]) = zeros(1,4)

P1.2.3 Given that the commands

x = linspace(0,1,200);
y = sqrt(1-x.^2);

have been carried out, show how to produce a plot of the circle x2 + y2 = 1 without any additional square roots or trigonometric
evaluations.

P1.2.4 Produce a single plot that displays the graphs of the functions sin(kx) across [0, 2π], k = 1:5.

P1.2.5 Assume that m is an initialized positive integer. Write a Matlab script that plots in a single window the functions x,
x2 , x3 , . . . , xm across the interval [0, 1].

P1.2.6 Assume that x is an initialized Matlab array and that m is a positive integer. Using the ones function, the pointwise
array multiply operator .*, and Matlab’s ability to scale and add arrays, write a fragment that computes an array y with the
property that the ith component of y has the following value:
m
X xki
yi = .
k=0
k!
24 CHAPTER 1. POWER TOOLS OF THE TRADE

P1.2.7 Write a Matlab fragment to plot the following ellipses in the same window:
Ellipse 1: x1 (t) = 3 + 6 cos(t) y1 (t) = −2 + 9 sin(t)
Ellipse 2: x2 (t) = 7 + 2 cos(t) y2 (t) = 8 + 6 sin(t)

P1.2.8 Consider the following Matlab script:

x = linspace(0,2*pi);
y = sin(x);
plot(x/2,y)
hold on
for k=1:3
plot((k*pi)+x/2,y)
end
hold off

What function is plotted and what is the range of x?

P1.2.9 Assume that x, y, and z are Matlab arrays initialized as follows:

x = linspace(0,2*pi,100);
y = sin(x);
z = exp(-x);

Write a Matlab fragment that plots the function e−x sin(x) across the interval [0, 4π]. The fragment should not involve
any additional calls to sin or exp. Hint: exploit the fact that sin has period 2π and that the exponential function satisfies
ea+b = ea eb .

P1.2.10 Modify the script SumOfSines so that f (x) = 2 sin(x) + 3 sin(2x) + 7 sin(3x) + 5 sin(4x) is plotted in one window and
its derivative in another. Use subplot placing one window above the other. Your implementation should not involve any loops
and should have appropriate titles on the plots.

1.3 Building Exploratory Environments


A consequence of Matlab’s friendliness and versatility is that it encourages the exploration of mathematical
and algorithmic ideas. Many computational scientists like to precede the rigorous analysis of a problem with
Matlab-based experimentation. We use three examples to show this, learning many new features of the
system as we go along.

1.3.1 The Up/Down Sequence


Suppose x1 is a given positive integer and that for k ≥ 1 we define the sequence x1 , x2, . . . as follows:

 xk /2 if xk is even
xk+1 = .

3xk + 1 if xk is odd

Thus, if x1 = 7, then the following sequence unfolds:

7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1, 4, 2, 1, 4, 2, 1, . . . .

We will call this the up/down sequence for obvious reasons. Note that it cycles once the value of 1 is reached.
A number of interesting questions are suggested:
• Does the sequence always reach the cycling stage?
• Let n be the smallest index for which xn = 1. How does n behave as a function of the initial value x1 ?
• Are there any systematic patterns in the sequence worth noting?
Our goal is to develop a script file that can be used to shed light on these and related issues.
We start with a script that solicits a starting value and then generates the sequence, assembling the
values in a vector x:
1.3. BUILDING EXPLORATORY ENVIRONMENTS 25

x(1) = input(’Enter initial positive integer:’);


k = 1;
while (x(k) ~= 1)
if rem(x(k),2) == 0
x(k+1) = x(k)/2;
else
x(k+1) = 3*x(k)+1;
end
k = k+1;
end

The input command is used to set up x(1). It has the form

input(hstring messagei)

and prompts for keyboard input. For example,

Enter initial positive integer:

Whatever number you type, it is assigned to x(1).


After x(1) is initialized, the generation of the sequence takes place under the auspices of a while-loop.
Each pass through the loop requires a test of the current x(k) in accordance with the rule for x(k+1) given
earlier. This is handled by an if-then-else.
Let’s look at the details. In Matlab, a test of the form x(k)==10 renders a one if it is true and a zero
if it is false.1 All the usual comparisons are supported:

Notation Meaning
< less than
<= less than or equal
== equal
>= greater than or equal
> greater than
~= not equal

A while-loop has the form

while hConditioni
hStatementsi
end

An if-then-else is structured as follows:

if hConditioni
hStatementsi
else
hStatementsi
end

Both of these control structures operate in the usual way. The condition is numerically valued, and is
interpreted as true if it is nonzero.
The remainder function rem is used to check whether or not x(k) is even. Assuming that a and b are
positive integers, a call of the form rem(a,b) returns the remainder when b is divided into a.
Now one of the things we do not know is whether or not the up/down sequence reaches 1. To guard
against the production of an unacceptably large x-vector, we can put a limit on how many terms to generate.
Setting that limit to 500 and presizing x to that length, we obtain
1 Remember, there is no boolean type in Matlab.
26 CHAPTER 1. POWER TOOLS OF THE TRADE

x = zeros(500,1);
x(1) = input(’Enter initial positive integer:’);
k = 1;
while ((x(k) ~= 1) & (k < 500))
if rem(x(k),2) == 0
x(k+1) = x(k)/2;
else
x(k+1) = 3*x(k)+1;
end
k = k+1;
end
n = k;
x = x(1:n);

The index of the first sequence member that equals 1 is assigned to n and x is “trimmed” to that length
with the assignment x = x(1:n). Notice the use of the and operator & in the while-loop condition. The
and, or, and not operations are all possible in Matlab :

Notation Meaning
& and
| or
~ not
xor exclusive or

The usual definitions apply with the understanding that 1 and 0 are used for true and false respectively.
Thus (x(k) == 1) & (k < 500)) has the value of 1 if x(k) equals 1 and k is strictly less than 500. If either
of these conditions is false, then the logical expression equals 0.
Computing x(1:n) brings us to the stage where we must decide how to display it and its properties. Of
course, we could display the vector simply by leaving off the semicolon in x = x(1:n);. Alternatively, we
can make use of fprintf’s vectorizing capability:
fprintf(’%10d\n’,x)
When a vector like x is passed to fprintf in this way. it just keeps cycling through the format string until
every vector component is processed.
Among the numerical properties of x that are interesting are the maximum value and the number of
integers ≤ x1 that are “hit” by the up/down process:
[xmax,imax] = max(x);
disp(sprintf(’\n x(%1.0f) = %1.0f is the max.’,imax,xmax))
density = sum(x<=x(1))/x(1);
disp(sprintf(’ The density is %5.3f.’,density))
When the max function is applied to a vector, it returns the maximum value and the index where it occurs.
It is also possible to use max in an expression. For example,
GrowthFactor = max(x)/x(1)
assigns to GrowthFactor the ratio of the largest value in x to x(1). Notice the use of the 1.0f format. For
integers greater than one digit in length, extra space is accorded as necessary. This ensures that there is no
gap between the displayed subscript and the right parenthesis, a small aesthetic point.
The assignment to density requires two explanations. First, it is legal to compare vectors in Matlab.
The comparison x<=x(1) returns a vector of 0’s and 1’s that is the same size as x. If x(k) <= x(1) is true,
then the kth component of this vector is one. The sum function applied to a vector sums its entries. Thus
sum(x<=x(1)) is precisely the number of components in x that are less than or equal to x(1).
Graphical display is also in order and can help us appreciate the “flow of events” as the sequence winds
its way to unity:
1.3. BUILDING EXPLORATORY ENVIRONMENTS 27

close all
figure
plot(x)
title(sprintf(’x(1) = %1.0f, n = %1.0f’,x(1),n));
figure
plot(sort(x,’descend’))
title(’Sequence values sorted.’)
I = find(rem(x(1:n-1),2));
if length(I)>1
figure
plot((1:n),zeros(1,n),I+1,x(I+1),I+1,x(I+1),’*’)
title(’Local Maxima’)
end

This script involves a number of new features. First, the command plot(x) plots the components of x
against their indices. It is equivalent to plot((1:n)’,x).
Second, the sort function is used to produce a plot of the sequence with its values ordered from small
to large. If v is a vector with length m, then u = sort(v) permutes the values in v and assigns them to u so
that
u1 ≤ u2 ≤ u3 ≤ · · · ≤ um .
The command sort(x,’descend’) produces a “big-to-little” sort.
Third, the expression rem(x(1:n-1),2) == 1 returns a 0-1 vector that designates which components of
x(1:n-1) are odd. The function rem, like many of Matlab’s built-in functions, accepts vector arguments
and merely returns a vector of the function applied to each of the components. The find function returns
a vector of subscripts that designate which entries in a vector are nonzero. Thus, if

x(1:n-1) = [ 17 52 26 13 40 20 10 5 16 8 4 2]’

and r = rem(x(1:n-1),2) and I = find(r), then

r(1:n-1) = [ 1 0 0 1 0 0 0 1 0 0 0 0]’

and I = [ 1 4 8]’. If the vector I is nonempty, then a plot of I+1 is produced showing the pattern of the
sequence’s “local maxima.” (The vector I+1 contains the indices of values in x(1:n-1) that are produced
by the “up operation” 3xk + 1.)
The last thing to discuss is figure. In all prior examples, our plots have appeared in a single window.
New plots erase old ones. But with each reference to figure, a new window is opened. Figures are indexed
from 1 and so figure(1) refers to a plot of x, figure(2) designates the plot of x sorted, and if I is nonempty,
then figure(3) contains a plot of its local maxima. The close all statement clears all windows and ensures
that the figure indexing starts at 1.
The script UpDown incorporates all of these features and by repeatedly running it we could bolster our
intuition about the up/down sequence. To make this enterprise more convenient, we write a second script
file that invokes UpDown:

% Script File: RunUpDown


% Environment for studying the up/down sequence.
% Stores selected results in file UpDownOutput.
while(input(’Another Example? (1=yes, 0=no)’))
diary UpDownOutput
UpDown
diary off
if (input(’Keep Output? (1=yes, 0=no)’)~=1)
delete UpDownOutput
end
end
28 CHAPTER 1. POWER TOOLS OF THE TRADE

By using this script we can keep trying new starting values until one of special interest is found. The
while-loop keeps running as long as you want to test another starting value. Before UpDown is run, the
diary UpDownOutput
command creates a file called UpDownOutput. Everything that is now written to the command window during
the execution of UpDown is now also written to UpDownOutput. After UpDown is run, we turn off this feature
with
diary off
The script then asks if the output should be kept. If not, then the file UpDownOutput is deleted. Note that
it is possible to record several possible runs of UpDown, but as soon as the if condition is true, everything
is erased. The advantage of writing output to a file is that it can then be edited to make it look nice. For
example,
For starting value x(1) = 511, the UpDown sequence is

x(1:62) =

511 1534 767 2302 1151 3454 1727 5182 2591 7774
3887 11662 5831 17494 8747 26242 13121 39364 19682 9841
29524 14762 7381 22144 11072 5536 2768 1384 692 346
173 520 260 130 65 196 98 49 148 74
37 112 56 28 14 7 22 11 34 17
52 26 13 40 20 10 5 16 8 4
2 1
The figures from the final UpDown run are available for printing as well.

1.3.2 Random Processes


Many simulations performed by computational scientists involve random processes. In order to implement
these on a computer, it is necessary to be able to generate sequences of random numbers. In Matlab this is
done with the built-in functions rand and randn. The command x = rand(1000,1) creates a length-1000
column vector of real numbers chosen randomly from the interval (0, 1). The uniform(0, 1) distribution is
used, meaning that if 0 < a < b < 1, then the fraction of values that fall in the range [a, b] will be about b −a.
The randn function should be used if a sequence of normally distributed random numbers is desired. The
underlying probability distribution is the normal(0, 1) distribution. A brief, graphically oriented description
of these functions should clarify their statistical properties.
Histograms are a common way of presenting statistical data. Here is a script that illustrates rand and
randn using this display technique:
% Script File: Histograms
% Histograms of rand(1000,1) and randn(1000,1).
close all
subplot(2,1,1)
x = rand(1000,1);
hist(x,30)
axis([-1 2 0 60])
title(’Distribution of Values in rand(1000,1)’)
xlabel(sprintf(’Mean = %5.3f. Median = %5.3f.’,mean(x),median(x)))
subplot(2,1,2)
x = randn(1000,1);
hist(x,linspace(-2.9,2.9,100))
title(’Distribution of Values in randn(1000,1)’)
xlabel(sprintf(’Mean = %5.3f. Standard Deviation = %5.3f’,mean(x),std(x)))
1.3. BUILDING EXPLORATORY ENVIRONMENTS 29

Distribution of Values in rand(1000,1)


60

50

40

30

20

10

0
−1 −0.5 0 0.5 1 1.5 2
Mean = 0.502. Median = 0.510.

Distribution of Values in randn(1000,1)


35

30

25

20

15

10

0
−3 −2 −1 0 1 2 3
Mean = −0.043. Standard Deviation = 0.943

Figure 1.9 The uniform and normal distributions

(See Figure 1.9.) Notice that rand picks values uniformly from [0, 1] while the distribution of values in
randn(1000,1) follows the familiar “bell shaped curve.” The mean, median, and standard deviation func-
tions mean, median, and std are referenced. The histogram function hist can be used in several ways
and the script shows two of the possibilities. A reference like hist(x,30) reports the distribution of the
x-values according to where they “belong” with respect to 30 equally spaced bins spread across the interval
[min(x), max(x)]. The bin locations can also be specified by passing hist a vector in the second param-
eter position (e.g., hist(x,linspace(-2.9,2.9,100))). This is done for the histogram of the normally
distributed data.
Building on rand and randn through translation and scaling, it is possible to produce random sequences
with specified means and variances. For example,

x = 10 + 5*rand(n,1);

generates a sequence of uniformly distributed numbers from the interval (10, 15). Likewise,

x = 10 + 5*randn(n,1);

produces a sequence of normally distributed random numbers with mean 10 and standard deviation 5.
It is possible to generate random integers using rand (or randn) and the floor function. The command
z = floor(6*rand(n,1)+1) computes a random vector of integers selected from {1, 2, 3, 4, 5, 6} and assigns
them to z. This is because floor rounds to −∞. The command z = ceil(6*x) is equivalent because ceil
rounds toward +∞. In either case, the vector z looks like a recording of n dice throws. Notice that floor
and ceil accept vector arguments and return vectors of the same size. (See also fix and round.) Here is a
script that simulates 1000 rolls of a pair of dice, displaying the outcome in histogram form:

% Script File: Dice


% Simulates 1000 rollings of a pair of dice.
close all
First = 1 + floor(6*rand(1000,1));
Second = 1 + floor(6*rand(1000,1));
Throws = First + Second;
hist(Throws, linspace(2,12,11));
title(’Outcome of 1000 Dice Rolls.’)
30 CHAPTER 1. POWER TOOLS OF THE TRADE

1.5

0.5

−0.5

−1

−1.5
−1.5 −1 −0.5 0 0.5 1 1.5

Figure 1.10 A target

Random simulations can be used to answer “nonrandom” questions. Suppose we throw n darts at the
circle-in-square target depicted in Figure 1.10. Assume that the darts land anywhere on the square with
equal probability and that the square has side 2 and center (0, 0). After a large number of throws, the
fraction of the darts that land inside the circle should be approximately equal to π/4, the ratio of the circle
area to the square’s area. Thus,

Number of Throws Inside the Circle


π ≈ 4 .
Total Number of Throws

By simulating the throwing of a large number of darts, we can produce an estimate of π. Here is a script
file that does just that:

Monte Carlo Estimate of Pi = 3.157


3.26

3.24

3.22

3.2

3.18

3.16

3.14

3.12

3.1

3.08
0 50 100 150 200 250 300 350 400 450 500
Hundreds of Trials

Figure 1.11 A Monte Carlo estimate of π


1.3. BUILDING EXPLORATORY ENVIRONMENTS 31

% Script File: Darts


% Estimates pi using random dart throws.
close all
rand(’seed’,.123456);
NumberInside = 0;
PiEstimate = zeros(500,1);
for k=1:500
x = -1+2*rand(100,1);
y = -1+2*rand(100,1);
NumberInside = NumberInside + sum(x.^2 + y.^2 <= 1);
PiEstimate(k) = (NumberInside/(k*100))*4;
end
plot(PiEstimate)
title(sprintf(’Monte Carlo Estimate of Pi = %5.3f’,PiEstimate(500)));
xlabel(’Hundreds of Trials’)

(See Figure 1.11.) Notice that the estimated values are gradually improving with n, but that the “progress”
towards 3.14159... is by no means steady or fast. Simulation in this spirit is called Monte Carlo. The
command rand(’seed’,.123456) starts the random number sequence with a prescribed seed. This enables
one to repeat the random simulation with exactly the same sequence of underlying random numbers.
The any and all functions indicate whether any or all of the components of a vector are nonzero. Thus,
if x and y are vectors of the same length, then a = any( x.b2 + y.b2 <= 1) assigns to a the value “1” if
there is at least one (xi , yi ) in the unit circle and “0” otherwise. Similarly, b = all( x.b2 + y.b2 <= 1)
assigns “1” to b if all the (xi , yi ) are in the unit circle and assigns “0” otherwise.

1.3.3 Polygon Smoothing


If x and y are n + 1-vectors (of the same type) and x1 = xn+1 and y1 = yn+1 , then plot(x,y,x,y,’*’)
displays the polygon obtained by connecting the points (x1 , y1 ), . . . , (xn+1 , yn+1 ) in order. If we compute

xnew = [(x(1:n)+x(2:n+1))/2;(x(1)+x(2))/2];
ynew = [(y(1:n)+y(2:n+1))/2;(y(1)+y(2))/2];
plot(xnew,ynew)

then a new polygon is displayed that is obtained by connecting the side midpoints of the original polygon.
We wish to explore what happens when this process is repeated.
The first issue that we have to deal with is how to specify the “starting polygon” such as the one displayed
in Figure 1.12. One approach is to use the ginput command that supports mouseclick input. It returns the
x-y-coordinates of the click with respect to the current axis. Under the control of a for-loop an assignment
of the form [x(k),y(k)] = ginput(1) could be used to places the coordinates of the kth vertex in x(k)
and y(k), e.g.,

n = input(’Enter the number of edges:’);


figure
axis([0 1 0 1])
axis square
hold on
x = zeros(n,1);
y = zeros(n,1);
for k=1:n
title(sprintf(’Click in %2.0f more points.’,n-k+1))
[x(k) y(k)] = ginput(1);
plot(x(1:k),y(1:k), x(1:k),y(1:k),’*’)
end
32 CHAPTER 1. POWER TOOLS OF THE TRADE

x = [x;x(1)];
y = [y;y(1)];
plot(x,y,x,y,’*’)
title(’The Original Polygon’)
hold off

The for-loop displays the sides of the polygon as it is “built up.” If we did not care about this kind of
graphical feedback as we click in the vertices, then the command [x,y] = ginput(n) could be used. This
just stores the coordinates of the next n mouseclicks in x and y. Notice how we set up an “empty” figure
with a prescribed axis in advance of the data acquisition.
Now that vertices of the starting polygon are available, the connect-the-midpoint process can begin:

k=0;
xlabel(’Click inside window to smooth, outside window to quit.’)
[a,b] = ginput(1);
v = axis;
while (v(1)<=a) & (a<=v(2)) & (v(3)<=b) & (b<=v(4));
k = k+1;
x = [(x(1:n)+x(2:n+1))/2;(x(1)+x(2))/2];
y = [(y(1:n)+y(2:n+1))/2;(y(1)+y(2))/2];
m = max(abs([x;y])); x = x/m; y = y/m;
figure
plot(x,y,x,y,’*’)
axis square
title(sprintf(’Number of Smoothings = %1.0f’,k))
xlabel(’Click inside window to smooth, outside window to quit.’)
v = axis;
[a,b] = ginput(1);
end

The command v = axis assigns to v a 4-vector [xmin , xmax , ymin , ymax ] that specifies the x and y ranges of
the current figure. The while-loop that oversees the process terminates as soon as the solicited mouseclick
falls outside the plot window. The polygons are scaled so that they are roughly the same size.
Once the execution of the loop is completed, the evolution of the smoothed polygons can be reviewed
by using figure. For example, the command figure(2) displays the polygon after two smoothings. (See
Figure 1.13.) This works because a new figure is generated each pass through the while-loop so in effect,
each plot is saved. The script Smooth encapsulates the whole process.
Problems

The Original Polygon


1

0.9

0.8

0.7

0.6

0.5

0.4

0.3

0.2

0.1

0
0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1

Figure 1.12 The initial polygon


1.4. ERROR 33

Number of Smoothings = 7
1

0.95

0.9

0.85

0.8

0.75

0.7
0.65 0.7 0.75 0.8 0.85 0.9
Click inside window to smooth, outside window to quit.

Figure 1.13 A smoothed polygon

P1.3.1 Suppose {xi} is the up/down sequence with x1 = m. Let g(m) be the index of the first xi that equals one. Plot the
values of g for m = 1:200.

P1.3.2 Consider the quadratic equation ax2 + bx + c = 0. Let P1 be the probability that this equation has complex roots,
given that the coefficients are random variables with uniform(0,1) distribution. Let P1 (n) be a Monte Carlo estimate of this
probability based on n trials. Let P2 be the probability that this equation has complex roots given that the coefficients are
random variables with normal(0,1) distribution. Let P2 (n) be a Monte Carlo estimate of this probability based on n trials.
Write a script that prints a nicely formatted table that reports the value of P1 (n) and P2 (n) for n = 100:100:800.

P1.3.3 Write a simulation that estimates the volume of {(x1, x2 , x3 , x4 ) : x21 +x22 +x23 +x24 ≤ 1}, the unit sphere in 4-dimensional
space.

P1.3.4 Let S = { (x, y) | − 1 ≤ x ≤ 1, −1 ≤ y ≤ 1}. Let S0 be the set of points in S that are closer to the point (.2, .4) than
to an edge of S. Write a Matlab script that estimates the area of S0 .

1.4 Error
Errors abound in scientific computation. Rounding errors attend floating point arithmetic, terminal screens
are granular, analytic derivatives are approximated with divided differences, a polynomial is used in lieu
of the sine function, the data acquired in a lab are correct to only three significant digits, etc. Life in
computational science is like this, and we have to build up a facility for dealing with it. In this section we
focus on the mathematical errors that arise through discretization and the rounding errors that arise due to
finite precision arithmetic.

1.4.1 Absolute and Relative Error


If x̃ approximates a scalar x, then the absolute error in x̃ is given by |x̃ − x| while the relative error is given
by |x̃ − x|/|x|. If the relative error is about 10−d , then x̃ has approximately d correct significant digits in
that there exists a number τ having the form

τ = ±(. 00 . . . 0} nd+1 nd+2 . . .) × 10g


| {z
d zeros

so that x̃ = x + τ . (Here, g is some integer.)


As an exercise in relative and absolute error, let’s examine the quality of the Stirling approximation
√  n n
Sn = 2πn , e = exp(1).
e
34 CHAPTER 1. POWER TOOLS OF THE TRADE

to the factorial function n! = 1 · 2 · · · n. Here is a script that produces a table of errors:

% Script File: Stirling


% Prints a table showing error in Stirling’s formula for n!
clc
disp(’ Stirling Absolute Relative’)
disp(’ n n! Approximation Error Error’)
disp(’----------------------------------------------------------------’)
e = exp(1);
nfact = 1;
for n = 1:13
nfact = n*nfact;
s = sqrt(2*pi*n)*((n/e)^n);
abserror = abs(nfact - s);
relerror = abserror/nfact;
s1 = sprintf(’ %2.0f %10.0f %13.2f’,n,nfact,s);
s2 = sprintf(’ %13.2f %5.2e’,abserror,relerror);
disp([s1 s2])
end

Notice how the strings s1 and s2 are concatenated before they are displayed. In general, you should think
of a string as a row vector of characters. Concatenation is then just a way of obtaining a new row vector
from two smaller ones. This is the logic behind the required square bracket.
The command clc clears the command window and moves the cursor to the top. This ensures that the
table produced is profiled nicely in the command window. Here it is:

Stirling Absolute Relative


n n! Approximation Error Error
----------------------------------------------------------------
1 1 0.92 0.08 7.79e-02
2 2 1.92 0.08 4.05e-02
3 6 5.84 0.16 2.73e-02
4 24 23.51 0.49 2.06e-02
5 120 118.02 1.98 1.65e-02
6 720 710.08 9.92 1.38e-02
7 5040 4980.40 59.60 1.18e-02
8 40320 39902.40 417.60 1.04e-02
9 362880 359536.87 3343.13 9.21e-03
10 3628800 3598695.62 30104.38 8.30e-03
11 39916800 39615625.05 301174.95 7.55e-03
12 479001600 475687486.47 3314113.53 6.92e-03
13 6227020800 6187239475.19 39781324.81 6.39e-03

1.4.2 Taylor Approximation


The partial sums of the exponential satisfy

n
X xk eη
ex = + xn+1
k! (n + 1)!
k=0

for some η in between 0 and x. The mathematics says that if we take enough terms, then the partial sums
converge. The script ExpTaylor explores this by plotting the partial sum relative error as a function of n.
1.4. ERROR 35

% Script File: ExpTaylor


% Plots, as a function of n, the relative error in the
% Taylor approximation 1 + x + x^2/2! +...+ x^n/n! to exp(x).
close all
nTerms = 50;
for x=[10 5 1 -1 -5 -10]
figure
term = 1; s = 1; f = exp(x)*ones(nTerms,1);
for k=1:nTerms, term = x.*term/k; s = s+ term; err(k) = abs(f(k) - s); end
relerr = err/exp(x);
semilogy(1:nTerms,relerr)
ylabel(’Relative Error in Partial Sum.’)
xlabel(’Order of Partial Sum.’)
title(sprintf(’x = %5.2f’,x))
end

x = 10.00
0
10

−2
10

−4
10
Relative Error in Partial Sum.

−6
10

−8
10

−10
10

−12
10

−14
10

−16
10
0 5 10 15 20 25 30 35 40 45 50
Order of Partial Sum.

Figure 1.14 Error in Taylor approximations to ex , x = 10

When plotting numbers that vary tremendously in range, it is useful to use semilogy. It works just like
plot, only the base-10 log of the y-vector is displayed. ExpTaylor produces six figure windows, one each for
the six x-values. For example, the x = 10 plot is in figure 1. By entering the command figure(1), this plot
is “brought up” by making the Figure 1 window the active window. It could then (for example) be printed.
(See Figures 1.14 and 1.15.)

1.4.3 Rounding Errors


The plots produced by ExpTaylor reveal that the mathematical convergence theory does not quite apply.
The errors do not go to zero as the number of terms in the series increases. In each case, they seem to
“bottom out” at some small value. Once that happens, the incorporation of more terms into the partial sum
does not make a difference. Moreover, by comparing the plots in Figures 1.14 and 1.15, we observe that
where the relative error bottoms out depends on x. The relative error for x = −10 is much worse than for
x = 10.
An explanation of this phenomenon requires an understanding of floating point arithmetic. Like it or
not, numerical computation involves working with an inexact computer arithmetic system. This will force us
to rethink the connections between mathematics and the development of algorithms. Nothing will be simple
ever again.
36 CHAPTER 1. POWER TOOLS OF THE TRADE

To dramatize this point, consider the plot of a rather harmless looking function: p(x) = (x − 1)6 . The
script Zoom graphs this polynomial over increasingly smaller neighborhoods around x = 1, but it uses the
formula
p(x) = x6 − 6x5 + 15x4 − 20x3 + 15x2 − 6x + 1.

x = −10.00
8
10

6
10

4
10

2
Relative Error in Partial Sum.

10

0
10

−2
10

−4
10

−6
10

−8
10

−10
10
0 5 10 15 20 25 30 35 40 45 50
Order of Partial Sum.

Figure 1.15 Error in Taylor approximations to ex , x = −10

% Script File: Zoom


% Plots (x-1)^6 near x=1 with increasingly refined scale.
% Evaluation via x^6 - 6x^5 + 15x^4 - 20x^3 + 15x^2 - 6x +1
% leads to severe cancelation.

close all
k = 0; n = 100;
for delta = [.1 .01 .008 .007 .005 .003 ]
x = linspace(1-delta,1+delta,n)’;
y = x.^6 - 6*x.^5 + 15*x.^4 - 20*x.^3 + 15*x.^2 - 6*x + ones(n,1);
k = k+1; subplot(2,3,k); plot(x,y,x,zeros(1,n))
axis([1-delta 1+delta -max(abs(y)) max(abs(y))])
end

Notice how the x-axis is plotted and how it is forced to appear across the middle of the window. (See Figure
1.16 for a display of the plots.) As we increase the “magnification,” a very chaotic behavior unfolds. It seems
that p(x) has thousands of zeros!
It turns out that if the plot is based on the formula (x − 1)6 instead of its expansion, then the expected
graph is displayed and this gets right to the heart of the example. Algorithms that are equivalent mathemat-
ically may behave very differently numerically. The time has come to look at floating point arithmetic.

1.4.4 The Floating Point Numbers


A nonzero value x in a base-2 floating point number system has the following form:

x = ±1.b1 b2 . . . bt × β e L≤e≤U
1.4. ERROR 37

−7 −12 −13
x 10 x 10 x 10
1
2
5 0.5
1

0 0 0

−1
−5 −0.5
−2
−1
0.9 1 1.1 0.99 1 1.01 0.995 1 1.005

−13 −14 −15


x 10 x 10 x 10
1.5 5
1
1
0.5
0.5

0 0 0

−0.5
−0.5
−1
−1
−1.5 −5
0.995 1 1.005 0.995 1 1.005 0.998 1 1.002

Figure 1.16 Plots of (x − 1)6 = x6 − 6x5 + 15x4 − 20x3 + 15x2 − 6x + 1 near x = 1

The bits b1 , b2 , . . . bt make up the mantissa. The exponent e is restricted to the interval [L, U ]. Zero is also a
floating point number and we assume that in its representation both the mantissa and exponent are set to
zero.
We denote the set of floating point numbers by F(t, L, U ). To emphasize the finiteness of this set, suppose
t = 2, L = −1 and U = +1. There are twelve positive floating point numbers:
 
 (1.00)2   −1 

 (1.01)   2 
2
x = × 20 .

 (1.10)2 
  1 
  2
(1.11)2

The base-2 notation is not difficult. Thus, x = (1.01)2 × 21 represents


 
1 1
1 + 0· + 1· × 2 = 2.5
2 4

There is a smallest positive floating point number (1.00 × 2−1 = .5) and a largest floating point number
(1.11 × 21 = 3.75). Moreover, the spacing between the floating point numbers is not uniform as can be seen
from this display of the positive portion of F(2, −1, 1):

t t t t t t t t t t t t
1
0 2 1 2 4
Extrapolating from this small example we identify three important numbers associated with F(t, L, U ):

m the smallest positive floating point number = 2L.


M the largest positive floating point number = (2 − 2−t )2U
eps the distance from 1 to the next largest floating point number = 2−t

Note that if x is a floating point number and 2e < x < 2e+1 , then x − 2e−t is its left “neighbor” and x + 2e−t
is its right neighbor.
38 CHAPTER 1. POWER TOOLS OF THE TRADE

Now let us talk about the errors associated with the F(t, L, U ) representation. If x is a real number, then
let fl(x) be the nearest floating point number to x. (Assume the existence of a tie-breaking rule.) Think of
fl(x) as the stored version of x. The following theorem bounds the relative error in fl(x).
Theorem 1 Suppose we are given a set of floating point numbers with mantissa length t and exponent range
[L, U ]. If x ∈ IR satisfies m < |x| < M , then
|fl(x) − x|
≤ 2−t−1 = eps
|x|
Proof Without loss of generality, assume that x is positive and that
x = (1.b1 b2 . . . bt bt+1 . . .)2 × 2e .
If x is a power of two, then the theorem obviously holds since fl(x) = x and the relative error is zero.
Otherwise we observe that the spacing of the floating point numbers at x is 2e−t . Since fl(x) is the closest
floating number to x, we have
1
|fl(x) − x| ≤ 2e−t = 2e−t−1 .
2
From the lower bound β e < x it follows that
|fl(x) − x| 2e−t−1
≤ = 2−t−1 . 
|x| 2e
Another way of saying the same thing is that
fl(x) = x(1 + δ)
where |δ| ≤ eps.
What are the values of t, L and U on a typical computer? For the widely implemented IEEE double
precision format, t = 52, L = −1022 and U = 1023. This representation fits into a 64-bit word because we
need one bit for the sign and because 11 bits are required to store e + 1023. (The last is a clever trick for
encoding the sign of the exponent.)
The quantity eps is referred to as the machine precision (a.k.a. unit roundoff) and is available in
Matlab through the built-in constant eps:
>> What_Is_eps = eps

What_Is_eps =

2.220446049250313e-016
Thus, in the IEEE floating point environment, eps = 2−52 ≈ 10−16.
IEEE floating point arithmetic is carefully designed so that when two floating point numbers are combined
via +, −, ×, or /, then the answer is the nearest floating point number to the exact answer. One way to say
this for any of these four “ops” is
fl(x op y) = (x op y)(1 + δ) |δ| ≤ eps
Thus, there is good relative error for an individual floating point operation. As we shall see, it does not
follow that sequences of floating point operations result in an answer that has O(eps) relative error.
Some simple while-loop computations can be used to glean information about the underlying floating
system. Here is a script that assigns the value of the smallest positive integer so 1 + 1/2p = 1 in floating
point arithmetic:
p = 0; y = 1; z = 1+y;
while z>1
y = y/2;
p = p+1;
z = 1+y;
end
1.4. ERROR 39

With IEEE arithmetic, p = 53. Stated another way, 1 + 1/252 can be represented exactly but 1 + 1/253
cannot.
The finiteness of the exponent range has ramifications too. A floating point operation can result in an
answer that is too big to represent. When this happens, it is called floating point overflow and a special
value called inf is produced. Here is a script that assigns to r the smallest positive integer so 2r = inf in
floating point arithmetic:
x = 1;
r = 0;
while x~=inf
x = 2*x;
r = r+1;
end
When IEEE arithmetic is used, r = 1024. In other words, 21023 can be represented but 21024 cannot.
At the other end of the scale, if a floating point operation renders a nonzero result that is too small to
represent, then an underflow results. In light of the fact that the smallest positive floating point number is
m = 2−1022 , we anticipate that the script
x = 1;
q = 0;
while x>0
x = x/2;
q = q+1;
end
would assign -1023 to q. However, the actual value that is assigned to q is 1075. This is because the IEEE
standard implements what is call gradual underflow meaning that the actual smallest floating point number
that can be represented is 2L−t = 2−1022−52 = 2−1074.
Sometimes these are just set to zero. Sometimes they result in program termination. Here is a script
that assigns to q the smallest positive integer so that 1/2q = 0 in floating point arithmetic:
Problems
P1.4.1 The binomial coefficient n-choose-k is defined by
„ «
n n!
= .
k k!(n − k)!
Let Bn,k = Sn /(Sk Sn−k ). Write a script analogous to Stirling that explores the error in Bn,k for the cases (n, k) =
(52, 2), (52, 3), . . . , (52, 13). There are no set rules on output except that it should look nice and clearly present the results.

P1.4.2 The sine function has the power series definition



X x2k+1
sin(x) = (−1)k .
(2k + 1)!
k=0

Write a script SinTaylor analogous to ExpTaylor that explores the relative error in the partial sums.

P1.4.3 Write a script that solicits n and plots both sin(x) and
n
X x2k+1
Sn (x) = (−1)k
k=0
(2k + 1)!

across the interval [0, 2π].

P1.4.4 To affirm your understanding of the floating point representation, what is the largest value of n so that n! can be exactly
represented in F(52, −1022, 1023)? Show your work.

P1.4.5 On a base-2 machine, the distance between 7 and the next largest floating point number is 2−12 . What is the distance
between 70 and the next largest floating point number?

P1.4.6 Assume that x and y are floating point numbers in F(t, −10, 10). What is the smallest possible value of y − x given
that x < 8 < y? (Your answer will involve t.)
40 CHAPTER 1. POWER TOOLS OF THE TRADE

P1.4.7 What is the largest value of k such that 10k can be represented exactly in F(52, −1022, 1023)?

P1.4.8 What is the nearest floating point number to 64 on a base-2 computer with 5-bit mantissas? Show work.

P1.4.9 If 127 is the nearest floating point number to 128 on a base-2 computer, then how long is the mantissas? Show work.

1.5 Designing Functions


An ability to write good Matlab functions is crucial. Two examples are used to clarify the essential ideas:
Taylor series and numerical differentiation.

1.5.1 Four Ways to Compute the Exponential of a Vector of Values


Consider once again the Taylor approximation
n
X xk
Tn (x) =
k!
k=0

to the exponential ex . It is possible to write functions in Matlab, and here is one that encapsulates this
approximation:
function y = MyExpF(x,n)
% y = MyExpF(x,n)
% x is a scalar, n is a positive integer
% and y = n-th order Taylor approximation to exp(x).
term = 1;
y = 1;
for k = 1:n
term = x*term/k;
y = y + term;
end

n= 4 −6 n= 8
x 10
0.02 7

6
0.015
5

4
0.01
3

2
0.005
1

0 0
−1 −0.5 0 0.5 1 −1 −0.5 0 0.5 1

−15 n = 16 −16 n = 20
x 10 x 10
8 5

4
6

3
4
2

2
1

0 0
−1 −0.5 0 0.5 1 −1 −0.5 0 0.5 1

Figure 1.17 Relative error in Tn (x)

The function itself must be placed in a separate .m file2 having the same name as the function, e.g., MyExpF.m.
Once that is done, it can be referenced like any of the built-in functions. Thus, the script
2 Subfunctions are an exception. Enter help function for details.
1.5. DESIGNING FUNCTIONS 41

m = 50;
x = linspace(-1,1,m);
y = zeros(1,m);
exact = exp(x);
k = 0;
for n = [4 8 16 20]
for i=1:m
y(i) = MyExpF(x(i),n);
end
RelErr = abs(exact - y)./exact;
k = k+1;
subplot(2,2,k)
plot(x,RelErr)
title(sprintf(’n = %2.0f’,n))
end

plots the relative error in Tn (x) for n = 4, 8, 16, and 20 across [−1, 1]. (See Figure 1.17.)
When writing a Matlab function you must adhere to the following rules and guidelines:
• From the example we infer the following general structure for a Matlab function:

function hOutput Parameteri = hName of Functioni(hInput Parametersi)


%
% hComments that completely specify the function.i
%
hfunction bodyi

• Somewhere in the function body the desired value must be assigned to the output variable.
• Comments that completely specify the function should be given immediately after the function state-
ment. The specification should detail all input value assumptions (the pre-conditions) and what may
be assumed about the output value (the postconditions).
• The lead block of comments after the function statement is displayed when the function is probed
using help (e.g., help MyExpF).
• The input and output parameters are formal parameters. At the time of the call they are replaced by
the actual parameters.
• All variables inside the function are local and are not part of the Matlab workspace.
• If the function file is not in the current directory, then it cannot be referenced unless the appropriate
path is established. Type help path.
Further experimentation with MyExpF shows that if n = 17, then full machine precision exponentials are
computed for all x ∈ [−1, 1]. With this understanding about the Taylor approximation across [−1, 1], we are
ready to develop a “vector version”:
function y = MyExp1(x)
% y = MyExp1(x)
% x is a column vector and y is a column vector with the property that
% y(i) is a Taylor approximation to exp(x(i)) for i=1:n.
n = 17; p = length(x);
y = ones(p,1);
for i=1:p
y(i) = MyExpF(x(i),n);
end
42 CHAPTER 1. POWER TOOLS OF THE TRADE

This example shows several things: (1) A Matlab function can have vector arguments and can return a
vector, (2) the length function can be used to determine the size of an input vector, (3) one function can
reference another. Here is a script that references MyExp1:
x = linspace(-1,1,50);
exact = exp(x);
RelErr = abs(exact - MyExp1(x’)’)./exact;
Notice the transpose that is required to ensure that the vector passed to MyExp1 is a column vector. The
other transpose is required to make MyExp1(x’) a row vector so that it can be combined with exact. Here
is another implementation that is not sensitive to the shape of x:
function y = MyExp2(x)
% y = MyExp2(x)
% x is an n-vector and y is an n-vector with the same shape
% and the property that y(i) is a Taylor approximation to exp(x(i)), i=1:n.

y = ones(size(x));
nTerms = 17;
term = ones(size(x));
for k=1:nTerms
term = x.*term/k;
y = y + term;
end
The expression ones(size(x)) creates a vector of ones that is exactly the same shape as x. In general, the
command [p,q] = size(A) returns the number of rows and columns in A in p and q, respectively. If such a
2-vector is passed to ones, then the appropriate matrix of ones is established. (The same comment applies
to zeros.) The new implementation “doesn’t care” whether x is a row or column vector. The script
x = linspace(-1,1,50);
exact = exp(x);
RelErr = abs(exact - MyExp2(x))./exact;
produces a vector of relative error exactly the same size as x.
Notice the use of pointwise multiplication. In contrast to MyExp1 which computes the component-level
exponentials one at a time, MyExp2 computes them “at the same time.” In general, Matlab runs faster in
vector mode. Here is a script that quantifies this statement by benchmarking these two functions:
nRepeat = 100;
disp(’ Length(x) Time(MyExp2)/Time(MyExp1)’)
disp(’--------------------------------------------’)
for L = 1000:100:1500
xL = linspace(-1,1,L);
tic
for k=1:nRepeat, y = MyExp1(xL); end
T1 = toc;
tic
for k=1:nRepeat, y = MyExp2(xL); end
T2 = toc;
disp(sprintf(’%6.0f %13.6f ’,L,T2/T1))
end
The script makes use of tic and toc. To time a code fragment, “sandwich” it in between a tic and a toc.
Keep in mind that the clock is discrete and is typically accurate to within a millisecond. Therefore, whatever
is timed should take somewhat longer than a millisecond to execute to ensure reliability. To address this
issue it is sometimes necessary to time repeated instances of the code fragment as above. Here are some
sample results:
1.5. DESIGNING FUNCTIONS 43

Length(x) Time(MyExp2)/Time(MyExp1)
--------------------------------
1000 0.086525
1100 0.101003
1200 0.104044
1300 0.080007
1400 0.087395
1500 0.082073

It is important to stress that these are sample results. Different timings would result on different computers.
The for-loop implementations in MyExp1 and MyExp2 are flawed in two ways. First, the value of n chosen
is machine dependent. A different n would be required on a computer with a different machine precision.
Second, the number of terms required for an x value near the origin may be considerably less than 17. To
rectify this, we can use a while-loop that keeps adding in terms until the next term is less than or equal to
eps times the size of the current partial sum:

function y = MyExpW(x)
% y = MyExpW(x)
% x is a scalar and y is a Taylor approximation to exp(x).
y = 0;
term = 1;
k=0;
while abs(term) > eps*abs(y)
k = k + 1;
y = y + term;
term = x*term/k;
end

To produce a vector version, we can proceed as in MyExp1 and simply call MyExpW for each component:

function y = MyExp3(x)
% y = MyExp3(x)
% x is a column n-vector and y is a column n-vector with the property that
% y(i) is a Taylor approximation to exp(x(i)) for i=1:n.
n = length(x);
y = ones(n,1);
for i=1:n
y(i) = MyExpW(x(i));
end

Alternatively, we can follow the MyExp2 idea and vectorize as follows:

function y = MyExp4(x)
% y = MyExp4(x)
% x is an n-vector and y is an n-vector with the same shape and the
% property that y(i) is a Taylor approximation to exp(x(i)) for i=1:n.
y = zeros(size(x));
term = ones(size(x));
k = 0;
while any(abs(term) > eps*abs(y))
y = y + term;
k = k+1;
term = x.*term/k;
end
44 CHAPTER 1. POWER TOOLS OF THE TRADE

Observe the use of the any function. It returns a “1” as long as there is at least one component in abs(term)
that is larger than eps times the corresponding term in abs(y). If any returns a zero, then this means that
term is small relative to y. In fact, it is so small that the floating point sum of y and term is y. The
while-loop terminates as this happens.

1.5.2 Numerical Differentiation


Suppose f(x) is a function whose derivative we wish to approximate at x = a. A Taylor series expansion
about this point says that

f 00 (η) 2
f(a + h) = f(a) + f 0 (a)h + h
2

for some η ∈ [a, a + h]. Thus,

f(a + h) − f(a)
Dh =
h

provides increasingly good approximations as h gets small since

h
Dh = f 0 (a) + f 00 (η) .
2

Here is a script that enables us to explore the quality of this approach when f(x) = sin(x):

a = input(’Enter a: ’);
h = logspace(-1,-16,16);
Dh = (sin(a+h) - sin(a))./h;
err = abs(Dh - cos(a));

Using this to find the derivative of sin at a = 1, we see the following:

h Absolute Error
1.0e-01 0.0429385533327507
1.0e-02 0.0042163248562708
1.0e-03 0.0004208255078129
1.0e-04 0.0000420744495186
1.0e-05 0.0000042073622750
1.0e-06 0.0000004207468094
1.0e-07 0.0000000418276911
1.0e-08 0.0000000029698852
1.0e-09 0.0000000525412660
1.0e-10 0.0000000584810365
1.0e-11 0.0000011687040611
1.0e-12 0.0000432402169239
1.0e-13 0.0007339159003137
1.0e-14 0.0037069761981869
1.0e-15 0.0148092064444385
1.0e-16 0.5403023058681398

The loss of accuracy may be explained as follows. Any error in the computation of the numerator of Dh is
magnified by 1/h. Let us assume that the values returned by sin are within eps of their true values. Thus,
instead of a precise calculus bound
h
|Dh − f 0 (a)| ≤ |f 00 (η)|
2
1.5. DESIGNING FUNCTIONS 45

as predicted earlier, we have a heuristic bound

h 00 2eps
|Dh − f 0 (a)| ≈ |f (η)| + .
2 h
The right-hand side incorporates the “truncationperror” due to calculus and the computation error due to
roundoff. This quantity is minimized when h = 2 eps/|f 00 (η)|.
Let’s package these observations and write a function that does numerical differentiation. The key
analytical detail is the intelligent choice of h. If we have an upper bound on the second derivative of the
form |f 00 (x)| ≤ M2 , then the truncation error can be bounded as follows:

M2
|Dh − f 0 (a)| ≤ h. (1.1)
2
If the absolute error in a computed function evaluation is bounded by δ, then

h 2δ
errD(h) = M2 +
2 h
is a reasonable model for the total error. This quantity is minimized if
r
δ
hopt = 2 ,
M2
giving p
errD(hopt ) = 2 δM2 .
Here is a function that implements this idea:

function [d,err] = Derivative(f,a,delta,M2)


% f is a handle that references a function f(x) whose derivative
% at x = a is sought. delta is the absolute error associated with
% an f-evaluation and M2 is an estimate of the second derivative
% magnitude near a. d is an approximation to f’(a) and err is an estimate
% of its absolute error.
%
% Usage:
% [d,err] = Derivative(@f,a)
% [d,err] = Derivative(@f,a,delta)
% [d,err] = Derivative(@f,a,delta,M2)

if nargin <= 3
% No derivative bound supplied, so assume the
% second derivative bound is 1.
M2 = 1;
end
if nargin == 2
% No function evaluation error supplied, so
% set delta to eps.
delta = eps;
end
% Compute optimum h and divided difference
hopt = 2*sqrt(delta/M2);
d = (f(a+hopt) - f(a))/hopt;
err = 2*sqrt(delta*M2);
46 CHAPTER 1. POWER TOOLS OF THE TRADE

There are several new syntactic features associated with this implementation. We identify them through a
sequence of examples.

Example 1. Compute the derivative of f(x) = exp(x) at x = 5 . Assume that the exp function returns values
that are correct to machine precision and use the fact that the second derivative of f is bounded by 500:
[der_val,err_est] = Derivative(@exp,5,eps,500)
To hand over a function to Derivative, you pass its handle. This is simply the name of the function preceded
by the “at” symbol “@”. In effect @exp “points” to the exp function. Another aspect of this example is that
functions in Matlab can return more than one item: Just separate the output parameters with commas
and enclose with square brackets.

Example 2. Same as Example 1 only (pretend) that we cannot produce an upper bound on the second
derivative:
[der_val,err_est] = Derivative(@exp,5,eps)
The nargin command makes it possible to have abbreviated calls. In this case, Matlab “knows” that this is
a 2-argument call and substitutes a value for the missing input parameter.

Example 3. Same as Example 1 only you don’t care about the error estimate:
der_val = Derivative(@exp,5,eps,500)
In this case

Example 4. Assuming the existence of


function y = MyF(x,alfa,beta)
y = alfa*exp(beta*x);
estimate the derivative at x = 10 assuming that α = 20 and β = −2:
alfa = 20;
beta = -2;
der_val = Derivative(@(x) MyF(x,alfa,beta),10);
This illustrates the use of the anonymous function idea which is very useful when functions depend on
parameters.

Problems

P1.5.1 It can be shown that


f (a + h) − f (a − h)
Ch =
2h
satisfies
M3 2
|Ch − f 0(a)| ≤ h
6
if
|f (3) (x)| ≤ M3
for all x. Model the error in the evaluation of Ch by
M3 h2 δ
errC(h) = +2 .
6 h
Generalize Derivative so that it has a 5th optional argument M3 being an estimate of the 3rd derivative. It should compute
f 0 (a) using the better of the two approximations Dh and Ch .

P1.5.2 Consider the ellipse P (t) = (x(t), y(t)) with


x(t) = a cos(t)
y(t) = b sin(t)
1.5. DESIGNING FUNCTIONS 47

and assume that 0 = t1 < t2 < . . . < tn = π/2. Define the points Q1 , . . . , Qn by

Qi = (x(ti), y(ti )).

Let Li be the tangent line to the ellipse at Qi . This line is defined by the parametric equations

x(t) = a cos(ti) − a sin(ti )t


y(t) = b sin(ti ) + b cos(ti)t.

Next, define the points P0 , . . . , Pn by


8
< (a, 0) i=0
Pi = intersection of Li and Li+1 i = 1...n − 1 .
(0, b) i=n
:

For your information, if the lines defined by

x1 (t) = α 1 + β1 t
y1 (t) = γ1 + δ1 t

x2 (t) = α 2 + β2 t
y2 (t) = γ2 + δ2 t

intersect, then the point of their intersection (x∗ , y∗ ) is given by


β2 (α1 δ1 − β1 γ1 ) − β1 (α2δ2 − β2 γ2 ) δ2(α1 δ1 − β1 γ1 ) − δ1(α2 δ2 − β2 γ2 )
x∗ = and y∗ = .
δ 1 β2 − β1 δ 2 δ 1 β2 − β1 δ 2
Complete the following function:

function [P,Q] = Points(a,b,t)


% a and b are positive, n = length(t)>=2, and 0 = t(1) < t(2) <... < t(n) = pi/2.
% For i=1:n, (Q(i,1),Q(i,2)) is the ith Q-point and (P(i,1),P(i,2)) is the ith P point.

Write a script file that calls Points with a = 5, b = 2, and t = linspace(0,pi/2,4). The script should then plot in one window
the first quadrant portion of the ellipse, the polygonal line that connects the Q points, and the polygonal line that connects the
P points. Use title to display P L and QL, the lengths of these two polygonal lines, i.e., title(sprintf(’ QL = %10.6f PL =
%10.6f ’,QL,PL )).

P1.5.3 Write a Matlab function Ellipse(P,A,theta) that plots the “tilted” ellipse defined by
»
P −A P +A
– h√ i
x(t) = cos(θ) + cos(t) − sin(θ) A · P sin(t)
2 2
»
P −A P +A
– h√ i
y(t) = sin(θ) + cos(t) + cos(θ) A · P sin(t)
2 2
for 0 ≤ t ≤ 2π. Your implementation should not have any loops.

P1.5.4 For a scalar z and a nonnegative integer n define


n
X z2k+1
f (z, n) = (−1)k .
k=0
(2k + 1)!

This is an approximation to the function sin(z). Write a Matlab function y = MySin(x,n) that accepts a vector x and a
nonnegative integer n and returns a vector y with the same size and orientation as x and with the property that yi = f (xi, n)
for i = 1:length(x). The implementation should not involve any loops. Write a script that graphically reports on the relative
error when MySin is applied to x = linspace(.01,pi-.01) for n=3:2:9. Use semilogy and present the four plots in a single
window using subplot. To avoid log(0) problems, plot the maximum of the true relative error and eps. Label the axes. The
title should indicate the value of n and the number of flops required by the call to MySin.

P1.5.5 Using tic and toc, plot the relative error in pause(k) for k = 1:10.

P1.5.6 Complete the following Matlab function

function [cnew,snew] = F(c,s,a,b)


% a and b are scalars with a<b. c and s are row (n+1)-vectors with the property that
% c = cos(linspace(a,b,n+1)) and s = sin(linspace(a,b,n+1))
%
% cnew and snew are column (2n+1)-vectors with the property that
% cnew = cos(linspace(a,b,2*n+1)) and snew = sin(linspace(a,b,2*n+1))
48 CHAPTER 1. POWER TOOLS OF THE TRADE

Your implementation should be vectorized and must make effective use of the trigonometric identities
cos(α + ∆) = cos(α) cos(∆) − sin(α) sin(∆)
sin(α + ∆) = sin(α) cos(∆) + cos(α) sin(∆)
in order to reduce the number of new cosine and sine evaluations. Hint: Let ∆ be the spacing associated with z.

P1.5.7 Complete the following function:

function BookCover(a,b,n)
% a and b are real with b<a. n is a positive integer.
% Let r1 = (a+b)/2 and r2 = (a-b)/2. In the same figure draws the ellipse
%
% (a*cos(t),b*sin(t)) 0<=t<=2*pi,
%
% the "big" circle
%
% (r1*cos(t),r1*sin(t)) 0<=t<=2*pi,
%
% and n "small" circles. The kth small circle should have radius r2 and center
% (r1*cos(2*pi*k/n),r1*sin(2*pi*k/n). A radius making angle -2*pi*k/n should be drawn
% inside the kth small circle.

Use BookCover to draw with correct proportions, the ellipse/circle configuration on the cover of the book.

1.6 Structure Arrays and Cell Arrays


As problems get more complicated it is very important to use appropriate data structures. The choice of a
good data structure can simplify one’s “algorithmic life.” To that end we briefly review two ways that more
advanced data structures can be used in Matlab: structure arrays and cell arrays.
A structure array has fields and values. Thus,

A = struct(’d’,16,’m’,23,’s’,47);

establishes A as a structure array with fields “d”, “m”, and “s”. Such a structure might be handy in a geodesy
application where latitudes and longitudes are measured in degrees, minutes, and seconds. The field values
are accessed with a “dot” notation. The value of A.d is 16, the value of A.m is 23, and the value of A.s is
47. The statement

r = pi*(A.d + A.m/60 + A.s/3600)/180;

assigns to r the radian equivalent of the angle represented by A. The triplet

NYC_Lat = struct(’d’,40,’m’,45,’s’,27);
NYC_Long = struct(’d’,75,’m’,12,’s’,32);
C1 = struct(’name’,’New York’,’lat’,NYC_Lat,’long’,NYC_Long);

establishes C1 as a structure array with three fields. The first field is a string and the last two are structure
arrays. Note that C1.long.d has value 75. One can also have an array of structure arrays:

NYC_Lat = struct(’d’,16,’m’,23,’s’,47);
NYC_Long = struct(’d’,74,’m’,2,’s’,32);
City(1) = struct(’name’,’New York’,’lat’,NYC_Lat,’long’,NYC_Long)
Ith_Lat = struct(’d’,42,’m’,25,’s’,16);
Ith_Long = struct(’d’,76,’m’,29,’s’,41);
City(2) = struct(’name’,’Ithaca’,’lat’,Ith_Lat,’long’,Ith_Long);

In this case, City(2).lat.d has value 42. We mention that a structure array can have an array field and
functions can have input and output parameters that are structure arrays.
A cell array is basically a matrix in which a given entry can be a matrix, a structure array, or a cell array.
If m and n are positive integers, then
1.6. STRUCTURE ARRAYS AND CELL ARRAYS 49

C = cell(m,n)

establishes C as an m-by-n cell array. Cell entries are referenced with curly brackets. Thus, the cell array C
in

C = cell(2,2);
C{1,1} = [1 2 ; 3 4];
C{1,2} = [ 5;6];
C{2,1} = [7 8];
C{2,2} = 9;
M = [C{1,1} C{1,2};C{2,1} C{2,2}]

is a way of representing the 3-by-3 matrix


 
1 2 5
M = 3 4 6 .
7 8 9

1.6.1 Three-digit Arithmetic


Structures and strings are nicely reviewed by developing a three-digit, base-10 floating point arithmetic
simulation package. Let’s assume that the exponent range is [−9, 9] and that we use a 4-field structure to
represent each floating point number as described in the following specification:
function f = Represent(x)
% f = Represent(x)
% Yields a 3-digit floating point representation of f:
%
% f.mSignBit mantissa sign bit (0 if x>=0, 1 otherwise)
% f.m mantissa (= f.m(1) + f.m(2)/10 + f.m(3)/100)
% f.eSignBit the exponent sign bit (0 if exponent nonnegative, 1 otherwise)
% f.e the exponent (-9<=f.e<=9)
%
% If x is outside of [-9.99*10^9,9.99*10^9], f.m is set to inf.
% If x is in the range (-1.00*10^-9,1.00*10^-9) f is the representation of zero
% in which both sign bits are 0, e is zero, and m = [0 0 0].

Thus, f = Represent(-237000) is equivalent to

f = struct(’mSignBit’,1,’m’,[2 3 7],’eSignBit’,0,’e’,6)
Complementing Represent is the following function, which can take a three-digit representation and compute
its value:
function x = Convert(f)
% x = Convert(f)
% f is a is a representation of a 3-digit floating point number.
% x is the value of f.

% Overflow situations
if (f.m == inf) & (f.mSignBit==0)
x = inf;
return
end
if (f.m == inf) & (f.mSignBit==1)
x = -inf;
return
end
50 CHAPTER 1. POWER TOOLS OF THE TRADE

% Mantissa value
mValue = (100*f.m(1) + 10*f.m(2) + f.m(3))/100;
if f.mSignBit==1
mValue = -mValue;
end

% Exponent value
eValue = f.e;
if f.eSignBit==1
eValue = -eValue;
end

x = mValue * 10^eValue;
To simulate three-digit floating point arithmetic, we convert the operands to conventional form, do the
arithmetic, and then represent the result in 3-digit form. The following function implements this approach:
function z = Float(x,y,op)
% z = Float(x,y,op)
% x and y are representations of a 3-digit floating point number.
% op is one of the strings ’+’, ’-’, ’*’, or ’/’.
% z is the 3-digit floating point representation of x op y.

sx = num2str(convert(x));
sy = num2str(convert(y));
z = represent(eval([’(’ sx ’)’ op ’(’ sy ’)’ ]));
Strings are enclosed in quotes. The conversion of a number to a string is handled by num2str. Strings are
concatenated by assembling them in square brackets. The eval function takes a string for input and returns
the value produced when that string is executed.
To “pretty print” the value of a floating point representation, we have
function s = Pretty(f)
% s = Pretty(f)
% f is a representation of a 3-digit floating point number.
% s is a string so that disp(s) "pretty prints" the value of f.

As an illustration of how these functions can be used, the script file Euler generates the partial sums
1 1
sn = 1 ++···+ .
2 n
In exact arithmetic the sn tend toward ∞, but when we run
% Script File: Euler
% Sums the series 1 + 1/2 + 1/3 + .. in 3-digit floating point arithmetic.
% Terminates when the addition of the next term does not change
% the value of the running sum.

oldsum = Represent(0);
one = Represent(1);
sum = one;
k = 1;
while Convert(sum) ~= Convert(oldsum)
k = k+1;
kay = Represent(k);
term = Float(one,kay,’/’);
oldsum = sum;
sum = Float(sum,term,’+’);
end
clc
disp([’The sum for ’ num2str(k) ’ or more terms is ’ pretty(sum)])
1.6. STRUCTURE ARRAYS AND CELL ARRAYS 51

the loop terminates after 200 terms.

1.6.2 Padé Approximants


A very useful class of approximants for the exponential function ez are the Padé functions defined by
p
!, q !
X (p + q − k)!p! k X (p + q − k)!q!
k
Rpq (z) = z (−z) .
(p + q)!k!(p − k)! (p + q)!k!(q − k)!
k=0 k=0

Assuming the availability of


function R = PadeCoeff(p,q)
% R = PadeCoeff(p,q)
% p and q are nonnegative integers and R is a representation of the
% (p,q)-Pade approximation N(x)/D(x) to exp(x):
%
% R.num is a row (p+1)-vector whose entries are the coefficients of the
% p-degree numerator polynomial N(x).
%
% R.den is a row (q+1)-vector whose entries are the coefficients of the
% q-degree denominator polynomial D(x).
%
% Thus,
% R.num(1) + R.num(2)x + R.num(3)x^2
% ------------------------------------
% R.den(1) + R.den(2)x
%
% is the (2,1) Pade approximation.
the following function returns a cell array whose entries specify a particular Padé approximation:
function P = PadeArray(m,n)
% P = PadeArray(m,n)
% m and n are nonnegative integers.
% P is an (m+1)-by-(n+1) cell array.
%
% P{i,j} represents the (i-1,j-1) Pade approximation N(x)/D(x) to exp(x).

P = cell(m+1,n+1);
for i=1:m+1
for j=1:n+1
P{i,j} = PadeCoeff(i-1,j-1);
end
end

Problems
P1.6.1 Write a function s = dot3(x,y) that returns the 3-digit representation of the inner product x’*y where x and y are
column vectors of the same length. The inner product should be computed using 3-digit arithmetic. (Make effective use of
represent, convert, and float.) The error can be computed via the command err = x’*y - convert(dot3(x,y)). Write a
script that plots a histogram of the error when dot3 is applied to 100 random x’*y problems of length 5. Use randn(5,1) to
generate the x and y vectors. Report the results in a histogram with 20 bins.

P1.6.2 Use PadeArray to generate representations of the Padé approximants Rpq for 0 ≤ p ≤ 3 and 0 ≤ q ≤ 3. Plot the relative
error of R11 , R22 and R33 across the interval [-5 5]. Use semilogy for the plots.

P1.6.3 The Chebychev polynomials are defined by


8
< 1 k=0
Tk (x) = x k=1 .
2xTk−1(x) − Tk−2 (x) k≥2
:

Write a function T = ChebyCoeff(n) that returns an n-by-1 cell array whose ith cell is a length-i array. The elements of the
array are the coefficients of Ti−1. Thus T{3} = [-1 0 2] since T2 (x) = 2x2 − 1.
52 CHAPTER 1. POWER TOOLS OF THE TRADE

1.7 More Refined Graphics


Plots can be embellished so that they carry more information and have a more pleasing appearance. In this
section we show how to set font, incorporate subscripts and superscripts, and use mathematical and Greek
symbols in displayed strings. We also discuss the careful placement of text in a figure window and how to
modify what the axes “say”. Line thickness and color are also treated.
Because refined graphics is best learned through experimentation, our presentation is basically by exam-
ple. Formal syntactic definitions are avoided. The reader is encouraged to play with the scripts provided.

1.7.1 Fonts
A font has a name, a size, and a style. Figure 1.18 shows some of the possibilities associated with the
Times-Roman font. The script ShowFonts displays similar tableaus for the AvantGarde, Bookman, Courier,
Helvetica, Helvetica-Narrow, NewCenturySchlbk, Palatino, and Zapfchancery fonts. Here are some sample
text commands where non-default fonts are used:

text(x,y,’Matlab’,’FontName’,’Times-Roman’,’FontSize’,12)
text(x,y,’Matlab’,’FontName’,’Helvetica’,’FontSize’,12,’FontWeight’,’bold’)
text(x,y,’Matlab’,’FontName’,’ZapfChancery’,’FontSize’,12,’FontAngle’,’oblique’)

The fonts can also be set when using title, xlabel, and ylabel, e.g.,

title(’Important Title’,’FontName’,’Helvetica’,’FontSize’,18,’FontWeight’,’bold’)

1.7.2 Mathematical Typesetting


It is possible to specify subscripts, superscripts, Greek letters, and various mathematical symbols in the
strings that are passed to title, xlabel, ylabel, and text. For example,

title(’{\itf}_{1}({\itx}) = sin(2\pi{\itx}){\ite}^{-2{\it\alphax}}’)

creates a title of the form sin(2πx)e−2αx . conventions are followed. “Special characters” are specified with

Times−Roman
Plain Bold Oblique

Matlab Matlab Matlab


Matlab Matlab Matlab
Matlab Matlab Matlab

Matlab Matlab Matlab

Matlab Matlab Matlab

Matlab Matlab Matlab

Matlab Matlab Matlab

Figure 1.18 Fonts

a \ prefix and some of the possibilities are given in Figures 1.19 and 1.20. In this setting, curly brackets are
used to determine scope. The underscore and caret are used for subscripts and superscripts. It is customary
to italicize mathematical expressions, except that numbers and certain function names should remain in
plain font. To do this use \it.
1.7. MORE REFINED GRAPHICS 53

Math Symbols

≠ \neq ← \leftarrow ∈ \in

≥ \geq → \rightarrow ⊂ \subset

≈ \approx ↑ \uparrow ∪ \cup

≡ \equiv ↓ \downarrow ∩ \cap

≅ \cong ⇐ \Leftarrow ⊥ \perp

± \pm ⇒ \Rightarrow ∞ \infty

∇ \nabla ⇔ \Leftrightarrow ∫ \int

∠ \angle ∂ \partial × \times

Figure 1.19 Math symbols

Greek Symbols

α \alpha ω \omega Σ \Sigma

β \beta φ \phi Π \Pi

γ \gamma π \pi Λ \Lambda

δ \delta χ \chi Ω \Omega

ε \epsilon ψ \psi Γ \Gamma

κ \kappa ρ \rho

λ \lambda σ \sigma

µ \mu τ \tau

ν \nu υ \upsilon

Figure 1.20 Greek symbols


54 CHAPTER 1. POWER TOOLS OF THE TRADE

1.7.3 Text Placement


The accurate placement of labels in a figure window is simplified by using HorizontalAlignment and
VerticalAlignment with suitable modifiers. With its vertices encoded in a pair of length-6 arrays x and y,

P2

P3 → ← P1

P → ←P
4 6

P5 ↑

Figure 1.21 Text placements

the labeled hexagon in Figure 1.21 is produced with the following fragment:
HA = ’HorizontalAlignment’; VA = ’VerticalAlignment’;
text(x(1),y(1),’\leftarrow {\itP}_{1}’, HA,’left’)
text(x(2),y(2),’\downarrow’, HA,’center’, VA,’baseline’)
text(x(2),y(2),’{ \itP}_{2}’, HA,’left’, VA,’bottom’)
text(x(3),y(3),’{\itP}_{3} \rightarrow’, HA,’right’)
text(x(4),y(4),’{\itP}_{4} \rightarrow’, HA,’right’)
text(x(5),y(5),’\uparrow’, HA,’center’, VA,’top’)
text(x(5),y(5),’{\itP}_{5} ’, HA,’right’, VA,’top’)
text(x(6),y(6),’\leftarrow {\itP}_{6}’, HA,’left’)

1.7.4 Line Width and Axes


It is possible to modify the thickness of the lines that are drawn by plot. The fragment

h = plot(x,y);
set(h,’LineWidth’,3)

plots y versus x with the line width attribute set to 3. The effect of various line width settings is shown in
Figure 1.22. It is also possible to regulate the font used by xlabel, ylabel, and title and to control the
“tick mark” placement along these axes. See Figure 1.23 which is produced by the following script:

F = ’Times-Roman’; n = 12; t = linspace(0,2*pi); c = cos(t); s = sin(t);


plot(c,s), axis([-1.3 1.3,-1.3 1.3]), axis equal
title(’The Circle ({\itx-a})^{2} + ({\ity-b})^{2} = {\itr}^{2}’,...
’FontName’,F,’FontSize’,n)
xlabel(’x’,’FontName’,F,’FontSize’,n)
ylabel(’y’,’FontName’,F,’FontSize’,n)
set(gca,’XTick’,[-.5 0 .5])
set(gca,’YTick’,[-.5 0 .5])
grid on
1.7. MORE REFINED GRAPHICS 55

LineWidth

default
1
2
3
4
5
6
7
8
9
10

Figure 1.22 Line width


The Circle (x−a)2 + (y−b)2 = r2

0.5

0
y

−0.5

−0.5 0 0.5
x

Figure 1.23 Axis design

We mention that grid is a toggle and when it is on, the grid lines associated with the prescribed axis ticks
are displayed. All tick marks can be suppressed by using the empty matrix, e.g., set(gca,’XTick’,[]).

1.7.5 Legends
It is sometimes useful to have a legend in plots that display more than one function. Figure 1.24 is produced
by the following script:
t = linspace(0,2);
axis([0 2 -1.5 1.5])
y1 = sin(t*pi); y2 = cos(t*pi);
plot(t,y1,t,y2,[0 .5 1 1.5 2],[0 0 0 0 0],’o’)
set(gca,’XTick’,[]), set(gca,’YTick’,[0]), grid on
legend(’sin(\pi t)’,’cos(\pi t)’,’roots’,0)
The integer provided to legend is used to specify position: 0 = least conflict with data, 1 = upper right-hand
corner (default), 2 = upper left-hand corner, 3 = lower left-hand corner, 4 = lower right-hand corner, and
-1 = to the right of the plot.
56 CHAPTER 1. POWER TOOLS OF THE TRADE

sin(π t)
cos(π t)
roots

Figure 1.24 Legend placement

1.7.6 Color
Matlab comes with 8 predefined colors:

rgb [0 0 0] [0 0 1] [0 1 0] [0 1 1] [1 0 0] [1 0 1] [1 1 0] [1 1 1]
color white blue green cyan red magenta yellow black
mnemonic w b g c r m y k

The “rgb triple” is a 3-vector whose components specify the amount of red, green and blue. The rgb values
must be in between 0 and 1. (See Figure 1.25.) To specify that a particular line be drawn with a predefined
color, just include its mnemonic in the relevant line type string. Here are some examples:

plot(x,y,’g’)
plot(x,y,’*g’)
plot(x1,y1,’r’,x2,y2,’.g’,x3,y3,’k.-’)

The fill function can be used to draw filled polygons with a specified color. If x and y are length-n
vectors then

fill(x,y,’m’)

draws a magenta polygon whose vertices are (xi , yi ), i = 1:n. “User-defined” colors can also be passed to
fill,

fill(x,y,[.3,.8,.4])

It is also possible draw several filled polygons at once:

fill(x1,y1,’g’,x2,y2,[.3,.8,.4])
1.7. MORE REFINED GRAPHICS 57

Built−In Colors A Gradient

white [ 1.00 , 1 , 1 ]

black [ 0.95 , 1 , 1 ]

blue [ 0.90 , 1 , 1 ]

green [ 0.85 , 1 , 1 ]

red [ 0.80 , 1 , 1 ]

yellow [ 0.70 , 1 , 1 ]

magenta [ 0.60 , 1 , 1 ]

cyan [ 0.40 , 1 , 1 ]

Figure 1.25 Color

See the script ShowColor for more details.

Problems

P1.7.1 Complete the following Matlab function so that it performs as specified:

function arch(a,b,theta1,theta2,r1,r2,ring_color)
%
% Adds an arch with center (a,b), inner radius r1, and outer radius r2 to the current figure.
% The arch is the set of all points of the form (a+r*cos(theta),b+r*sin(theta)) where
% r1 <= r <= r2 and theta1 <= theta <= theta2 where theta1 and theta2 in radians.
% The color of the displayed arch is prescribed by ring_color, a 3-vector encoding the rgb triple.

Write a function OlympicRings(r,n,ring colors) with the property that the script

close all
ring_colors = [0 0 1 ; 1 1 0 ; 1 1 1 ; 0 1 0 ; 1 0 0];
OlympicRings(1,5,ring_colors)
axis off equal

produces the following output (in black and white):

In a call to OlympicRings, r is the outer radius of each ring and n is the number of rings. Index the rings left to right from 0 to
n − 1. The parameter ring colors is an n-by-3 matrix whose k + 1st row specifies the color of the kth ring. The inner radius
of each ring is .85r. The center (ak , bk ) of the kth ring is given by (1.15rk, 0) if k is even and by (1.15rk, −r) if k is odd.
Notice that the rings are interlocking. Thus, to get the right “over-and-under” appearance you cannot simply superimpose
the drawing of the 5 rings. You’ll have to split up the drawing of each ring into sections and the small little cross lines you see
in the above figure are a hint.
58 CHAPTER 1. POWER TOOLS OF THE TRADE

M-Files and References

Script Files
SineTable Prints a short table of sine evaluations.
SinePlot Displays a sequence of sin(x) plots.
ExpPlot Plots exp(x) and an approximation to exp(x).
TangentPlot Plots tan(x).
SineAndCosPlot Superimposes plots of sin(x) and cos(x).
Polygons Displays nine regular polygons, one per window.
SumOfSines Displays the sum of four sine functions.
SumOfSines2 Displays a pair of sum-of-sine functions.
UpDown Sample core exploratory environment.
RunUpDown Framework for running UpDown.
Histograms Displays the distribution of rand and randn.
Clouds Displays 2-dimensional rand and randn.
Dice Histogram of 1000 dice rolls.
Darts Monte Carlo computation of pi.
Smooth Polygon smoothing.
Stirling Relative and absolute error in Stirling formula.
ExpTaylor Plots relative error in Taylor approximation to exp(x).
Zoom Roundoff in the expansion of (x-1)b6.
FpFacts Examines precision, overflow, and underflow.
TestMyExp Examines MyExp1, MyExp2, MyExp3, and MyExp4.
Euler Three-digit arithmetic sum of 1 + 1/2 +...+ 1/n.
ShowPadeArray Tests the function PadeArray.
ShowFonts Illustrates how to use fonts.
ShowSymbols Shows how to generate math symbols.
ShowGreek Shows how to generate Greek letters.
ShowText Shows how to align with text.
ShowLineWidth Shows how vary line width in a plot.
ShowAxes Shows how to set tick marks on axes.
ShowLegend Shows how to add a legend to a plot.
ShowColor Shows how to use built-in colors and user-defined colors.

Function Files
MyExpF For-loop Taylor approximation to exp(x).
MyExp1 Vectorized version of MyExpF.
MyExp2 Better vectorized version of MyExpF.
MyExpW While-loop Taylor approximation to exp(x).
MyExp3 Vectorized version of MyExpW.
MyExp4 Better vectorized version of MyExpW.
Derivative Numerical differentiation.
Represent Sets up 3-digit arithmetic representation.
Convert Converts 3-digit representation to float.
Float Simulates 3-digit arithmetic.
Pretty Pretty prints a 3-digit representation.
PadeArray Builds a cell array of Pade coefficients.
Chapter 2

Polynomial Interpolation

§2.1 The Vandermonde Approach


§2.2 The Newton Approach
§2.3 Properties
§2.4 Special Topics

In the problem of data approximation, we are given some points (x1 , y1 ), . . . , (xn , yn ) and are asked to find
a function φ(x) that “captures the trend” of the data. If the trend is one of decay, then we may seek a φ of
the form a1 e−λ1 x + a2 e−λ2 x . If the trend of the data is oscillatory, then a trigonometric approximant might
be appropriate. Other settings may require a low-degree polynomial. Regardless of the type of function
used, there are many different metrics for success, e.g., least squares.
A special form of the approximation problem ensues if we insist that φ actually “goes through” the data,
as shown in Figure 2.1. This means that φ(xi ) = yi , i = 1:n and we say that φ interpolates the data. The
polynomial interpolation problem is particularly important:

Given x1 , . . . , xn (distinct) and y1 , . . . , yn , find a polynomial pn−1 (x) of


degree n − 1 (or less) such that pn−1 (xi ) = yi for i = 1:n.

Thus, p2 (x) = 1 + 4x − 2x2 interpolates the points (−2, −15), (3, −5), and (1, 3).
Each (xi , yi ) pair can be regarded as a snapshot of some function f(x): yi = f(xi ). The function f may
be explicitly available, as when we want to interpolate sin(x) at x = 0, π/2, and π with a quadratic. On
other occasions, f is implicitly defined, as when we want to interpolate the solution to a differential equation
at a discrete number of points.
The discussion of polynomial interpolation revolves around how it can be represented, computed, and
evaluated:
• How do we represent the interpolant pn−1 (x)? Instead of expressing the interpolant in terms of the
“usual” basis polynomials 1, x, and x2 , we could use the alternative basis 1, (x + 2), and (x + 2)(x − 3).
Thus,
p2 (x) = −15 + 2(x + 2) − 2(x + 2)(x − 3)
is another way to express the quadratic interpolant of the data (−2, −15), (3, −5), and (1, 3). Different
bases have different computational virtues.
• Once we have settled on a representation for the polynomial interpolant, how do we determine the
associated coefficients? It turns out that this aspect of the problem involves the solution of a linear
system of equations with a highly structured coefficient matrix.

1
2 CHAPTER 2. POLYNOMIAL INTERPOLATION

0.8

0.6

0.4

0.2

−0.2

−0.4

−0.6
0 0.2 0.4 0.6 0.8 1 1.2 1.4 1.6 1.8 2

Figure 2.1 The interpolation of four data points with a cubic polynomial

• After we have computed the coefficients, how can the interpolant be evaluated with efficiency? For
example, if the interpolant is to be plotted then we are led to the problem of evaluating a polynomial
on a vector of values.

In Matlab these issues can be handled by polyfit and polyval. The script

x = [-2 3 1];
y = [-15 -5 3];
a = polyfit(x,y,2)
xvals = linspace(-3,2,100);
pvals = polyval(a,xvals);
plot(xvals,pvals)

plots the polynomial interpolant of the data (−2, −15), (3, −5), and (1, 3). The interpolant is given by
p(x) = 1 + 4x − 2x2 and the call to polyfit computes a representation of this polynomial. In particular, a
is assigned the vector [-2 4 1].
In general, if x and y are n-vectors, then a = polyfit(x,y,n-1) assigns a length-n vector to a with the
property that the polynomial

p(x) = an + an−1 x + an−2 x2 + · · · + a1 xn−1

interpolates the data (x1 , y1 ), . . . , (xn , yn ).


The function polyval is used to evaluate polynomials in the Matlab representation. In the above script
polyval(a,xvals) is a vector of interpolant evaluations.
In this chapter we start with what we call the “Vandermonde” approach to the polynomial interpolation
problem. The Newton representation is considered in §2.2 and accuracy issues in §2.3. Divided differences,
inverse interpolation, interpolation in the plane, and trigonmetric interpolation are briefly discussed in §2.4.

2.1 The Vandermonde Approach


In the Vandermonde approach, the interpolant is expressed as a linear combination of 1, x, x2 , etc. Al-
though monomials are not the best choice for a basis, our familiarity with this way of “doing business” with
polynomials makes them a good choice to initiate the discussion.
2.1. THE VANDERMONDE APPROACH 3

2.1.1 A Four-point Interpolation Problem


Let us find a cubic polynomial
p3 (x) = a1 + a2 x + a3 x2 + a4 x3
that interpolates the four data points (−2, 10), (−1, 4), (1, 6), and (2, 3). Note that this is the “reverse” of
Matlab ’s convention for representing polynomials. 1 Each point of interpolation leads to a linear equation
that relates the four unknowns a1 , a2 , a3 , and a4 :

p3 (−2) = 10 ⇒ a1 − 2a2 + 4a3 − 8a4 = 10


p3 (−1) = 4 ⇒ a1 − a2 + a3 − a4 = 4
p3 (1) = 6 ⇒ a1 + a2 + a3 + a4 = 6
p3 (2) = 3 ⇒ a1 + 2a2 + 4a3 + 8a4 = 3
Expressing these four equations in matrix/vector terms gives
    
1 −2 4 −8 a1 10
 1 −1 1 −1   a2   4 
  = .
 1 1 1 1   a3   6 
1 2 4 8 a4 3

The solution a = [4.5000 1.9167 0.5000 − 0.9167]T to this 4-by-4 system can be found as follows:

y = [10; 4; 6; 3];
V = [1 -2 4 -8; 1 -1 1 -1; 1 1 1 1; 1 2 4 8];
a = V\y;

2.1.2 The General n Case


From this example, it looks like the polynomial interpolation problem reduces to a linear equation problem.
For general n, the goal is to determine a1 , . . . , an so that if

pn−1 (x) = a1 + a2 x + a3 x2 + · · · + an xn−1 ,

then
pn−1 (xi ) = a1 + a2 xi + a3 x2i + · · · + an xin−1 = yi
for i = 1:n. By writing these equations in matrix-vector form, we obtain
    
1 x1 x21 · · · x1n−1 a1 y1
 2 n−1     
 1 x2 x2 · · · x2   a2   y2 
    
 1 x 2 n−1     
 3 x3 · · · x3   a3  =  y3 .
 . . .. ..  
..   ..    . 
 .. .. . . .  .   .. 
  
2 n−1 a yn
1 xn xn · · · xn n

Designate the matrix of coefficients by V . The solvability of the interpolation problem hinges on the non-
singularity of V . Suppose there is a vector c such that V c = 0. It follows that the polynomial

q(x) = c1 + c2 x + · · · + cn xn−1

is zero at x = x1 , . . . , x = xn . This says that we have a degree n − 1 polynomial with n roots. The only
way that this can happen is if q is the zero polynomial (i.e., c = 0). Thus V is nonsingular because the only
vector that it zeros is the zero vector.
1 Matlab would represent the sought-after cubic as p = a + a x + a x2 + a x3 . Our chosen style is closer to what one
3 4 3 2 1
would find in a typical math book: p3 (x) = a0 + a1 x + a2 x2 + a3 x3 .
4 CHAPTER 2. POLYNOMIAL INTERPOLATION

2.1.3 Setting Up and Solving the System


Let us examine the construction of the Vandermonde matrix V . Our first method is based on the observation
that the ith row of V involves powers of xi and that the powers increase from 0 to n−1 as the row is traversed
from left to right. A conventional double-loop approach gives
n = length(x); V = zeros(n,n);
for i=1:n
% Set up row i.
for j=1:n
V(i,j) = x(i)b(j-1);
end
end

Algorithms that operate on a two-dimensional array in row-by-row fashion are row oriented.
The inner-loop in the preceding script can be vectorized because Matlab supports pointwise exponenti-
ation. For example, u = [1 2 3 4] .b[3 5 2 3] assigns to u the row vector [1 32 9 64]. The i-th row of
V requires exponentiating the scalar xi to each of the values in the row vector 0:n − 1 = (0, 1, . . . , n − 1).
Thus, row = (x(i)*ones(1,n)).b(0:n-1) assigns the vector (1, xi , x2i , . . . , xin−1 ) to row, precisely the val-
ues that make up the ith row of V . The ith row of a matrix V may be referenced by V(i,:), and so we
obtain
n = length(x); V = zeros(n,n);
for i=1:n
% Set up the i-th row of V.
V(i,:) = (x(i)*ones(1,n)).b(0:n-1);
end
By reversing the order of the loops in the original set-up script, we obtain a column oriented algorithm:
n = length(x); V = zeros(n,n);
for j=1:n
% Set up column j.
for i=1:n
V(i,j) = x(i)b(j-1);
end
end
If j > 1, then V (i, j) is the product of x(i) and V (i, j − 1), the matrix entry to its left. This suggests that
the required exponentiations can be obtained through repeated multiplication:
n = length(x);
V = ones(n,n);
for j=2:n
% Set up column j.
for i=1:n
V(i,j) = x(i)*V(i,j-1)
end
end
The generation of the jth column involves pointwise vector multiplication:
     
x1 v1,j−1 v1,j
 ..   ..   . 
 .  .∗  .  =  ..  .
xn vn,j−1 vn,j
This may be implemented by V(:,j) = x .* V(:,j-1). Basing our final implementation on this, we obtain
2.1. THE VANDERMONDE APPROACH 5

function a = InterpV(x,y)
% a = InterpV(x,y)
% This computes the Vandermonde polynomial interpolant where
% x is a column n-vector with distinct components and y is a
% column n-vector.
%
% a is a column n-vector with the property that if
%
% p(x) = a(1) + a(2)x + ... a(n)x^(n-1)
% then
% p(x(i)) = y(i), i=1:n

n = length(x);
V = ones(n,n);
for j=2:n
% Set up column j.
V(:,j) = x.*V(:,j-1);
end
a = V\y;

Column-oriented, matrix-vector implementations will generally be favored in this text. One reason for doing
this is simply to harmonize with the traditions of linear algebra, which is usually taught with a column-
oriented perspective.

2.1.4 Nested Multiplication


We now consider the evaluation of pn−1 (x) = a1 + · · · + an xn−1 at x = z, assuming that z and a(1:n) are
available. The routine approach

n = length(a);
zpower = 1;
pVal = a(1);
for i=2:n
zpower = z*zpower;
pVal = pVal + a(i)*zpower;
end

assigns the value of pn−1 (z) to pVal.


A more efficient algorithm is based on a nested organization of the polynomial, which we illustrate for
the case n = 4:
p3 (x) = a1 + a2 x + a3 x2 + a4 x3 = ((a4 x + a3 )x + a2 )x + a1 .
Note that the fragment

pVal = a(4);
pVal = z*pVal + a(3);
pVal = z*pVal + a(2);
pVal = z*pVal + a(1);

assigns the value of p3 (z) to pVal. For general n, this nested multiplication idea takes on the following form:

n = length(a);
pVal = a(n);
for i=n-1:-1:1
pVal = z*pVal + a(i);
end
6 CHAPTER 2. POLYNOMIAL INTERPOLATION

This is widely known as Horner’s rule.


Before we encapsulate the Horner idea in a Matlab function, let us examine the case when the interpolant
is to be evaluated at many different points. To be precise, suppose z(1:m) is initialized and that for i = 1:m,
we want to assign the value of pn−1 (z(i)) to pVal(i). One obvious approach is merely to repeat the preceding
Horner iteration at each point. Instead, we develop a vectorized implementation that can be obtained if
we think about the “simultaneous” evaluation of the interpolants at each zi . Suppose m = 5 and n = 4
(i.e, the case when a cubic interpolant is to be evaluated at five different points). The first step in the five
applications of the Horner idea may be summarized as follows:
   
pVal(1) a(4)
 pVal(2)   a(4) 
   
 pVal(3)  =  a(4)  .
   
 pVal(4)   a(4) 
pVal(5) a(4)

In vector terms pVal = a(n)*ones(m,1). The next step requires a multiply-add of the following form:
     
pVal(1) z(1)*pVal(1) a(3)
 pVal(2)   z(2)*pVal(2)   a(3) 
     
 pVal(3)  =  z(3)*pVal(3)  +  a(3)  .
     
 pVal(4)   z(4)*pVal(4)   a(3) 
pVal(5) z(5)*pVal(5) a(3)

That is,
pVal = z.*pVal + a(3)
The pattern is clear for the cubic case:

pVal = a(4)*ones(m,1);
pVal = z .* pVal + a(3);
pVal = z .* pVal + a(2);
pVal = z .* pVal + a(1);

From this we generalize to the following:

function pVal = HornerV(a,z)


% pVal = HornerV(a,z)
% evaluates the Vandermonde interpolant on z where
% a is an n-vector and z is an m-vector.
%
% pVal is a vector the same size as z with the property that if
%
% p(x) = a(1) + .. +a(n)x^(n-1)
% then
% pVal(i) = p(z(i)) , i=1:m.

n = length(a);
m = length(z);
pVal = a(n)*ones(size(z));
for k=n-1:-1:1
pVal = z.*pVal + a(k);
end

Each update of pval requires 2m flops so approximately 2mn flops are required in total.
As an application, here is a script that displays cubic interpolants of sin(x) on [0, 2π]. The abscissas are
chosen randomly.
2.1. THE VANDERMONDE APPROACH 7

% Script File: ShowV


% Plots 4 random cubic interpolants of sin(x) on [0,2pi].
% Uses the Vandermonde method.

close all
x0 = linspace(0,2*pi,100)’;
y0 = sin(x0);
for eg=1:4
x = 2*pi*sort(rand(4,1));
y = sin(x);
a = InterpV(x,y);
pVal = HornerV(a,x0);
subplot(2,2,eg)
plot(x0,y0,x0,pVal,’--’,x,y,’*’)
axis([0 2*pi -2 2])
end
Figure 2.2 displays a sample output.
2 2

1 1

0 0

−1 −1

−2 −2
0 2 4 6 0 2 4 6

2 2

1 1

0 0

−1 −1

−2 −2
0 2 4 6 0 2 4 6

Figure 2.2 Random cubic interpolants of sin(x) on [0, 2π]

Problems
P2.1.1 Instead of expressing the polynomial interpolant in terms of the basis functions 1, x, . . . , xn−1 , we can work with the
alternative representation
n „ «k−1
X x−u
pn−1 (x) = ak .
k=1
v
Here u and v are scalars that serve to shift and scale the x-range. Generalize InterpV so that it can be called with either two,
three, or four arguments. A call of the form a = InterpV(x,y) should assume that u = 0 and v = 1. A call of the form a =
InterpV(x,y,u) should assume that v = 1 and that u houses the shift factor. A call of the form a = InterpV(x,y,u,v) should
assume that u and v house the shift and scale factors, respectively.

P2.1.2 A polynomial of the form


p(x) = a1 + a2 x2 + · · · + am x2m−2
is said to be even, while a polynomial of the form
p(x) = a1 x + a3 x3 + · · · + am x2m−1
is said to be odd. Generalize HornerV(a,z) so that it has an optional third argument type that indicates whether or not the
underlying polynomial is even or odd. In particular, a call of the form HornerV(a,z,’even’) should assume that ak is the
coefficient of x2k−2 . A call of the form HornerV(a,z,’odd’) should assume that ak is the coefficient of x2k−1 .
8 CHAPTER 2. POLYNOMIAL INTERPOLATION

P2.1.3 Assume that z and a(1:n) are initialized and define


p(x) = a1 + a2 x + · · · + an xn−1 .
R1 Rz
Write a script that evaluates (1) p(z)/p(−z), (2) p(z) + p(−z), (3) p0 (z), (4) 0 p(x)dx, and (e) −z p(x)dx. Make effective use
of HornerV.

P2.1.4 (a) Assume that L (scalar), R (scalar), and c(1:4) are given. Write a script that computes a(1:4) so that if p(x) =
a1 + a2 x + a3 x2 + a4 x3 , then p(L) = c1 , p0 (L) = c2 , p00 (L) = c3 , and p(R) = c4 . Use \ to solve any linear system
that arises in your method. (b) Write a function a = TwoPtInterp(L,cL,R,cR) that returns the coefficients of a polynomial
p(x) = a1 + a2 x + · · · + an xn that satisfies p(k−1) (L) = cL(k) for k = 1:length(cL) and p(k−1) (R) = cR(k) for k = 1:length(cR).
The degree of p should be one less than the total number of end conditions. (The problem of determining a cubic polynomial
whose value and slope are prescribed at two points is discussed in detail in §3.2.1. It is referred to as the cubic Hermite
interpolation problem.)

P2.1.5 Write a function PlotDerPoly(x,y) that plots the derivative of the polynomial interpolant of the data (xi , yi ), i = 1:n.
Assume that x1 < · · · < xn and the plot should be across the interval [x1 , xn ]. Use polyfit and polyval.

2.2 The Newton Representation


We now look at a form of the polynomial interpolant that is generally more useful than the Vandermonde
representation.

2.2.1 A Four-Point Example


To motivate the idea, consider once again the problem of interpolating the four points (x1 , y1 ), (x2 , y2 ),
(x3 , y3 ), and (x4 , y4 ) with a cubic polynomial p3 (x). However, instead of expressing the interpolant in terms of
the “canonical” basis 1, x, x2 , and x3 , we use the basis 1, (x−x1), (x−x1)(x−x2 ), and (x−x1 )(x−x2 )(x−x3).
This means that we are looking for coefficients c1 , c2 , c3 , and c4 so that if

p3 (x) = c1 + c2 (x − x1 ) + c3 (x − x1 )(x − x2 ) + c4 (x − x1 )(x − x2 )(x − x3 ), (2.1)

then yi = p3 (xi ) = yi for i = 1:4. In expanded form, these four equations state that

y1 = c1

y2 = c1 + c2 (x2 − x1 )

y3 = c1 + c2 (x3 − x1 ) + c3 (x3 − x1 )(x3 − x2 )

y4 = c1 + c2 (x4 − x1 ) + c3 (x4 − x1 )(x4 − x2 ) + c4 (x4 − x1 )(x4 − x2 )(x4 − x3 ).


By rearranging these equations, we obtain the following four-step solution process:

c1 = y1

y2 − c1
c2 =
x2 − x1

y3 − (c1 + c2 (x3 − x1 ))
c3 =
(x3 − x1 )(x3 − x2 )

y4 − (c1 + c2 (x4 − x1 ) + c3 (x4 − x1 )(x4 − x2 ))


c4 = .
(x4 − x1 )(x4 − x2 )(x4 − x3 )
This sequential solution process is made possible by the clever choice of the basis polynomials and the result
is the Newton representation of the interpolating polynomial.
2.2. THE NEWTON REPRESENTATION 9

To set the stage for the general-n algorithm, we redo the n = 4 case using matrix-vector notation
to discover a number of simplifications. The starting point is the system of equations that we obtained
previously which can be expressed in the following form:
    
1 0 0 0 c1 y1
 1 (x2 − x1 ) 0 0   c2   y2 
    
 1 (x3 − x1 ) (x3 − x1 )(x3 − x2 ) 0   c3  =  y3  .
1 (x4 − x1 ) (x4 − x1 )(x4 − x2 ) (x4 − x1 )(x4 − x2 )(x4 − x3 ) c4 y4

From this we see immediately that c1 = y1 . We can eliminate c1 from equations 2, 3, and 4 by subtracting
equation 1 from equations 2, 3, and 4:

    
1 0 0 0 c1 y1
 0 (x2 − x1 ) 0 0   c2   y2 − y1 
  = 
 0 (x3 − x1 ) (x3 − x1 )(x3 − x2 ) 0   c3   y3 − y1  .
0 (x4 − x1 ) (x4 − x1 )(x4 − x2 ) (x4 − x1 )(x4 − x2 )(x4 − x3 ) c4 y4 − y1

If we divide equations 2, 3, and 4 by (x2 − x1 ), (x3 − x1 ), and (x4 − x1 ), respectively, then the system
transforms to

    
1 0 0 0 c1 y1
 0 1 0 0   c2   y21 
  = 
 0 1 (x3 − x2 ) 0   c3   y31  ,
0 1 (x4 − x2 ) (x4 − x2 )(x4 − x3 ) c4 y41

where y21 , y31 , and y41 are defined by


y2 − y1 y3 − y1 y4 − y1
y21 = y31 = y41 = .
x2 − x1 x3 − x1 x4 − x1
Notice that
         
y21 y2 y1 x2 x1
 y31  =  y3  −  y1  ./  x3  −  x1  = (y(2:4) − y(1))./(x(2:4) − x(1)).
y41 y4 y1 x4 x1

The key point is that we have reduced the size of problem by one. The remaining unknowns satisfy a 3-by-3
system:
    
1 0 0 c2 y21
 1 (x3 − x2 ) 0   c3  =  y31  .
1 (x4 − x2 ) (x4 − x2 )(x4 − x3 ) c4 y41
This is exactly the system obtained were we to seek the coefficients of the quadratic

q(x) = c2 + c3 (x − x2 ) + c4 (x − x2 )(x − x3 )

that interpolates the data (x2 , y21 ), (x3 , y31 ), and (x4 , y41 ).

2.2.2 The General n Case


For general n, we see that if c1 = y1 and

q(x) = c2 + c3 (x − x2 ) + · · · + cn (x − x2 ) · · · (x − xn−1 )
10 CHAPTER 2. POLYNOMIAL INTERPOLATION

interpolates the data  


yi − y1
xi , i = 2:n,
xi − x1
then
p(x) = c1 + (x − x1 )q(x)
interpolates (x1 , y1 ), . . . , (xn , yn ). This is easy to verify. Indeed, for j = 1:n
yj − y1
p(xj ) = c1 + (xj − x1 )q(xj ) = y1 + (xj − x1 ) = yj .
xj − x1

This sets the stage for a recursive formulation of the whole process:

function c = InterpNRecur(x,y)
% c = InterpNRecur(x,y)
% The Newton polynomial interpolant.
% x is a column n-vector with distinct components and y is
% a column n-vector. c is a column n-vector with the property that if
%
% p(x) = c(1) + c(2)(x-x(1))+...+ c(n)(x-x(1))...(x-x(n-1))
% then
% p(x(i)) = y(i), i=1:n.

n = length(x); c = zeros(n,1); c(1) = y(1);


if n > 1
c(2:n) = InterpNRecur(x(2:n),(y(2:n)-y(1))./(x(2:n)-x(1)));
end

If n = 1, then the constant interpolant p(x) ≡ y1 is returned (i.e., c1 = y1 .) Otherwise, the final c-vector is
a “stacking” of y1 and the solution to the reduced problem. The recursive call obtains the coefficients of the
interpolant q(x) mentioned earlier.
To develop a nonrecursive implementation, we return to our four-point example and the equation
    
1 0 0 0 c1 y1
 0 1 0 0   c2   y21 
    
 0 1 (x3 − x2 ) 0   c3  =  y31  .
0 1 (x4 − x2 ) (x4 − x2 )(x4 − x3 ) c4 y41

From this we see that c2 = y21 . Now subtract equation 2 from equation 3 and divide by (x3 − x2 ). Next,
subtract equation 2 from equation 4 and divide by (x4 − x2 ). With these operations we obtain
    
1 0 0 0 c1 y1
 0 1 0 0   c2   y21 
    ,
 0 0 1 0   c3  =  y321 
0 0 1 (x4 − x3 ) c4 y421
where
y31 − y21 y41 − y21
y321 = y421 = .
x3 − x2 x4 − x2
At this point we see that c3 = y321 . Finally, by subtracting the third equation from the fourth equation and
dividing by (x4 − x3 ), we obtain
    
1 0 0 0 c1 y1
 0 1 0 0   c2   y21 
    
 0 0 1 0   c3  =  y321  ,
0 0 0 1 c4 y4321
2.2. THE NEWTON REPRESENTATION 11

where
y421 − y321
y4321 = .
x4 − x3
Clearly, c4 = y4321 . The pattern for the general n case should be apparent:
for k=1:n-1
ck = yk
for j = k + 1:n
Subtract equation k from equation j and divide the result by (xj − xk ).
end
end
cn = yn
However, when updating the equations we need only keep track of the changes in the y-vector. For example,
       
yk+1 yk xk+1 xk
   ..  ./  ..   .. 
y(k + 1:n) ←  ...  −  .   .  −  . 
yn yk xn xk

= (y(k + 1:n) − y(k)) ./ (x(k + 1) − x(k)).


This leads to
function c = InterpN(x,y)
% c = InterpN(x,y)
% The Newton polynomial interpolant.
% x is a column n-vector with distinct components and y is
% a column n-vector. c is a column n-vector with the property that if
%
% p(x) = c(1) + c(2)(x-x(1))+...+ c(n)(x-x(1))...(x-x(n-1))
% then
% p(x(i)) = y(i), i=1:n.
n = length(x);
for k = 1:n-1
y(k+1:n) = (y(k+1:n)-y(k)) ./ (x(k+1:n) - x(k));
end
c = y;

2.2.3 Nested Multiplication


As with the Vandermonde representation, the Newton representation permits an efficient nested multiplica-
tion scheme. For example, to evaluate p3 (x) at x = z, we have the nesting
p3 (x) = ((c4 (x − x3 ) + c3 )(x − x2 ) + c2 )(x − x1 ) + c1 .
The fragment
pVal = c(4);
pVal = (z-x(3))*pVal + c(3);
pVal = (z-x(2))*pVal + c(2);
pVal = (z-x(1))*pVal + c(1);
assigns the value of p3 (z) to pVal. If z is a vector, then this becomes
pVal = c(4)*ones(size(z));
pVal = (z-x(3)).*pVal + c(3);
pVal = (z-x(2)).*pVal + c(2);
pVal = (z-x(1)).*pVal + c(1);
12 CHAPTER 2. POLYNOMIAL INTERPOLATION

In general, we have

function pVal = HornerN(c,x,z)


% pVal = HornerN(c,x,z)
% Evaluates the Newton interpolant on z where c and x are n-vectors, z is an
% m-vector, and pVal is a vector the same size as z with the property that if
%
% p(x) = c(1) + c(2)(x-x(1))+ ... + c(n)(x-x(1))...(x-x(n-1))
% then
% pVal(i) = p(z(i)) , i=1:m.

n = length(c);
pVal = c(n)*ones(size(z));
for k=n-1:-1:1
pVal = (z-x(k)).*pVal + c(k);
end
The script ShowN illustrates HornerN and InterpN.
Problems
P2.2.1 Write a Matlab function a = N2V(c,x), where c is a column n-vector, x is a column (n − 1)-vector and a is a column
n-vector, so that if
p(x) = c1 + c2 (x − x1 ) + · · · + cn (x − x1 )(x − x2 ) · · · (x − xn−1 ),
then
p(x) = a1 + a2 x + · · · + an xn−1 .
In other words, N2V converts from the Newton representation to the Vandermonde representation.

P2.2.2 Suppose we are given the data (xi, yi ), i = 1:n. Assume that the xi are distinct and that n ≥ 2. Let pL (x) and pR (x)
be degree n − 2 polynomials that satisfy
pL (xi ) = yi i = 1:n − 1

pR (xi ) = yi i = 2:n.
Note that if
(x − xn )pL(x) − (x − x1 )pR (x)
p(x) = ,
x1 − xn
then p(xi ) = yi , i = 1:n. In other words, p(x) is the unique degree n − 1 interpolant of (xi, yi ), i = 1:n. Using this result,
complete the following function:

function pVal = RecurEval(x,y,z);


%
% x is column n-vector with distinct entries, y is a column n-vector, and z is
% a column m-vector.
%
% pVal is a column m-vector with the property that pVal(i) = p(z(i))
% where p(x) is the degree n-1 polynomial interpolant of (x(i),y(i)), i=1:n.

The implementation should be recursive and vectorized. No loops are necessary! Use RecurEval to produce an interpolant of
sin(2πx) at x = 0:.25:1.

P2.2.3 Write a Matlab script that solicits the name of a built-in function (as a string), the left and right limits of an interval
[L, R], and a positive integer n and then displays both the function and the n − 1 degree interpolant of it at linspace(L,R,n).

P2.2.4 Assume that n, z(1:n), L, R, and a(1:6) are available. Write an efficient Matlab script that assigns to q(i) the value
of the polynomial
q(x) = a1 + a2 (x − L) + a3 (x − L)2 + a4 (x − L)3 + a5 (x − L)3 (x − R) + a6 (x − L)3 (x − R)2
at x = zi , i = 1:n. It doesn’t matter whether q(1:n) is a row vector or a column vector.

P2.2.5 Write a Matlab script that plots a closed curve


(px (t), py (t)) 0≤t≤1
that passes through the points (0,0), (0,3), (4,0). The functions px and py should be cubic polynomials. Make effective use of
InterpN and HornerN. The plot should be based on one hundred evaluations of px and py .
2.3. PROPERTIES 13

2.3 Properties
With two approaches to the polynomial interpolation problem, we have an occasion to assess their relative
merits. Speed and accuracy are the main concerns.

2.3.1 Efficiency
One way to talk about the efficiency of a numerical method such as InterpV or InterpN is to relate the
number of required flops to the “length” of the input. For InterpV, the amount of required arithmetic grows
as the cube of n, the number of interpolation points. We say that InterpV is an O(n3 ) method meaning that
work goes up by a factor of 8 if n is doubled. (An n-by-n linear equation solve requires about 2n3 /3 flops.)
On the other hand, InterpN is an O(n2 ) method. If we double n then work increases by an approximate
factor of 4.
Generally speaking quadratic methods (like InterpN) are to be preferred to cubic methods (like InterpV)
especially for large values of n. However, the “big-oh” predictions are typically not realized in practice for
small values of n. Moreover, counting flops does not take into account overheads associated with function
calls and memory access. Benchmarking these two methods using tic and toc would reveal that they are
not terribly different for modest n.
So far we have just discussed execution efficiency. Memory efficiency is also important. InterpV requires
an n-by-n array, while InterpN needs just a few n-vectors. In this case we say that InterpV is quadratic in
memory while InterpN is linear in memory.

2.3.2 Accuracy
We know that the polynomial interpolant exists and is unique, but how well does it approximate? The
answer to the question depends on the derivatives of the function that is being interpolated.
Theorem 2 Suppose pn−1 (x) interpolates the function f(x) at the distinct points x1 , . . . , xn . If f is n times
continuously differentiable on an interval I containing the xi , then for any x ∈ I
f (n) (η)
f(x) = pn−1 (x) + (x − x1 ) · · · (x − xn )
n!
where a ≤ η ≤ b.
Proof For clarity and with not a tremendous loss of generality, we prove the theorem for the n = 4 case.
Consider the function
F (t) = f(t) − p3 (t) − cL(t),
where
f(x) − p3 (x)
c=
(x − x1 )(x − x2 )(x − x3 )(x − x4 )
and L(t) = (t − x1 )(t − x2 )(t − x3 )(t − x4 ). Note that F (x) = 0 and F (xi ) = 0 for i = 1:4. Thus, F has at
least five zeros in I. In between these zeros F 0 has a zero and so F 0 has at least four zeros in I. Continuing
in this way, we conclude that
(4)
F (4)(t) = f (4) (t) − p3 (t) − cL(4) (t)
(4)
has at least one zero in I which we designate by ηx. Since p3 has degree ≤ 3, p3 (t) ≡ 0. Since L is a monic
(4)
polynomial with degree 4, L3 (t) = 4!. Thus,
(4)
0 = F (4)(ηx) = f (4) (ηx ) − p3 (ηx ) − cL(4)(ηx ) = f (4) (ηx ) − c · 4!. 

This result shows that the quality of pn−1 (x) depends on the size of the nth derivative. If we have a bound
on this derivative, then we can compute a bound on the error. To illustrate this point in a practical way,
suppose |f (n) (x)| ≤ Mn for all x ∈ [a, b]. It follows that for any z ∈ [a, b] we have
Mn
|f(z) − pn−1 (z)| ≤ max |(x − x1 )(x − x2 ) · · · (x − xn )|.
n! a≤x≤b
14 CHAPTER 2. POLYNOMIAL INTERPOLATION

If we base the interpolant on the equally spaced points


 
b−a
xi = a + (i − 1), i = 1:n
n−1

then, by a simple change of variable,


 n
b−a s(s − 1) · · · (s − n + 1)
|f(z) − pn−1 (z)| ≤ Mn max .

n−1 0≤s≤n−1 n!

It can be shown that the max is no bigger than 1/(4n), from which we conclude that
 n
Mn b − a
|f(z) − pn−1 (z)| ≤ . (2.2)
4n n − 1

Thus, if a function has ill-behaved higher derivatives, then the quality of the polynomial interpolants may
actually decrease as the degree increases.
A classic example of this is the problem of interpolating the function f(x) = 1/(1 + 25x2 ) across the
interval [−1, 1]. See Figure 2.3. While the interpolant “captures” the trend of the function in the middle
part of the interval, it blows up near the endpoints. The script RungeEg explores the phenomenon in greater
detail.
Equal Spacing (n = 11)
2

1.5

0.5

−0.5
−1 −0.8 −0.6 −0.4 −0.2 0 0.2 0.4 0.6 0.8 1

Figure 2.3 The Runge phenomenon

Problems
P2.3.1 Write a Matlab script that compares HornerN and HornerV from the flop point of view.

P2.3.2 Write a Matlab script that repeatedly solicits an integer n and produces a reasonable plot of the function e(s) =
|s(s − 1) · · · (s − n + 1)/n!| on the interval [0, n − 1]. Verify experimentally that this function is never bigger than 1, a fact that
we used to establish (2.2).

P2.3.3 Write a Matlab function nBest(L,R,a,delta) that returns an integer n such that if pn−1 (x) interpolates eax at
L + (i − 1)(R − L)/(n − 1), i = 1:n, then |pn−1 (z) − eaz | ≤ δ for all z ∈ [L, R]. Try to make the value of n as small as you can.

2.4 Special Topics


As a follow-up to the preceding developments, we briefly discuss properties and algorithms associated with
divided differences, inverse interpolation, and two-dimensional linear interpolation. We also introduce the
important idea of trigonometric interpolation.
2.4. SPECIAL TOPICS 15

2.4.1 Divided Differences


Returning to the n = 4 example used in the previous section, we can express c1 , c2 , c3 , and c4 in terms of
the xi and f:
c1 = f(x1 )

f(x2 ) − f(x1 )
c2 =
x2 − x1

f(x3 ) − f(x1 ) f(x2 ) − f(x1 )



x3 − x1 x2 − x1
c3 =
x3 − x2

f(x4 ) − f(x1 ) f(x2 ) − f(x1 ) f(x3 ) − f(x1 ) f(x2 ) − f(x1 )


− −
x4 − x1 x2 − x1 x3 − x1 x2 − x1

x4 − x2 x3 − x2
c4 = .
x4 − x3
The coefficients are called divided differences. To stress the dependence of ck on f and x1 , . . . , xk , we write

ck = f[x1 , . . . , xk ]

and refer to this quantity as the k − 1st order divided difference. Thus,
 
Xn k−1
Y
pn−1 (x) = f[x1 , . . . , xk ]  (x − xj )
k=1 j=1

is the n-point polynomial interpolant of f at x1 , . . . , xn .


We now establish another recursive property that relates the divided differences of f on designated subsets
of {x1 , . . . , xn}. Suppose pL (x) and pR (x) are the interpolants of f on {x1 , . . . , xk−1} and {x2 , . . . , xk },
respectively. It is easy to confirm that if

(x − xk )pL (x) − (x − x1 )pR (x)


p(x) = , (2.3)
x1 − xk

then p(xi ) = f(xi ), i = 1:k. Thus p(x) is the interpolant of f on {x1 , . . . , xk } and so

p(x) = f[x1 ] + f[x1 , x2 ](x − x1 ) + · · · + f[x1 , . . . , xn](x − x1 ) · · · (x − xk−1). (2.4)

Note that since

pL (x) = f[x1 ] + f[x1 , x2 ](x − x1 ) + · · · + f[x1 , . . . , xk−1](x − x1 ) · · · (x − xk−2 ),

the coefficient of xk−2 is given by f[x1 , . . . , xk−1]. Likewise, since

pR (x) = f[x2 ] + f[x2 , x3 ](x − x2 ) + · · · + f[x2 , . . . , xk ](x − x2 ) · · · (x − xk−1 ),

the coefficient of xk−2 is given by f[x2 , . . . , xk ]. Comparing the coefficients of xk−1 in (2.3) and (2.4), we
conclude that

f[x2 , . . . , xk ] − f[x1 , . . . , xk−1]


f[x1 , . . . , xk ] = . (2.5)
xk − x1
16 CHAPTER 2. POLYNOMIAL INTERPOLATION

f [x1 , x2 , x3 , x4 , x5 ]
H
 HH
 H
 H
 HH
 H
 H
f [x1 , x2 , x3 , x4 ] f [x2 , x3 , x4 , x5 ]
Q Q
 Q  Q
 Q  Q
 Q  Q
f [x1 , x2 , x3 ] f [x2, x3 , x4 ] f [x2, x3 , x4 ] f [x3 , x4 , x5 ]
@ @ @ @
@ @ @ @
f [x1 , x2 ] f [x2 , x3 ] f [x2 , x3 ] f [x3, x4 ] f [x2, x3 ] f [x3 , x4 ] f [x3 , x4 ] f [x4, x5 ]
A A A A A A A A
 A  A  A  A  A  A  A  A
f [x1] f [x2] f [x2 ] f [x3 ] f [x2 ] f [x3 ] f [x3] f [x4] f [x2] f [x3] f [x3 ] f [x4 ] f [x3] f [x4 ] f [x4 ] f [x5 ]

Figure 2.4 Divided differences

f [x1]
@
@
f [x2] @ f [x1 , x2 ]
@ @
@ @
f [x3] @ f [x2 , x3 ] @ f [x1 , x2 , x3 ]
@ @ @
@@ @@ @
@ f [x1, x2 , x3 , x4 ]
f [x4] f [x3 , x4 ] f [x2 , x3 , x4 ]
@ @ @ @
@ @ @ @
f [x5] @ f [x4 , x5 ] @ f [x3 , x4 , x5 ] @ f [x2, x3 , x4 , x5 ] @ f [x1, x2 , x3 , x4 , x5 ]

Figure 2.5 Efficient computation of divided differences

The development of higher-order divided differences from lower order divided differences is illustrated in
Figure 2.4. Observe that the sought-after divided differences are along the left edge of the tree. Pruning the
excess, we see that the required divided differences can be built up as shown in Figure 2.5. This enables us
to rewrite InterpN as follows:

function c = InterpN2(x,y)
% c = InterpN2(x,y)
% The Newton polynomial interpolant.
% x is a column n-vector with distinct components and y is
% a column n-vector. c is a column n-vector with the property that if
% p(x) = c(1) + c(2)(x-x(1))+...+ c(n)(x-x(1))...(x-x(n-1))
% then
% p(x(i)) = y(i), i=1:n.
n = length(x);
for k = 1:n-1
y(k+1:n) = (y(k+1:n)-y(k:n-1)) ./ (x(k+1:n) - x(1:n-k));
end
c = y;
2.4. SPECIAL TOPICS 17

A number of simplifications result if the xi are equally spaced. Assume that

xi = x1 + (i − 1)h,

where h > 0 is the spacing. From (2.5) we see that

f[x2 , . . . , xk ] − f[x1 , . . . , xk−1]


f[x1 , . . . , xk ] = .
h(k − 1)

This makes divided difference a scaling of the differences ∆f[x1 , . . . , xk ], which we define by

 f(x1 ) if k = 1
∆f[x1 , . . . , xk ] = .

∆f[x2 , . . . , xk ] − ∆f[x1 , . . . , xk−1 ] if k > 1

For example,

0th 1st 2nd 3rd 4th


Order Order Order Order Order
f1
f2 f2 − f1
f3 f3 − f2 f3 − 2f2 + f1
f4 f4 − f3 f4 − 2f3 + f2 f4 − 3f3 + 3f2 − f1
f5 f5 − f4 f5 − 2f4 + f3 f5 − 3f4 + 3f3 − f2 f5 − 4f4 + 6f3 − 4f2 + f1

It is not hard to show that


∆f[x1 , . . . , xk ]
f[x1 , . . . , xk ] = .
hk−1 (k − 1)!

The built-in function diff can be used to compute differences. In particular, if y is an n-vector, then

d = diff(y)

and

d = y(2:n) - y(1:n-1)

are equivalent. A second argument can be used to compute higher-order differences. For example,

d = diff(y,2)

computes the second-order differences:

d = y(3:n) - 2*y(2:n-1) + y(1:n-2)

Problems

P2.4.1 Compare the computed ci produced by InterpN and InterpN2.

P2.4.2 Complete the following Matlab function:

function [c,x,y] = InterpNEqual(fname,L,R,n)

Make effective use of the diff function.


18 CHAPTER 2. POLYNOMIAL INTERPOLATION

2.4.2 Inverse Interpolation


Suppose the function f(x) has√ an inverse on [a, b]. This means that there is a function g so that g(f(x)) = x
for all x ∈ [a, b]. Thus g(x) = x is the inverse of f(x) = x2 on [0, 1]. If
a = x1 < x2 < · · · < xn = b
and yi = f(xi ), then the polynomial that interpolates the data (yi , xi ), i = 1:n is an interpolate of f’s
inverse. Thus the script
x = linspace(0,1,6)’;
y = x.*x;
a = InterpV(y,x);
yvals = linspace(y(1),y(6));
xvals = HornerV(a,yvals);
plot(yvals,xvals);
plots a quintic interpolant of the square root function. This is called inverse interpolation, and it has
an important application in zero finding. Suppose f(x) is continuous and either monotone increasing or
decreasing on [a, b]. If f(a)f(b) < 0, then f has a zero in [a, b]. If q(y) is an inverse interpolant, then q(0)
can be thought of as an approximation to this root.

Problems
P2.4.3 Suppose we have three data points (x1 , y1 ), (x2 , y2 ), and (x3 , y3 ) with the property that x1 < x2 < x3 and that y1 and
y3 are opposite in sign. Write a function root = InverseQ(x,y) that returns the value of the inverse quadratic interpolant at
0.

2.4.3 Interpolation in Two Dimensions


Suppose (x̃, ỹ) is inside the rectangle
R = {(x, y) : a ≤ x ≤ b, c ≤ y ≤ d}.
Suppose f(x, y) is defined on R and that we have its values on the four corners
fac = f(a, c)
fbc = f(b, c)
fad = f(a, d)
fbd = f(b, d).
Our goal is to use linear interpolation to obtain an estimate of f(x̃, ỹ). Suppose λ ∈ [0, 1] with the property
that x̃ = (1 − λ)a + λb. It follows that
fxc = (1 − λ)fac + λfbc
fxd = (1 − λ)fad + λfbd
are linearly interpolated estimates of f(x̃, c) and f(x̃, d), respectively. Consequently, if µ ∈ [0, 1] with
ỹ = (1 − µ)c + µd, then a second interpolation between f1 and f2 gives an estimate of f(x̃, ỹ):
z = (1 − µ)fxc + µfxd ≈ f(x̃, ỹ).
Putting it all together, we see that
z = (1 − µ)((1 − λ)fac + λfbc ) + µ((1 − λ)fad + λfbd )
≈ f((1 − λ)a + λb, (1 − µ)c + µd).
Figure 2.6 depicts the interpolation points. To interpolate the values in a matrix of f(x, y) evaluations it
is necessary to “locate” the point at which the interpolation is required. The four relevant values from the
array must then be combined as described above:
2.4. SPECIAL TOPICS 19

((1 − λ)a + λb, d)


(a, d) r u r (b, d)

(a, (1 − µ)c + µd)) r u r (b, (1 − µ)c + µd))

(a, c) r u r (b, c)
((1 − λ)a + λb, c)

Figure 2.6 Linear interpolation in two dimensions

function z = LinInterp2D(xc,yc,a,b,c,d,fA)
% z = LinInterp2D(xc,yc,a,b,n,c,d,m,fA)
% Linear interpolation on a grid of f(x,y) evaluations.
% xc, yc, a, b, c, and d are scalars that satisfy a<=xc<=b and c<=yc<=d.
% fA is an n-by-m matrix with the property that
%
% A(i,j) = f(a+(i-1)(b-a)/(n-1),c+(j-1)(d-c)/(m-1)) , i=1:n, j=1:m
%
% z is a linearly interpolated value of f(xc,yc).

[n,m] = size(fA);
% xc = a+(i-1+dx)*hx 0<=dx<=1
hx = (b-a)/(n-1); i = max([1 ceil((xc-a)/hx)]); dx = (xc - (a+(i-1)*hx))/hx;
% yc = c+(j-1+dy)*hy 0<=dy<=1
hy = (d-c)/(m-1); j = max([1 ceil((yc-c)/hy)]); dy = (yc - (c+(j-1)*hy))/hy;
z = (1-dy)*((1-dx)*fA(i,j)+dx*fA(i+1,j)) + dy*((1-dx)*fA(i,j+1)+dx*fA(i+1,j+1));

The following can be used for the table-generation across a uniform grid:

function fA = SetUp(f,a,b,n,c,d,m)
% Sets up a matrix of f(x,y) evaluations.
% f is a handle to a function of the form f(x,y).
% a, b, c, and d are scalars that satisfy a<=b and c<=d.
% n and m are integers >=2.
% fA is an n-by-m matrix with the property that
%
% A(i,j) = f(a+(i-1)(b-a)/(n-1),c+(j-1)(d-c)/(m-1)) , i=1:n, j=1:m

x = linspace(a,b,n);
y = linspace(c,d,m);
fA = zeros(n,m);
for i=1:n
for j=1:m
fA(i,j) = f(x(i),y(j));
end
end
20 CHAPTER 2. POLYNOMIAL INTERPOLATION

Problems

P2.4.4 Analogous to LinInterp2D, write a function CubicInterp2D(xc,yc,a,b,n,c,d,m,fA) that does cubic interpolation from
a matrix of f (x, y) evaluations. Start by figuring out “where” (xc , yc ) is in the grid with respect to x = linspace(a,b,n) and
y = linspace(c,d,m). Suppose this is the situation: as in LinearInterp2D.

y
4

y
3

(xc,yc)

y
2

y
1
x x x x
1 2 3 4

For i = 1:4, construct a cubic pi (x) that interpolates f at (x1 , yi ), (x2 , yi ), (x3, yi ) and (x4 , yi ). Then construct a cubic q(y)
that interpolates (xc , pi (xc )), i = 1:4 and return the value of q(yc ). The above picture/plan assumes that (xc , yc ) is not in an
“edge tile” so you’ll have to work out something reasonable to do if that is the case.

P2.4.5 (a) Use SetUp to produce a matrix of function evaluations for


1
f (x, y) = .
.2(x − 3)2 + .3 ∗ (y − 1)2 + .2
Set (a, b, n, c, d, m) = (0, 5, 300, 0, 3, 150). (b) Produce a plot that shows what f “looks like” along the line segment {(x, y) | x =
5 − 5t, y = 3t, 0 ≤ t ≤ 1}. Do this by interpolating f at a sufficiently large number of points along the line segment.

P2.4.6 This problem is about two-dimensional linear interpolation inside a triangle. Suppose that we know the value of a
function f (u, v) at the vertices of triangle ABC and that we wish to estimate its value at a point P inside the following triangle:

A B

Consider the following method for doing this:


• Compute the intersection Q of line AP and line BC.
• Use linear interpolation to estimate f at Q from its value at B and C.
• Use linear interpolation to estimate f at P from its value at A and its estimate at Q.
Complete the following function so that it implements this method. Vectorization is not important.
2.4. SPECIAL TOPICS 21

A B

function fp = InterpTri(x,y,fvals,p)
% Suppose f(u,v) is a function of two variables defined everywhere in the plane.
% Assume that x, y, and fvals are column 3-vectors and p is a column 2-vector.
% Assume that (p(1),p(2)) is inside the triangle defined by (x(1),y(1)), (x(2),y(2)),
% and (x(3),y(3)) and that fvals(i) = f(x(i),y(i)) for i=1:3. fp is an estimate of
% f(p(1),p(2)) obtained by linear interpolation.

2.4.4 Trigonometric Interpolation


Suppose f(t) is a periodic function with period T , n = 2m, and that we want to interpolate the data
(t0 , f0 ), . . . , (tn , fn ) where fk = f(tk ) and

T
tk = k , k = 0:n.
n

Because the data is periodic it makes more sense to interpolate with a periodic function rather than with a
polynomial. So let us pursue an interpolant that is a linear combination of cosines and sines rather than an
interpolant that is a linear combination of 1, x, x2 , etc.
Assuming that j is an integer, the functions cos(2πjt/T ) and sin(2πjt/T ) have the same period as f
prompting us to seek real scalars a0 , . . . , am and b0 , . . . , bm so that if

m 
X    
2πj 2πj
F (t) = aj cos t + bj sin t ,
T T
j=0

then F (tk ) = fk for k = 0:n. This is a linear system that consists of n + 1 equations in 2(m + 1) = n + 2
unknowns. However, we note that b0 and bm are not involved in any equation since sin(2πjt/T ) = 0 if
t = t0 = 0 or t = tn = T . Moreover, the k = 0 equation and the k = n equation are identical because of
periodicity. Thus, we really want to determine a0 , . . . , am and b1 , . . . , bm−1 so that if

X
m−1 
2πj
 
2πj
 
2πm

F (t) = a0 + aj cos t + bj sin t + am cos t ,
T T T
j=1

then F (tk ) = f(tk ) = fk for k = 0:n − 1. This is an n-by-n linear system in n unknowns:

m−1
X
fk = a0 + (aj cos(kjπ/m) + bj sin(kjπ/m)) + (−1)k am k = 0:n − 1.
j=1
22 CHAPTER 2. POLYNOMIAL INTERPOLATION

Here is what the system looks like for the case n = 6 with angles specified in degrees:
    
f0 cos(0) cos(0) cos(0) cos(0) sin(0) sin(0) a0
    
 f1   cos(0) cos(60) cos(120) cos(180) sin(60) sin(120)   a1 
    
 f2   cos(0) cos(120) cos(240) cos(360) sin(120) sin(240)   a2 
    
  =   
 f3   cos(0) cos(180) cos(360) cos(540) sin(180) sin(360)   a3 
    
 f4   cos(0) cos(240) cos(480) cos(720) sin(240) sin(480)  
 b1 
    
f5 cos(0) cos(300) cos(600) cos(900) sin(300) sin(600) b2

  
1 1 1 1 0 0 a0
 √ √  
 1 1/2 −1/2 −1 3/2 3/2  a1 
 √ √  
 1 −1/2 −1/2 1 3/2 − 3/2  a2 
=   
.
 1 −1 1 −1 0 0 
  a3 
 √ √  
 1 −1/2 −1/2 1 − 3/2 3/2 
 b1 

√ √
1 1/2 −1/2 −1 − 3/2 − 3/2 b2

For general even n, CSInterp sets up the defining linear system and solves for a and b:

function F = CSInterp(f)
% F = CSInterp(f)
% f is a column n vector and n = 2m.
% F.a is a column m+1 vector and F.b is a column m-1 vector so that if
% tau = (pi/m)*(0:n-1)’, then
% f = F.a(1)*cos(0*tau) +...+ F.a(m+1)*cos(m*tau) +
% F.b(1)*sin(tau) +...+ F.b(m-1)*sin((m-1)*tau)

n = length(f); m = n/2;
tau = (pi/m)*(0:n-1)’;
P = [];
for j=0:m, P = [P cos(j*tau)]; end
for j=1:m-1, P = [P sin(j*tau)]; end
y = P\f;
F = struct(’a’,y(1:m+1),’b’,y(m+2:n));

Note that the a and b vectors are returned in a structure. The matrix of coefficients can be shown to be
nonsingular so the interpolation process that we have presented is well-defined. However, it involves O(n3 )
flops because of the linear system solve. In P2.4.7 we show how to reduce this to O(n2 ). The evaluation of
the trigonmetric interpolant can be handled by

function Fvals = CSeval(F,T,tvals)


% F.a is a length m+1 column vector, F.b is a length m-1 column vector,
% T is a positive scalar, and tvals is a column vector.
% If
% F(t) = F.a(1) + F.a(2)*cos((2*pi/T)*t) +...+ F.a(m+1)*cos((2*m*pi/T)*t) +
% F.b(1)*sin((2*pi/T)*t) +...+ F.b(m-1)*sin((2*m*pi/T)*t)
%
% then Fvals = F(tvals).
Fvals = zeros(length(tvals),1);
tau = (2*pi/T)*tvals;
for j=0:length(F.a)-1, Fvals = Fvals + F.a(j+1)*cos(j*tau); end
for j=1:length(F.b), Fvals = Fvals + F.b(j)*sin(j*tau); end
2.4. SPECIAL TOPICS 23

We close this section by applying CSinterp and CSeval to a data fitting problem that confronted Gauss
in connection with the asteroid Pallas. The problem is to interpolate the following ascension-declination
data
α 0 30 60 90 120 150 180 210 240 270 300 330
d 408 89 -66 10 338 807 1238 1511 1583 1462 1183 804
with a function of the form
5
X
d(α) = a0 + [aj cos(2πjα/360) + bj sin(2πjα/360)] + a6 cos(12πα/360).
j=1

Here is a script that does this and plots the results shown in Figure 2.7:

% Script File: Pallas


% Plots the trigonometric interpolant of the Gauss Pallas data.
A = linspace(0,360,13)’;
D = [ 408 89 -66 10 338 807 1238 1511 1583 1462 1183 804 408]’;
Avals = linspace(0,360,200)’;
F = CSInterp(D(1:12));
Fvals = CSeval(F,360,Avals);

plot(Avals,Fvals,A,D,’o’)
axis([-10 370 -200 1700])
set(gca,’xTick’,linspace(0,360,13))
xlabel(’Ascension (Degrees)’)
ylabel(’Declination (minutes)’)

1600

1400

1200

1000
Declination (minutes)

800

600

400

200

−200
0 30 60 90 120 150 180 210 240 270 300 330 360
Ascension (Degrees)

Figure 2.7 Fitting the Pallas data

Problems

P2.4.7 Observe that the matrix of coefficients P in CSinterp has the property that P T P is diagonal. Use this fact to reduce
the flop count in that function from O(n3 ) to O(n2 ). (With the fast Fourier transform it is possible to actually the flop count
to an amazing O(n log n). )
24 CHAPTER 2. POLYNOMIAL INTERPOLATION

M-Files and References

Script Files
ShowV Illustrates InterpV and HornerV
ShowN Illustrates InterpN and HornerN
ShowRungePhenom Examines accuracy of interpolating polynomial.
TableLookUp2D Illustrates SetUp and LinInterp2D.
Pallas Fits periodic data with CSInterp and CSEval.

Function Files
InterpV Construction of Vandermonde interpolating polynomial.
HornerV Evaluates the Vandermonde interpolating polynomial.
InterpNRecur Recursive construction of the Newton interpolating polynomial.
InterpN Nonrecursive construction of the Newton interpolating polynomial.
InterpN2 Another nonrecursive construction of the Newton interpolating polynomial.
HornerN Evaluates the Newton interpolating polynomial.
SetUp Sets up matrix of f(x,y) evaluation.
LinInterp2D 2-Dimensional Linear Interpolation.
Humps2D A sample function of two variables.
CSInterp Fits periodic data with sines and cosines.
CSEval Evaluates sums of sines and cosines.
ShowMatPolyTools Illustrates polyfit and polyval.

References
W.L. Briggs and V.E. Henson (1995). The DFT: An Owner’s Manual for the Discrete Fourier Transform,
SIAM Publications, Philadelphia, PA.
S.D. Conte and C. de Boor (1980). Elementary Numerical Analysis: An Algorithmic Approach, Third
Edition, McGraw-Hill, New York.
P. Davis (1963). Interpolation and Approximation, Blaisdell, New York.
Chapter 3

Piecewise Polynomial Interpolation

§3.1 Piecewise Linear Interpolation


§3.2 Piecewise Cubic Hermite Interpolation
§3.3 Cubic Splines
An important lesson from Chapter 2 is that high-degree polynomial interpolants at equally-spaced points
should be avoided. This can pose a problem if we are to produce an accurate interpolant across a wide
interval [α, β]. One way around this difficulty is to partition [α, β],

α = x1 < x2 < · · · < xn = β


and then interpolate the given function on each subinterval [xi , xi+1 ] with a polynomial of low degree. This
is the piecewise polynomial interpolation idea. The xi are called breakpoints.
We begin with piecewise linear interpolation working with both fixed and adaptively determined break-
points. The latter requires a classical divide-and-conquer approach that we shall use again in later chapters.
Piecewise linear functions do not have a continuous first derivative, and this creates problems in certain
applications. Piecewise cubic Hermite interpolants address this issue. In this setting, the value of the
interpolant and its derivative is specified at each breakpoint. The local cubics join in a way that forces first
derivative continuity.
Second derivative continuity can be achieved by carefully choosing the first derivative values at the
breakpoints. This leads to the topic of splines, a very important idea in the area of approximation and
interpolation. It turns out that cubic splines produce the smoothest solution to the interpolation problem.

3.1 Piecewise Linear Interpolation


Assume that x(1:n) and y(1:n) are given where α = x1 < · · · < xn = β and yi = f(xi ), i = 1:n. If you
connect the dots (x1 , y1 ), . . . , (xn , yn ) with straight lines, as in Figure 3.1, then the graph of a piecewise
linear function is displayed. We already have considerable experience with such functions, for this is what
plot(x,y) displays.

3.1.1 Set-Up
The piecewise linear interpolant is built upon the local linear interpolants

Li (z) = ai + bi (z − xi ),

where for i = 1:n − 1 the coefficients are defined by


yi+1 − yi
ai = yi and bi = .
xi+1 − xi

1
2 CHAPTER 3. PIECEWISE POLYNOMIAL INTERPOLATION

0.8

0.6

0.4

0.2

−0.2

−0.4

−0.6

−0.8

−1

−0.2 0 0.2 0.4 0.6 0.8 1 1.2

Figure 3.1 A piecewise linear function

Note that Li (z) is just the linear interpolant of f at the points x = xi and x = xi+1 . We then define


 L1 (z) if x1 ≤ z < x2

 L2 (z) if x2 ≤ z < x3
L(z) = .. .. .

 . .


Ln−1 (z) if xn−1 ≤ z ≤ xn
The act of setting up L is the act of solving each of the local linear interpolation problems. The n − 1 divided
differences b1 , . . . , bn−1 can obviously be computed by a loop,
for i=1:n-1
b(i) = (y(i+1)-y(i))/(x(i+1)-x(i));
end
or by using pointwise division,
b = (y(2:n)-y(1:n-1)) ./ (x(2:n)-x(1:n-1))
or by using the built-in function diff:
b = diff(y) ./ diff(x)
Packaging these operations we obtain
function [a,b] = pwL(x,y)
% Generates the piecewise linear interpolant of the data specified by the
% column n-vectors x and y. It is assumed that x(1) < x(2) < ... < x(n).
%
% a and b are column (n-1)-vectors with the property that for i=1:n-1, the
% line L(z) = a(i) + b(i)z passes though the points (x(i),y(i)) and (x(i+1),y(i+1)).

n = length(x);
a = y(1:n-1);
b = diff(y) ./ diff(x);
Thus,
z = linspace(0,1,9);
[a,b] = pwL(z,sin(2*pi*z));
sets up a piecewise linear interpolant of sin(2πz) on a uniform, nine-point partition of [0, 1].
3.1. PIECEWISE LINEAR INTERPOLATION 3

3.1.2 Evaluation
To evaluate L at a point z ∈ [α, β], it is necessary to determine the subinterval that contains z. In our
problem x has the property that x1 < · · · < xn and so sum(x<=z) is the number of xi that are to the left of
z or equal to z. It follows that

if z == x(n);
i = n-1;
else
i = sum(x<=z);
end

determines the index i so that xi ≤ z ≤ xi+1 . Notice the special handling of the case when z equals xn .
(Why?) A total of n comparisons are made because every component in x is compared to z.
A better approach is to exploit the monotonicity of the xi and to use binary search. Here is the main
idea. Suppose we have indices Left and Right so that xLeft ≤ z ≤ xRight . If mid = floor((Left + Right)/2),
then by checking z’s relation to xmid we can halve the search space by redefining Left or Right accordingly:

mid = floor((Left+Right)/2);
if z < x(mid)
Right = mid;
else
Left = mid;
end

Repeated application of this process eventually identifies the subinterval that houses z:

if z == x(n)
i = n-1;
else
Left = 1; Right = n;
while Right > Left+1
% z is in [x(Left),x(Right)].
mid = floor((Left+Right)/2);
if z < x(mid)
Right = mid;
else
Left = mid;
end
end
i = Left;
end

Upon completion, i contains the index of the subinterval that contains z. If n = 10 and z ∈ [x6 , x7 ], then
here is the succession of Left and Right values produced by the binary search method:

Left Right mid


1 10 5
5 10 7
5 7 6
6 7 -

Roughly log2 (n) comparisons are required to locate the appropriate subinterval. If n is large, then this is
much more efficient than the sum(x<z) method, which requires n comparisons.
For “random” z, we can do no better than binary search. However, if L is to be evaluated at an ordered
succession of points, then we can improve the subinterval location process. For example, suppose we want to
plot L on [α, β]. This requires the assembly of the values L(z1 ), . . . , L(zm ) in a vector where m is a typically
large integer and α ≤ z1 ≤ · · · ≤ zm ≤ β. Rather than locate each zi via binary search, it is more efficient to
4 CHAPTER 3. PIECEWISE POLYNOMIAL INTERPOLATION

exploit the systematic “migration” of the evaluation point as it moves left to right across the subintervals.
Chances are that if i is the subinterval index associated with the current z-value, then i will be the correct
index for the next z-value. This “guess” at the correct subinterval can be checked before we launch the
binary search process.

function i = locate(x,z,g)
% Locates z in a partition x.
% x is column n-vector with x(1) < x(2) <...<x(n) and
% z is a scalar with x(1) <= z <= x(n).
% g (1<=g<=n-1) is an optional input parameter
% i is an integer such that x(i) <= z <= x(i+1). Before the general
% search for i begins, the value i=g is tried.
if nargin==3
% Try the initial guess.
if (x(g)<=z) & (z<=x(g+1))
i = g;
return
end
end
n = length(x);
if z==x(n)
i = n-1;
else
% Binary Search
Left = 1; Right = n;
while Right > Left+1
% x(Left) <= z <= x(Right)
mid = floor((Left+Right)/2);
if z < x(mid)
Right = mid;
else
Left = mid;
end
end
i = Left;
end

This function makes use of the return command. This terminates the execution of the function. It is
possible to restructure locate to avoid the return, but the resulting logic would be cumbersome. As an
application of locate, here is a function that produces a vector of L-values:

function LVals = pwLeval(a,b,x,zVals)


% Evaluates the piecewise linear polynomial defined by the column (n-1)-vectors
% a and b and the column n-vector x. It is assumed that x(1) < ... < x(n).
% zVals is a column m-vector with each component in [x(1),x(n)].
% LVals is a column m-vector with the property that LVals(j) = L(zVals(j))
% for j=1:m where L(z)= a(i) + b(i)(z-x(i)) for x(i)<=z<=x(i+1).

m = length(zVals); LVals = zeros(m,1); g = 1;


for j=1:m
i = locate(x,zVals(j),g);
LVals(j) = a(i) + b(i)*(zVals(j)-x(i));
g = i;
end
3.1. PIECEWISE LINEAR INTERPOLATION 5

Interpolation of humps(x) with pwL, n = 10


100

80

60

40

20

−20
0 0.5 1 1.5 2 2.5 3

Figure 3.2 Piecewise linear approximation

The following script illustrates the use of this function, producing a sequence of piecewise linear approxima-
tions to the built-in function
1 1
humps(x) = + − 6.
(x − .3) + .01 (x − .9)2 + .04
2

% Script File: ShowPWL1


% Convergence of the piecewise linear interpolant to
% humps(x) on [0,3]
close all
z = linspace(0,3,200)’;
fvals = humps(z);
for n = [5 10 25 50]
figure
x = linspace(0,3,n)’;
y = humps(x);
[a,b] = pwL(x,y);
Lvals = pwLEval(a,b,x,z);
plot(z,Lvals,z,fvals,’--’,x,y,’o’);
title(sprintf(’Interpolation of humps(x) with pwL, n = %2.0f’,n))
end
(See Figure 3.2 for the 10-point case.) Observe that more interpolation points are required in regions where
humps is particularly nonlinear.

3.1.3 A Priori Determination of the Breakpoints


Let us consider how many breakpoints we need to obtain a satisfactory piecewise linear interpolant. If
z ∈ [xi , xi+1 ], then from Theorem 2,
f (2) (η)
f(z) = L(z) + (z − xi )(z − xi+1 ),
2
where η ∈ [xi , xi+1 ]. If the second derivative of f on [α, β] is bounded by M2 and if h̄ is the length of the
longest subinterval in the partition, then it is not hard to show that

M2 h̄2
|f(z) − L(z)| ≤
8
6 CHAPTER 3. PIECEWISE POLYNOMIAL INTERPOLATION

for all z ∈ [α, β].


A typical situation where this error bound can be put to good use is in the design of the underlying
partition upon which L is based. Assume that L(x) is based on the uniform partition

α = x1 < x2 < · · · < xn = β,


where
i−1
xi = α + (β − α).
n−1
To ensure that the error between L and f is less than or equal to a given positive tolerance δ, we insist that
 2
M2 h̄2 M2 β − α
|f(z) − L(z)| ≤ = ≤ δ.
8 8 n−1

From this we conclude that n must satisfy


p
n ≥ 1 + (β − α) M2 /8δ.

For the sake of efficiency, it makes sense to let n be the smallest integer that satisfies this inequality:

function [x,y] = pwLstatic(f,M2,alpha,beta,delta)


% Generates interpolation points for a piecewise linear approximation of
% prescribed accuracy.
%
% f is a handle that references a function f(x).
% Assume that f can take vector arguments.
% M2 is an upper bound for|f"(x)| on [alpha,beta].
% alpha and beta are scalars with alpha<beta.
% delta is a positive scalar.
%
% x and y column n-vectors with the property that y(i) = f(x(i)), i=1:n.
% The piecewise linear interpolant L(x) of this data satisfies
% |L(z) - f(z)| <= delta for x(1) <= z <= x(n).

n = max(2,ceil(1+(beta-alpha)*sqrt(M2/(8*delta))));
x = linspace(alpha,beta,n)’;
y = f(x);

The partition produced by pwLstatic does not take into account the sampled values of f. As a result, the
uniform partition produced may be much too refined in regions where f 00 is much smaller than the bound
M2 .

3.1.4 Adaptive Piecewise Linear Interpolation


Suppose f is very nonlinear over just a small portion of [α, β] and very smooth elsewhere. (See Figure 3.2.)
This means that if we use pwLstatic to generate the partition, then we are compelled to use a large M2.
Lots of subintervals and (perhaps costly) f-evaluations will be required. Over regions where f is smooth,
the partition will be overly refined.
To address this problem, we develop a recursive partitioning algorithm that “discovers” where f is “extra
nonlinear” and that clusters the breakpoints accordingly. A definition simplifies the discussion. We say that
the subinterval [xL, xR] is acceptable if
 

f xL + xR − f(xL) + f(xR) ≤ δ
2 2
or if
xR − xL ≤ hmin ,
3.1. PIECEWISE LINEAR INTERPOLATION 7

where δ > 0 and hmin > 0 are (typically small) refinement parameters. The first condition measures the
discrepancy between the line that connects (xL, f(xL)) and (xR, f(xR)) and the function f(x) at the interval
midpoint m = (xL+xR)/2. The second condition says that sufficiently short subintervals are also acceptable
where “sufficiently short” means less than hmin in length.
One more definition is required before we can describe the complete partitioning process. A partition
x1 < · · · < xn is acceptable if each subinterval is acceptable. Note that if
(L)
xL = x1 < · · · < x(L)
n = m

is an acceptable partition of [xL, m] and if


(R)
m = x1 < · · · < x(R)
n = xR

is an acceptable partition of [m, xR], then


(L) (R)
xL = x1 < · · · < x(L)
n < x2 < · · · < x(R)
n = xR

is an acceptable partition of [xL, xR]. This sets the stage for a recursive determination of an acceptable
partition:

function [x,y] = pwLadapt(f,xL,fL,xR,fR,delta,hmin)


% Adaptively determines interpolation points for a piecewise linear
% approximation of a specified function.
%
% f is a handle that references a function of the form y = f(u).
% xL and xR are real scalars and fL = f(xL) and fR = f(xR).
% delta and hmin are positive real scalars that determine accuracy.
%
% x and y are column n-vectors with the property that
% xL = x(1) < ... < x(n) = xR
% and y(i) = f(x(i)), i=1:n. Each subinterval [x(i),x(i+1)] is
% either <= hmin in length or has the property that at its midpoint m,
% |f(m) - L(m)| <= delta where L(x) is the line that connects (x(i),y(i))
% and (x(i+1),y(i+1)).

if (xR-xL) <= hmin


% Subinterval is acceptable
x = [xL;xR];
y = [fL;fR];
else
mid = (xL+xR)/2;
fmid = f(mid);
if (abs(((fL+fR)/2) - fmid) <= delta )
% Subinterval accepted.
x = [ xL;xR];
y = [fL;fR];
else
% Produce left and right partitions, then synthesize.
[xLeft,yLeft] = pwLAdapt(f,xL,fL,mid,fmid,delta,hmin);
[xRight,yRight] = pwLAdapt(f,mid,fmid,xR,fR,delta,hmin);
x = [ xLeft;xRight(2:length(xRight))];
y = [ yLeft;yRight(2:length(yRight))];
end
end
8 CHAPTER 3. PIECEWISE POLYNOMIAL INTERPOLATION

delta = 0.0500 Static n= 668 delta = 0.0500 Adapt n= 130


100 100

80 80

60 60

40 40

20 20

0 0

−20 −20
0 0.5 1 1.5 2 2.5 3 0 0.5 1 1.5 2 2.5 3

Figure 3.3 Static versus adaptive approximation

The idea behind the function is to check and see if the input interval is acceptable. If it is not, then acceptable
partitions are obtained for the left and right half intervals. These are then “glued” together to obtain the
final, acceptable partition.
The distinction between static and adaptive piecewise linear interpolation is revealed by running the
following script:

% Script File: ShowpwL2


% Compares pwLstatic and pwLsdapt on [0,3] using the function
%
% humps(x) = 1/((x-.3)^2 + .01) + 1/((x-.9)^2+.04)
%
close all
% Second derivative estimate based on divided differences
z = linspace(0,1,101);
humpvals = humps(z);
M2 = max(abs(diff(humpvals,2)/(.01)^2));
for delta = [1 .5 .1 .05 .01]
figure
[x,y] = pwLstatic(@humps,M2,0,3,delta);
subplot(1,2,1)
plot(x,y,’.’);
title(sprintf(’delta = %8.4f Static n= %2.0f’,delta,length(x)))
[x,y] = pwLadapt(@humps,0,humps(0),3,humps(3),delta,.001);
subplot(1,2,2)
plot(x,y,’.’);
title(sprintf(’delta = %8.4f Adapt n= %2.0f’,delta,length(x)))
set(gcf,’position’,[200 200 1200 500])
end

(See Figure 3.3.) The humps function is very nonlinear in the vicinity of x = .3. A second derivative bound
is approximated with differences and used in pwLstatic. In the example approximately four times as many
function evaluations are required when the static approach is taken.
3.2. PIECEWISE CUBIC HERMITE INTERPOLATION 9

Problems
P3.1.1 Generalize locate so that it tries i = g + 1 and i = g − 1 before resorting to binary search. (Take care to guard against
subscript out-of-range.) Implement pwLeval with this modified subinterval locator and document the speed-up.

P3.1.2 Write a function i = LocateUniform(alpha,beta,n,z) that assumes [α, β] is partitioned into n − 1 subintervals of equal
length and returns the index of the interval that houses z.

P3.1.3 What happens if pwLadapt is applied to sin(x) with [α, β] = [0, 2π]?

P3.1.4 Describe what would happen if pwLadapt is called with delta = 0.

P3.1.5 Describe why the number of recursive calls in pwLadapt is bounded if |f 00(x)| is bounded on [α, β].

P3.1.6 Modify pwLadapt so that a subinterval is accepted if |f (p) − λ(p)| and |f (q) − λ(q)| are less than or equal to delta,
where p = (2xL + xR)/3, q = (xL + 2xR)/3, and λ(x) is the line that connects (xL, fL) and (xR, fR). Avoid redundant function
evaluations.

P3.1.7 If pwLadapt is applied to the function f (x) = x on the interval [0,1], then a partition x(1:n) is produced that satisfies
x2 − x1 ≤ x3 − x2 ≤ · · · ≤ xn − xn−1 .
Why?

P3.1.8 Generalize pwLadapt(f,xL,fL,xR,fR,delta,hmin) to

function [x,y,eTaken] = pwLadapt(f,xL,fL,xR,fR,delta,hmin,eMax)

so that no more than eMax function evaluations are taken. The value of eTaken should be the actual number of function
evaluations spent. Let n = length(x). In a “successful” call, x(n) should equal xR, meaning that a satisfactory piecewise
linear approximation was found extending across the entire interval [xL, xR]. If this is not the case, then the evaluation limit
was encountered before xR was reached and x(n) will be less than xR. In this situation vectors returned define a satisfactory
piecewise linear approximation across [x(1), x(n)].

P3.1.9 Notice that in pwLadapt the vector y does not include all the computed function evaluations. So that these evaluations
are not lost, generalize pwLadapt to

[x,y,xUnused,yUnused] = pwLadaptGen(f,xL,fL,xR,fR,delta,hmin,...)

where (a) the x and y vectors are identical to what pwLadapt computes and (b) xUnused and yUnused are column vectors that
contain the x-values and function values that were computed, but not included in x and y. Thus, the xUnused and yUnused
vectors should have the property that yUnused(i) = feval(fname,xUnused(i)), i = 1:length(xUnused). You are allowed to
extend the calling sequence if convenient. In that case, indicate the values that should be passed through these new parameters
at the top-level call. xUnused and yUnused should be assigned the empty vector [ ] if xR-xL<hmin. The order of the values in
xUnused is not important.

P3.1.10 Vectorize locate and pwLeval.

3.2 Piecewise Cubic Hermite Interpolation


Now let’s graduate to piecewise cubic functions. With the increase in degree we can obtain a smoother fit
to a given set of n points. The idea is to interpolate both f and its derivative with a cubic on each of the
subintervals.

3.2.1 Cubic Hermite Interpolation


So far we have only considered the interpolation of function values at distinct points. In the Hermite
interpolation problem, both the function and its derivative are interpolated. To illustrate the idea, we
consider the interpolation of the function f(z) = cos(z) at the points x1 = 0, x2 = δ, x3 = 3π/2 − δ, and
x4 = 3π/2 by a cubic p3 (z). For small δ we notice that p3 (z) seems to interpolate both f and f 0 at z = 0 and
z = 3π/2. The interpolation shown in Figure 3.4 on the next page was obtained by running the following
script:
10 CHAPTER 3. PIECEWISE POLYNOMIAL INTERPOLATION

Interpolation of cos(x). Separation = 0.300


1.5

0.5

−0.5

−1

−1.5
−0.5 0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 5

Figure 3.4 A “nearly” Hermite interpolation

% Script File: ShowHermite


% Plots a succession of cubic interpolants to cos(x).
% x(2) converges to x(1) = 0 and x(3) converges to x(4) = 3pi/2.
close all
z = linspace(-pi/2,2*pi,100);
CosValues = cos(z);
for d = [1 .5 .3 .1 .05 .001]
figure
xvals = [0;d;(3*pi/2)-d;3*pi/2];
yvals = cos(xvals);
c = InterpN(xvals,yvals);
CubicValues = HornerN(c,xvals,z);
plot(z,CosValues,z,CubicValues,’--’,xvals,yvals,’*’)
axis([-.5 5 -1.5 1.5])
title(sprintf(’Interpolation of cos(x). Separation = %5.3f’,d))
end

As the points coalesce, the cubic converges to a cubic interpolant of the cosine and its derivative at the
points 0 and 3π/2. This is called the Hermite cubic interpolant.
In the general cubic Hermite interpolation problem, we are given function values yL and yR and derivative
values sL and sR and seek coefficients a, b, c, and d so that if

q(z) = a + b(z − xL ) + c(z − xL)2 + d(z − xL )2 (z − xR ),

then
q(xL ) = yL q(xR ) = yR
0
q (xL) = sL q 0 (xR ) = sR .
Each of these equations “says” something about the unknown coefficients. Noting that

q 0 (z) = b + 2c(z − xL ) + d(2(z − xL )(z − xR ) + (z − xL)2 ),

we see that
a = yL a + b∆x + c(∆x)2 = yR
b = sL b + 2c∆x + d(∆x)2 = sR ,
3.2. PIECEWISE CUBIC HERMITE INTERPOLATION 11

where ∆x = xR − xL . Expressing this in matrix-vector we obtain


    
1 0 0 0 a yL
 0 1 0 0  b   sL 
    
 1 ∆x (∆x)2 0  c  =  yR  .
0 1 2∆x (∆x)2 d sR
The solution to this triangular system is straightforward:

a = yL
b = sL
0
yL − sL
c =
∆x
0
sR + sL − 2yL
d =
(∆x)2

where
0 yR − yL yR − yL
yL = = .
∆x xR − xL
Thus, we obtain
function [a,b,c,d] = HCubic(xL,yL,sL,xR,yR,sR)
% Cubic Hermite interpolation
% (xL,yL,sL) and (xR,yR,sR) are x-y-slope triplets with xL and xR distinct.
% a,b,c,d are real numbers with the property that if
% p(z) = a + b(z-xL) + c(z-xL)^2 + d(z-xL)^2(z-xR)
% then p(xL)=yL, p’(xL)=sL, p(xR)=yR, p’(xR)=sR.
a = yL; b = sL; delx = xR - xL;
yp = (yR - yL)/delx;
c = (yp - sL)/delx;
d = (sL - 2*yp + sR)/(delx*delx);
An error expression for the cubic Hermite interpolant can be derived from Theorem 3.

Theorem 3 Suppose f(z) and its first four derivatives are continuous on [xL, xR ] and that the constant M4
satisfies
|f (4) (z)| ≤ M4
for all z ∈ [L, R]. If q is the cubic Hermite interpolant of f at xL and xR , then
M4 4
|f(z) − q(z)| ≤ h ,
384
where h = xR − xL .

Proof If qδ (z) is the cubic interpolant of f at xL , xL + δ, xR − δ, and xR , then from Theorem 2 we have

M4
|f(z) − qδ (z)| ≤ |(z − xL)(z − xL − δ)(z − xR + δ)(z − xR )|
24
for all z ∈ [xL, xR ]. We assume without proof1 that

lim qδ (z) = q(z)


δ→0

and so
M4
|f(z) − q(z)| ≤ |(z − xL )(z − xL )(z − xR )(z − xR )|.
24
1 But check out ShowHermite
12 CHAPTER 3. PIECEWISE POLYNOMIAL INTERPOLATION

The maximum value of the quartic polynomial on the right occurs at the midpoint z = xL + h/2 and so for
all z in the interval [xL, xR] we have
 4
M4 h M4 4
|f(z) − q(z)| ≤ = h . 
24 2 384

Theorem 3 says that if the interval length is divided by 10, then the error bound is reduced by a factor of
104 .

3.2.2 Representation and Set-Up


We now show how to glue a sequence of Hermite cubic interpolants together so that the resulting piecewise
cubic polynomial C(z) interpolates the data (x1 , y1 ), . . . , (xn , yn ), with the prescribed slopes s1 , . . . , sn . To
that end we assume x1 < x2 < · · · < xn and define the ith local cubic by

qi (z) = ai + bi (z − xi ) + ci (z − xi )2 + di (z − xi )2 (z − xi+1 ).

Define the piecewise cubic polynomial by




 q1 (z) if x1 ≤ z < x2

 q2 (z) if x2 ≤ z < x3
C(z) = .. .. .

 . .


qn−1 (z) if xn−1 ≤ z ≤ xn

Our goal is to determine a(1:n − 1), b(1:n − 1), c(1:n − 1), and d(1:n − 1) so that

C(xi ) = yi
, i = 1:n
C 0 (xi ) = si

This will be the case if we simply solve the following n − 1 cubic Hermite problems:

qi (xi ) = yi
qi0 (xi ) = si
qi (xi+1 ) = yi+1
qi0 (xi+1 ) = si+1
The results of §3.2.1 apply:

yi0 − si si+1 + si − 2yi0


ai = yi , bi = si , ci = , di = ,
∆xi (∆xi )2

where ∆xi = xi+1 − xi and


yi+1 − yi yi+1 − yi
yi0 = = .
∆xi xi+1 − xi
We could use HCubic to resolve the coefficients:

for i=1:n-1
[a(i), b(i), c(i), d(i)] = HCubic(x(i),y(i),s(i),x(i+1),y(i+1),s(i+1))
end

But a better solution is to vectorize the computation, and this gives


3.2. PIECEWISE CUBIC HERMITE INTERPOLATION 13

function [a,b,c,d] = pwC(x,y,s)


% Piecewise cubic Hermite interpolation.
%
% x,y,s column n-vectors with x(1) < ... < x(n)
%
% a,b,c,d column (n-1)-vectors that define a continuous, piecewise
% cubic polynomial q(z) with the property that for i = 1:n,
%
% q(x(i)) = y(i) and q’(x(i)) = s(i).
%
% On the interval [x(i),x(i+1)],
%
% q(z) = a(i) + b(i)(z-x(i)) + c(i)(z-x(i))^2 + d(i)(z-x(i))^2(z-x(i+1)).
n = length(x);
a = y(1:n-1);
b = s(1:n-1);
Dx = diff(x);
Dy = diff(y);
yp = Dy ./ Dx;
c = (yp - s(1:n-1)) ./ Dx;
d = (s(2:n) + s(1:n-1) - 2*yp) ./ (Dx.* Dx);

If M4 bounds |f (4)(x)| on the interval [x1 , xn ], then Theorem 3 implies that

M4 4
|f(z) − C(z)| ≤ h̄
384

for all z ∈ [x1 , xn ], where h̄ is the length of the longest subinterval (i.e., maxi |xi+1 − xi |.)

3.2.3 Evaluation
The evaluation of C(z) has two parts. As with any piecewise polynomial that must be evaluated, the
position of z in the partition must be ascertained. Once that is accomplished, the relevant local cubic must
be evaluated. Here is a function that can be used to evaluate C at a vector of z values:

function Cvals = pwCeval(a,b,c,d,x,zVals)


% Evaluates the pwC defined by the column (n-1)-vectors a,b,c, and
% d and the column n-vector x. It is assumed that x(1) < ... < x(n).
% zVals is a column m-vector with each component in [x(1),x(n)].
%
% CVals is a column m-vector with the property that CVals(j) = C(zVals(j))
% for j=1:m where on the interval [x(i),x(i+1)]
%
% C(z)= a(i) + b(i)(z-x(i)) + c(i)(z-x(i))^2 + d(i)(z-x(i))^2(z-x(i+1))
m = length(zVals);
Cvals = zeros(m,1);
g=1;
for j=1:m
i = Locate(x,zVals(j),g);
Cvals(j) = d(i)*(zVals(j)-x(i+1)) + c(i);
Cvals(j) = Cvals(j)*(zVals(j)-x(i)) + b(i);
Cvals(j) = Cvals(j)*(zVals(j)-x(i)) + a(i);
g = i;
end
14 CHAPTER 3. PIECEWISE POLYNOMIAL INTERPOLATION

Analogous to pwLeval, we use Locate to determine the subinterval that houses the jth evaluation point zj .
The cubic version of HornerN is then used to evaluate the appropriate local cubic. The following script file
illustrates the use of pwC and pwCeval:
% Script File: ShowpwCH
% Convergence of the piecewise cubic hermite interpolant to
% exp(-2x)sin(10*pi*x) on [0,1].)
close all
z = linspace(0,1,200)’;
fvals = exp(-2*z).*sin(10*pi*z);
for n = [4 8 16 24]
x = linspace(0,1,n)’;
y = exp(-2*x).*sin(10*pi*x);
s = 10*pi*exp(-2*x).*cos(10*pi*x)-2*y;
[a,b,c,d] = pwC(x,y,s);
Cvals = pwCeval(a,b,c,d,x,z);
figure
plot(z,fvals,z,Cvals,’--’,x,y,’*’);
title(sprintf(’Interpolation of exp(-2x)sin(10pi*x) with pwCH, n = %2.0f’,n))
end
legend(’e^{-2z}sin(10\pi z)’,’The pwC interpolant’)
Sample output is displayed in Figure 3.5.

Interpolation of exp(−2x)sin(10pi*x) with pwCH, n = 8


1
−2z
e sin(10π z)
0.8 The pwC interpolant

0.6

0.4

0.2

−0.2

−0.4

−0.6

−0.8
0 0.2 0.4 0.6 0.8 1

Figure 3.5 Piecewise cubic Hermite interpolant of e−2x sin(10πx), n = 8

Problems
P3.2.1 Write a function [a,b,c,d] = pwCstatic(f,fp,M4,alpha,beta,delta) analogous to pwLstatic. It should produce a
piecewise cubic Hermite approximation with uniform spacing. It should use the error result of Theorem 3 and the 4th derivative
bound M4 to determine the partition. The parameters f and fp should be handles that reference the function and its derivative
respectively.

P3.2.2 Write a recursive function


function [x,y,s] = pwCAdapt(f,fp,L,fL,DfL,R,fR,DfR,delta,hmin)
analogous to pwLadapt. Use the same interval acceptance tests as in pwLadapt. The parameters f and fp should be handles
that reference
√ the function and its derivative respectively. Use both pwLAdapt and pwCAdapt to produce approximations to
f (x) = x on the interval [.001, 9]. Fix hmin = .001. Print a table that shows the number of partition points computed by
pwLadapt and pwCadapt for delta = .1, .01, .001, .0001, and .00001.

P3.2.3 Complete the following function:


3.2. PIECEWISE CUBIC HERMITE INTERPOLATION 15

function [R,fR] = stretch(L,fL,tol);


% L,fL are scalars that satisfy fL = exp(-L) and tol is a positive real.
% R,fR are scalars that satisfy fR = exp(-R) with the property that if q(z) is the cubic
% hermite interpolant of exp(-z) at z=L and z=R, then |q(z) - exp(-z)| <= tol on [L,R].

Make effective use of the error bound in Theorem 3 when choosing R. Hint: How big can you make R and still guarantee the
required accuracy? Making effective use of stretch complete the following:

function [x,y] = pwCexp(a,b,tol)


% a,b are scalars that satisfy a < b and tol is a positive real.
% x,y are column n-vectors where a = x(1) < x(2) < ... < x(n) = b
% and y(i) = exp(-x(i)), i=1:n. The partition is chosen so that if C(z)
% is the piecewise cubic hermite interpolant of exp(-z) on this partition,
% then |C(z) - exp(-z)| <= tol for all z in [a,b]

P3.2.4 We want to interpolate a function f on [a, b] with error less than tol. When is it cheaper to set up a piecewise linear
interpolant L(z) with a uniform partition than a piecewise cubic hermite interpolant C(z) with a uniform partition? Your
answer should make use of the following facts and assumptions:
• If ` is the linear interpolant of f on an interval [α, β], then on that interval the error is no bigger than M2 (β − α)2 /8,
where M2 is an upper bound for |f (2) (z)|. Assume that M2 is known.
• If p is the cubic hermite interpolant of f on an interval [α, β], then on that interval the error is no bigger than M4 (β −
α)4/384, where M4 is an upper bound for |f (4) (z)|. Assume that M4 is known.
• A vectorized Matlab implementation of the function f is available and it requires σn seconds to execute when applied
to an n-vector. Assume that σ is known.
• A vectorized Matlab implementation of the function f 0 is available and it requires τ n seconds to execute when applied
to an n-vector. Assume that τ is known.

P3.2.5 Consider the quartic polynomial q(t) having the form


q(t) = a1 + a2 t + a3 t2 + a4 t2 (t − 1) + a5 t2 (t − 1)2 .
Given scalars v0 , s0 , v1 , s1 , vτ , and τ , our goal is to determine the ai so that

q(0) = v0 q 0 (0) = s0 q(1) = v1 q 0 (1) = s1 q(τ ) = vτ


We refer to this fourth degree Hermite interpolation problem as the “H4 problem” and to q as an “H4 interpolant.” Note that
its value is prescribed at three points and that at two of those points we also specify its slope. Complete the following function:

function A = H4(v0,s0,v1,s1,vtau,tau)
%
% Assume that the six inputs are length-n column vectors.
% A is an n-by-5 matrix with the property that if qi(t) is the quartic polynomial
%
% qi(t) = A(i,1) + A(i,2)t + A(i,3)t^2 + A(i,4)t^2(t-1) + A(i,5)t^2(t-1)^2
%
% then qi(0) = v0(i), qi’(0) = s0(i), qi(1) = v1(i), qi’(1) = s1(i), and qi(tau(i)) = vtau(i)
% for i=1:n.

Your implementation should not involve any loops. Also develop a vectorized implementation for evaluation:

function Y = H4Eval(A,tval)
% Assume that A is an n-by-5 matrix and that tval is a length-m row vector.
% For i=1:n, let qi(t) be the quartic polynomial
%
% qi(t) = A(i,1) + A(i,2)t + A(i,3)t^2 + A(i,4)t^2(1-t) + A(i,5)t^2(1-t)^2.
%
% Y is an n-by-m matrix with the property that Y(i,j) = qi(tval(j)) for
% i=1:n and j=1:m.

To test your implementations, write a script that plots in a single window the functions f (t), q1 (t), and q2 (t) where f (t) =
e−t sin(5t) and q1 and q2 are H4 interpolants that satisfy
q1 (0) = f (0) q10 (0) = f 0 (0) q1 (1) = f (1) q10 (1) = f 0 (1) q1 (.5) = f (.5)

q2 (1) = f (1) q20 (1) = f 0 (1) q2 (2) = f (2) q20 (2) = f 0 (2) q2 (1.5) = f (1.5).
There should be just a single call to H4 and H4Eval. In the same plot window, plot q1 across [0,1] and q2 across [1,2]. Print the
coefficients of the two interpolants.
16 CHAPTER 3. PIECEWISE POLYNOMIAL INTERPOLATION

3.3 Cubic Splines


In the piecewise cubic Hermite interpolation problem, we are given n triplets

(x1 , y1 , s1 ), . . . , (xn , yn , sn )

and determine a function C(x) that is piecewise cubic on the partition x1 < · · · < xn with the property that
C(xi ) = yi and C 0 (xi ) = si for i = 1:n. This interpolation strategy is subject to a number of criticisms:

• The function C(z) does not have a continuous second derivative: Its display may be too crude in
graphical applications, because the human eye can detect discontinuities in the second derivative.

• In other applications where C and its derivatives are part of a larger mathematical landscape, there
may be difficulties if C 00 (x) is discontinuous. For example, trouble arises if C is a distance function.

• In experimental settings where the yi are “instrument readings,” we may not have the first derivative
information required by the cubic Hermite process. Indeed, the underlying function f may not be
known explicitly.

These reservations prompt us to pose the cubic spline interpolation problem:

Given (x1 , y1 ), . . . , (xn , yn ) with α = x1 < · · · < xn = β, find a


piecewise cubic interpolant S(z) with the property that S, S 0 , and
S 00 are continuous.

The function S(z) that solves this problem is a cubic spline interpolant. This can be accomplished by
choosing the appropriate slope values s1 , . . . , sn .

3.3.1 Continuity at the Interior Breakpoints


Assume that S(z) is the cubic Hermite interpolant of the data (xi , yi , si ) for i = 1:n. We ask the following
question: Is it possible to choose s1 , . . . , sn so that the second derivative of S is continuous? Let us look at
what happens to S 00 at each of the “interior” breakpoints x2 , . . . , xn−1. To the left of xi+1 , S(z) is defined
by the local cubic

yi0 − si si + si+1 − 2yi0


qi (z) = yi + si (z − xi ) + (z − xi )2 + (z − xi )2 (z − xi+1 ),
∆xi (∆xi )2
where yi0 = (yi+1 − yi )/(xi+1 − xi ) and ∆xi = xi+1 − xi . The second derivative of this local cubic is given by

yi0 − si si + si+1 − 2yi0


qi00 (z) = 2 + [4(z − xi ) + 2(z − xi+1 )] . (3.1)
∆xi (∆xi )2

Likewise, to the right of xi+1 the piecewise cubic C(z) is defined by


0 0
yi+1 − si+1 si+1 + si+2 − 2yi+1
qi+1 (z) = yi+1 + si+1 (z − xi+1 ) + (z − xi+1 )2 + (z − xi+1 )2 (z − xi+2 ).
∆xi+1 (∆xi+1 )2

The second derivative of this local cubic is given by


0 0
00 yi+1 − si+1 si+1 + si+2 − 2yi+1
qi+1 (z) = 2 + [4(z − xi+1 ) + 2(z − xi+2 )] . (3.2)
∆xi+1 (∆xi+1 )2

To force second derivative continuity at xi+1 , we insist that


2
qi00 (xi+1 ) = (2si+1 + si − 3yi0 )
∆xi
3.3. CUBIC SPLINES 17

and
00 2 0
qi+1 (xi+1 ) = (3yi+1 − 2si+1 − si+2 )
∆xi+1
be equal. That is,

∆xi+1 si + 2 (∆xi + ∆xi+1 ) si+1 + ∆xi si+2 = 3 ∆xi+1 yi0 + ∆xi yi+1
0
(3.3)

for i = 1:n − 2. If we choose s1 , . . . , sn to satisfy these equations, then S 00 (z) is continuous.


Before we plunge into the resolution of these equations for general n, we acquire some intuition by
examining the n = 7 case. The equations designated by (3.3) are as follows:

i=1 ⇒ ∆x2 s1 + 2(∆x1 + ∆x2 )s2 + ∆x1 s3 = 3(∆x2 y10 + ∆x1 y20 )
i=2 ⇒ ∆x3 s2 + 2(∆x2 + ∆x3 )s3 + ∆x2 s4 = 3(∆x3 y20 + ∆x2 y30 )
i=3 ⇒ ∆x4 s3 + 2(∆x3 + ∆x4 )s4 + ∆x3 s5 = 3(∆x4 y30 + ∆x3 y40 )
i=4 ⇒ ∆x5 s4 + 2(∆x4 + ∆x5 )s5 + ∆x4 s6 = 3(∆x5 y40 + ∆x4 y50 )
i=5 ⇒ ∆x6 s5 + 2(∆x5 + ∆x6 )s6 + ∆x5 s7 = 3(∆x6 y50 + ∆x5 y60 ).

Notice that we have five constraints and seven parameters and therefore two “degrees of freedom.” If we
move two of the parameters (s1 and s7 ) to the right hand side and assemble the results in matrix-vector
form, then we obtain a 5-by-5 linear system
   
s2 3(∆x2 y10 + ∆x1 y20 ) − ∆x2 s1
   
 s   3(∆x3 y20 + ∆x2 y30 ) 
 3   
   
T s(2:6) = T  s4  =  3(∆x 4 y3
0
+ ∆x 3 y4
0
)  = r,
   
 s   0
3(∆x5 y4 + ∆x4 y5 ) 0 
 5   
0 0
s6 3(∆x6 y5 + ∆x5 y6 ) − ∆x5 s7

where
 
2(∆x1 + ∆x2 ) ∆x1 0 0 0
 
 ∆x3 2(∆x2 + ∆x3 ) ∆x2 0 0 
 
 
T =  0 ∆x4 2(∆x3 + ∆x4 ) ∆x3 0 .
 
 0 0 ∆x5 2(∆x4 + ∆x5 ) ∆x4 
 
0 0 0 ∆x6 2(∆x5 + ∆x6 )

Matrices like this that are zero everywhere except on the diagonal, subdiagonal, and superdiagonal are said
to be tridiagonal.
Different choices for the end slopes s1 and sn yield different cubic spline interpolants. Having defined
the end slopes, the interior slopes s(2:n − 1) ar determined by solving an (n − 2)-by-(n − 2) linear system.
In each case that we consider here, the matrix of coefficients looks like
 
t11 t12 0 ··· 0
 .. 
 ∆x3 2(∆x2 + ∆x3 ) ∆x2 . 
 
 
 
T =  .. ..
.
..
.
..
.
.. ,
 . . 
 0 ∆xn−2 2(∆xn−3 + ∆xn−2 ) ∆xn−3 
 
 
0 ··· 0 tn−2,n−3 tn−2,n−2
while the right-hand side r has the form
18 CHAPTER 3. PIECEWISE POLYNOMIAL INTERPOLATION

 
r1
 3(∆x3y20 + ∆x2 y30 ) 
 
 .. 
r= . .
 
 3(∆xn−2yn−3
0 0
+ ∆xn−3 yn−2 ) 
rn−2

As we show in the next subsection, the values of t11 , t12 , and r1 depend on how s1 is chosen. The values
of tn−2,n−3, tn−2,n−2, and rn−2 depend on how sn is defined. Moreover, the T matrices that emerge can be
shown to be nonsingular.
The following fragment summarizes what we have established so far about the linear system T s(2:n−1) =
r:
n=length(x);
Dx = diff(x);
yp = diff(y) ./ Dx;
T = zeros(n-2,n-2);
r = zeros(n-2,1);
for i=2:n-3
T(i,i) = 2(Dx(i)+Dx(i+1));
T(i,i-1) = Dx(i+1);
T(i,i+1) = Dx(i);
r(i) = 3(Dx(i+1)*yp(i) + Dx(i)*yp(i+1));
end

This sets up all but the first and last rows of T and all but the first and last components of r. How T and r
are completed depends on the end conditions that are imposed on the spline.

3.3.2 The Complete Spline


The complete spline is obtained by setting s1 = µL and sn = µR , where µL and µR are given real values.
With these constraints, setting i = 1 and i = n − 2 in (3.3) gives
∆x2 µL + 2(∆x1 + ∆x2 )s2 + ∆x1 s3 = 3(∆x2 y10 + ∆x1 y20 )
0 0
∆xn−1 sn−2 + 2(∆xn−2 + ∆xn−1)sn−1 + ∆xn−2µR = 3(∆xn−1yn−2 + ∆xn−2 yn−1 ),

and so the first and last equations are given by

2(∆x1 + ∆x2 )s2 + ∆x1s3 = 3(∆x2 y10 + ∆x1 y20 ) − ∆x2 µL


0 0
∆xn−1sn−2 + 2(∆xn−2 + ∆xn−1 )sn−1 = 3(∆xn−1yn−2 + ∆xn−2 yn−1 ) − ∆xn−2µR .

Thus, the setting up of T and r and the resolution of s are completed with the fragment
T(1,1) = 2*(Dx(1) + Dx(2));
T(1,2) = Dx(1);
r(1) = 3*(Dx(2)*yp(1) + Dx(1)*yp(2)) - Dx(2)*muL;
T(n-2,n-2) = 2*(Dx(n-2) + Dx(n-1));
T(n-2,n-3) = Dx(n-1);
r(n-2) = 3*(Dx(n-1)*yp(n-2) + Dx(n-2)*yp(n-1)) - Dx(n-2)*muR;
s = [ muL; T \ r(1:n-2) ; muR];
assuming that muL and muR house µL and µR , respectively.
3.3. CUBIC SPLINES 19

3.3.3 The Natural Spline


Instead of prescribing the slope of the spline at the endpoints, we can prescribe the value of its second
derivative. In particular, if we insist that µL = q100 (x1 ), then from (3.1) it follows that

y10 − s1 s1 + s2 − 2y10
µL = 2 −2 ,
∆x1 ∆x1
from which we conclude that
1 0 µL 
s1 = 3y1 − s2 − ∆x1 .
2 2
Substituting this result into the i = 1 case of (3.3) and rearranging, we obtain
µL
(2∆x1 + 1.5∆x2)s2 + ∆x1 s3 = 1.5∆x2y10 + 3∆x1 y20 + ∆x1 ∆x2 .
4
00
Likewise, by setting µR = qn−1 (xn ), then (3.2) implies
0 0
yn−1 − sn−1 sn−1 + sn − 2yn−1
µR = 2 +4 ,
∆xn−1 ∆xn−1
from which we conclude that
1 0 µR 
sn = 3yn−1 − sn−1 + ∆xn−1 .
2 2
Substituting this result into the i = n − 2 case of (3.3) and rearranging we obtain

0 0 µR
∆xn−1sn−2 + (1.5∆xn−2 + 2∆xn−1)sn−1 = 3∆xn−1yn−2 + 1.5∆xn−2yn−1 − ∆xn−2∆xn−1 .
4
Thus, the setting up of T and r and the resolution of s are completed with the fragment

T(1,1) = 2*Dx(1) + 1.5*Dx(2);


T(1,2) = Dx(1);
r(1) = 1.5*Dx(2)*yp(1) + 3*Dx(1)*yp(2)) + Dx(1)*Dx(2)*muL/4;
T(n-2,n-2) = 1.5*Dx(n-2) + 2*Dx(n-1);
T(n-2,n-3) = Dx(n-1);
r(n-2) = 3*Dx(n-1)*yp(n-2) + 1.5*Dx(n-2)*yp(n-1) -Dx(n-2)*Dx(n-1)*muR;
stilde = T \ r;
s1 = (3*yp(1) - stilde(1) - muL*Dx(1)/2)/2;
sn = (3*yp(n-1) - stilde(n-2) + muR*Dx(n-1)/2)/2;
s = [s1; stilde; sn];

If µL = µR = 0, then the resulting spline is called the natural spline.

3.3.4 The Not-a-Knot Spline


This method for prescribing the end conditions is appropriate if no endpoint derivative information is avail-
able. It produces the not-a-knot spline. The idea is to ensure third derivative continuity at both x2 and
xn−1 . Note from (3.1) that

si + si+1 − 2yi0
qi000 (x) = 6 ,
(∆xi )2
and so q1000(x2 ) = q2000 (x2 ) says that

s1 + s2 − 2y10 s2 + s3 − 2y20
2
= .
(∆x1 ) (∆x2 )2
20 CHAPTER 3. PIECEWISE POLYNOMIAL INTERPOLATION

It follows that this will be the case if we set


 2
∆x1
s1 = −s2 + 2y10 + , (s2 + s3 − 2y20 ).
∆x2

As a result of making the third derivative continuous at x2 , the cubics q1 (x) and q2 (x) are identical.
000 000
Likewise, qn−2 (xn−1 ) = qn−1 (xn−1 ) says that
0 0
sn−2 + sn−1 − 2yn−2 sn−1 + sn − 2yn−1
= .
(∆xn−2)2 (∆xn−1)2

It follows that this will be the case if we set


 2
0 ∆xn−1 0
sn = −sn−1 + 2yn−1 + (sn−2 + sn−1 − 2yn−2 ).
∆xn−2

Thus, the first and last equations for the not-a-knot spline are set up as follows:

q = Dx(1)*Dx(1)/Dx(2);
T(1,1)= 2*Dx(1) +Dx(2) + q;
T(1,2) = Dx(1) + q;
r(1) = Dx(2)*yp(1) + Dx(1)*yp(2)+2*yp(2)*(q+Dx(1));
q= Dx(n-1)*Dx(n-1)/Dx(n-2);
T(n-2,n-2) = 2*Dx(n-1) + Dx(n-2)+q;
T(n-2,n-3) = Dx(n-1)+q;
r(n-2) = Dx(n-1)*yp(n-2) + Dx(n-2)*yp(n-1) +2*yp(n-2)*(Dx(n-1)+q);
stilde = T\ r;
s1 = -stilde(1)+2*yp(1);
s1 = s1 + ((Dx(1)/Dx(2))b2)*(stilde(1)+stilde(2)-2*yp(2));
sn = -stilde(n-2) +2*yp(n-1);
sn=sn+((Dx(n-1)/Dx(n-2))b2)*(stilde(n-3)+stilde(n-2)-2*yp(n-2));
s=[s1;stilde;sn];

3.3.5 The Cubic Spline Interpolant


The function CubicSpline can be used to construct the cubic spline interpolant with any of the three
aforementioned types of end conditions. Here is its specification:

function [a,b,c,d] = CubicSpline(x,y,derivative,muL,muR)


% [a,b,c,d] = CubicSpline(x,y,derivative,muL,muR)
% Cubic spline interpolation with prescribed end conditions.
%
% x,y are column n-vectors. It is assumed that n >= 4 and x(1) < ... x(n).
% derivative is an integer (1 or 2) that specifies the order of the endpoint derivatives.
% muL and muR are the endpoint values of this derivative.
%
% a,b,c, and d are column (n-1)-vectors that define the spline S(z). On [x(i),x(i+1)],
%
% S(z) = a(i) + b(i)(z-x(i)) + c(i)(z-x(i))^2 + d(i)(z-x(i))^2(z-x(i+1).
%
% Usage:
% [a,b,c,d] = CubicSpline(x,y,1,muL,muR) S’(x(1)) = muL, S’(x(n)) = muR
% [a,b,c,d] = CubicSpline(x,y,2,muL,muR) S’’(x(1)) = muL, S’’(x(n)) = muR
% [a,b,c,d] = CubicSpline(x,y) S’’’(z) continuous at x(2) and x(n-1)
%
3.3. CUBIC SPLINES 21

Notice that a two-argument call is all that is required to produce the not-a-knot spline. The script
ShowSpline examines various CubicSpline interpolants to the sine function.
Error bounds for the cubic spline interpolant are complicated to derive. The bounds are not good if the
end conditions are improperly chosen. Figure 3.6 shows what can happen if the complete spline is used with
end conditions that are at variance with the behavior of the function being interpolated. However, if the
’Bad’ Complete spline interpolant of sin(x) with 6 subintervals
1

0.8

0.6

0.4

0.2

−0.2

−0.4

−0.6

−0.8

−1
0 2 4 6 8 10 12 14

Figure 3.6 Bad end conditions

end values are properly chosen or if the not-a-knot approach is used, then the error bound has the form
M4 h̄4 where h̄ is the maximum subinterval length and M4 bounds the 4th derivative of the function being
interpolated. The script ShowSplineErr confirms this for the case of an “easy” f(x). It produces the plots
shown in Figure 3.7. Notice that the error is reduced by a factor of 104 if the subinterval length is reduced

−3 −3
10 10

−4 −4
10 10

−5 −5
10 10

−6 −6
10 10

−7 −7
10 10

−8 −8
10 10

−9 −9
10 10

−10 −10
10 10

−11 −11
10 10

−12 −12
10 10
0 0.2 0.4 0.6 0.8 1 0 0.2 0.4 0.6 0.8 1
Knot Spacing = 0.050 Knot Spacing = 0.005

Figure 3.7 Not-a-knot spline error

by a factor of ten.

3.3.6 Matlab Spline Tools


The Matlab function spline can be used to compute not-a-knot spline interpolants. It can be called with
either two or three arguments. The script
22 CHAPTER 3. PIECEWISE POLYNOMIAL INTERPOLATION

z = linspace(-5,5);
x = linspace(-5,5,9);
y = atan(x);
Svals = spline(x,y,z);
plot(z,Svals);
illustrates a three-argument call. It plots the n = 9 not-a-knot spline interpolant of the function f(x) =
arctan(x) across the interval [−5, 5]. The first two arguments in the call to spline specify the interpolation
points that define the spline S. The spline is then evaluated at z with the values returned in Svals. Thus
S(x(i)) = y(i) for i=1:length(x) and S(z(i)) = Svals(i) for i=1:length(z).
A 2-argument call to spline returns what is called the pp-representation of the spline. This type of
reference is required whenever one has to manipulate the local cubics that make up the spline. The pp-
representation of a spline is different from the four-vector representation that we have been using for piecewise
cubics. For one thing, it is more general because it can accommodate piecewise polynomials of arbitrary
degree.
To gain a facility with Matlab’s piecewise polynomial tools, let’s consider the problem of constructing
the pp-representation of the derivative of a cubic spline S. In particular, let’s plot S 0 where S is a nine-point,
equally spaced, not-a-knot spline interpolant of the arctangent function across the interval [−5, 5]. We start
by constructing the pp-representation of S:
x = linspace(-5,5,9);
y = atan(x);
S = spline(x,y)
A two-argument call to spline such as this produces the pp-representation of the spline. The ppval function
can be used to evaluate a piecewise polynomial in this representation:
z = linspace(-5,5);
Svals = ppval(S,z);
plot(z,Svals)
The call to ppval returns the value of the spline at z. The vector Svals contains the values of the spline on
z. These values are then plotted.
The derivative of the spline is a piecewise quadratic polynomial, and by using the functions unmkpp and
mkpp we can produce its pp-representation. A call to unmkpp unveils the four major components of the
pp-representation:
[x,rho,L,k] = unmkpp(S)
The x-values are returned in x. The coefficients of the local polynomials are assembled in an L-by-k matrix
rho. L is the number of local polynomials and k-1 is their degree. So in our case, x = linspace(-5,5,9), L
= 8, and k=4. The coefficients of the i-th local cubic are stored in ith row of the rho matrix. In particular,
the spline is defined by
S(z) = ρi,4 + ρi,3 (z − xi ) + ρi,2 (z − xi )2 + ρi,1 (z − xi )3
on the interval [xi , xi+1 ]. Thus, rho(i,j) is the ith local polynomial coefficient of (x − xi )k−j+1 .
The function mkpp takes the breakpoints and the array of coefficients and produces the pp-representation
of the piecewise polynomial so defined. Thus, to set up the pp-representation of the spline’s derivative, we
execute
drho = [3*rho(:,1) 2*rho(:,2) rho(:,3)];
dS = mkpp(x,drho);
The set-up of the three-column matrix drho follows from the observation that
S 0 (x) = ρi,3 + 2ρi,2 (x − xi ) + 3ρi,1 (x − xi )2
on the interval [xi , xi+1 ]. Putting it all together, we obtain
3.3. CUBIC SPLINES 23

% Script File: ShowSplineTools


% Illustrates the Matlab functions spline, ppval, mkpp, unmkpp
close all
% Set Up Data:
n = 9;
x = linspace(-5,5,n);
y = atan(x);
% Compute the spline interpolant and its derivative:
S = spline(x,y);
[x,rho,L,k] = unmkpp(S);
drho = [3*rho(:,1) 2*rho(:,2) rho(:,3)];
dS = mkpp(x,drho);
% Evaluate S and dS:
z = linspace(-5,5);
Svals = ppval(S,z);
dSvals = ppval(dS,z);

% Plot:
atanvals = atan(z);
figure
plot(z,atanvals,z,Svals,x,y,’*’);
title(sprintf(’n = %2.0f Spline Interpolant of atan(x)’,n))
datanvals = ones(size(z))./(1 + z.*z);
figure
plot(z,datanvals,z,dSvals)
title(sprintf(’Derivative of n = %2.0f Spline Interpolant of atan(x)’,n))

Problems

P3.3.1 What can you say about the n = 4 not-a-knot spline interpolant of f (x) = x3 ?

P3.3.2 Suppose S(z) is the not-a-knot spline interpolant of (x1 , y1 ), (x2 , y2 ), (x3 , y3 ), and (x4 , y4 ) where it is assumed that
the xi are distinct. Suppose p(x) is the cubic interpolant at same four points. Explain why S(z) = p(z) for all z.

P3.3.3 Let S(z) be the natural spline interpolant of z3 at z = −3, z = −1, z = 1, z = 3. What is S(0)?

P3.3.4 Given σ > 0, (xi , yi , si ), and (xi+1 , yi+1 , si+1 ), show how to determine ai , bi , ci , and di so that

gi (x) = ai + bi (x − xi ) + ci eσ(x−xi ) + di e−σ(x−xi )


satisfies gi (xi ) = yi , gi0 (xi) = si , gi (xi+1 ) = yi+1 , and gi0 (xi+1 ) = si+1 .

P3.3.5 Another approach that can be used to make up for a lack of endpoint derivative information is to glean that information
from a four-point cubic interpolant. For example, if qL (x) is the cubic interpolant of (x1 , y1 ), (x2 , y2 ), (x3 , y3 ), and (x4 , y4 ),
then either of the endpoint conditions

q10 (x1 ) = 0
qL (x1 )
q100 (x1 ) = 00
qL (x1 )
is reasonable, where q1 (x) is the leftmost local cubic. Likewise, if qR (x) is the cubic interpolant of (xn−3 , yn−3 ), (xn−2 , yn−2 ),
(xn−1 , yn−1 ), and (xn, yn ), then either of the right endpoint conditions

0 0
qn−1 (xn ) = qR (xn )
00 00
qn−1 (xn ) = qR (xn )
is reasonable, where qn−1 (x) is the rightmost local cubic.
Modify CubicSpline so that it invokes this strategy whenever the function call involves just three arguments, (i.e., [a,b,c,d]
= CubicSpline(x,y,derivative.) The value of derivative should determine which derivative is to be matched at the endpoints.
24 CHAPTER 3. PIECEWISE POLYNOMIAL INTERPOLATION

(Its value should be 1 or 2.) Augment the script file ShowSpline so that it graphically depicts the splines that are produced by
this method.

P3.3.6 Explain how Matlab’s spline tools can be used to compute


Z β
[S 00 (x)]2dx,
α

where S(x) is a cubic spline.

P3.3.7 Suppose S(x) is a cubic spline interpolant of the data (x1 , y1 ), . . . , (xn , yn ) obtained using spline. Write a Matlab function
d3 = MaxJump(S) that returns the maximum jump in the third derivative of the spline S assumed to be in the pp-representation.
Vectorize as much as possible. Use the max function.

P3.3.8 Write a Matlab function S = Convert(a,b,c,d,x) that takes our piecewise cubic interpolant representation and con-
verts it into pp form.

P3.3.9 Complete the following function:

function [a,b,c,d] = SmallSpline(z,y)


% z is a scalar and y is 3-vector.
% a,b,c,d are column 2-vectors with the property that if
%
% S(x) = a(1) + b(1)(x - z) + c(1)(x - z)^2 + d(1)(x - z)^3 on [z-1,z]
% and
% S(x) = a(2) + b(2)(x - z) + c(2)(x - z)^2 + d(2)(x - z)^3 on [z,z+1]
% then
% (a) S(z-1) = y(1), S(z) = y(2), S(z+1) = y(3),
% (b) S’’(z-1) = S’’(z+1) = 0
% (c) S, S’, and S’’ are continuous on [z-1,z+1]
%

P3.3.10 In computerized typography the problem arises of finding an interpolant to points that lie on a path in the plane (e.g.,
a printed capital S). Such a shape cannot be represented as a function of x because it is not single valued. One approach is
to number the points (x1, y1 ), . . . , (xn , yn ) as we traverse the curve. Let di be the straight-line distance between (xi , yi ) and
(xi+1 , yi+1 ), i = 1:n − 1. Set ti = d1 + · · · + di−1 , i = 1:n. Suppose Sx (t) is a spline interpolant of (t1 , x1 ), . . . , (tn, xn ) and
that Sy (t) is a spline interpolant of (t1 , y1 ), . . . , (tn , yn ).
It follows that the curve Λ = {(Sx (t), Sy (t)) : t1 ≤ t ≤ tn } is smooth and passes through the n points. Write a
Matlab function [xi,yi] = SplineInPlane(x,y,m) that returns in xi(1:m) and yi(1:m) the x-y coordinates of m points
on the curve Λ. Use the Matlab Spline function to determine the splines Sx (t) and Sy (t).
To test SplineInPlane write a script that solicits an arbitrary number of points from the plot window using ginput. It
should echo your mouseclicks by placing an asterisk at each point. After all the points are acquired it should compute the
splines Sx and Sy defined above and then plot the curve Λ. Use hold on so that the asterisks are also displayed.
Submit listings and sample output showing a personally designed letter “S”. The number of input points used is up to you.

P3.3.11 Let S(x) be the not-a-knot cubic spline interpolant of (0,0), (1,1), (2,8), (3,27). Explain why S(3/2) = (3/2)3.

P3.3.12 Suppose x and y are column n-vectors with x1 < x2 < · · · < xn . If z is a column m-vector, then sval = spline(x,y,z)
is a column m-vector with the property that sval(i) = S(zi ), where S the not-a-knot spline interpolant of (x1 , y1 ), . . . , (xn , yn ).
Let
S1 (x) be the not-a-knot spline interpolant of sin(x) at xi = (i − 1)/10, i = 1:21
S2 (x) be the not-a-knot spline interpolant of exp(x) at xi = (i − 1)/10, i = 1:21
S3 (x) be the not-a-knot spline interpolant of sin(x) · exp(x) at xi = (i − 1)/10, i = 1:21
S4 (x) be the not-a-knot spline interpolant of 2 sin(x) + 3 exp(x) at xi = (i − 1)/10, i = 1:21
Write a vectorized Matlab script that plots in a single window these four splines across the interval [0,2]. The plots
should be based on one-hundred, equally-spaced evaluations. Avoid unnecessary function calls. You do not have to exploit any
trigonometric or exponential identities.

P3.3.13 Produce a plot that shows that it is a bad idea to interpolate with the natural spline if the second derivative of the
underlying function is not zero at the endpoints.

P3.3.14 Suppose f (t) and its first two derivatives are defined everywhere. If f has period T (positive), then f (t + T ) = f (t)
for all t. Consider the problem of interpolating such a function on an interval [τ, τ + T ] with a spline S having breakpoints

τ = t1 < · · · < tn = τ + T .
M-FILES AND REFERENCES 25

It makes sense to require


S 0 (τ ) = S 0 (τ + T )
00
S (τ ) = S 00 (τ + T ),
since f 0 (τ ) = f 0 (τ + T ) and f 00 (τ ) = f 00 (τ + T ). Moreover, we can then extend S periodically off the “base” interval [τ, τ + T ]
and obtain a piecewise cubic interpolant that is continuous through the second derivative. (a) Modify CubicSpline so that a
3-argument call of the form

[a,b,c,d] = CubicSpline(x,y,0)

produces the periodic spline interpolant. In other words,


S(xi ) = yi
S 0 (x1 ) = S 0 (xn )
00
S (x1 ) = S 00 (xn )
where i = 1:n and n is the length of x. Test your adaptation with the function
f (x) = sin(2πx) − .3 · cos(4πx) + .6 · sin(6πx) + .2 · cos(8πx)
by generating its periodic spline interpolant on linspace(0,1,15). Print a table of the coefficients a(1:14), b(1:14), c(1:14), and
d(1:14) and plot both f and the spline across [0, 1]. (b) A not-a-knot spline interpolant of f across [τ, τ + T ] will in general
not be periodic. However, we can make it “almost” periodic by choosing t2 = t1 + δ and tn−1 = tn − δ for small δ. Write a
function

function s = Periodic(f,t1,T,n,del)
% f is a handle that references an available function f(t) that has period T and is defined
% everywhere. t1 is a real scalar, n is an integer >= 4, and del a positive scalar that
% satisfies del < T/2.
%
% s is the pp-form of the not-a-knot spline that interpolates f at t(1),...,t(n) where
%
% t1 if k=1
% t1 + del if k=2
% t(k) = t1 + del + (k-2)*(T-2*del)/(n-3) if k=3:n-2
% t1 + T-del if k=n-1
% t1 + T if k=n
%
% A four-parameter reference of the form s = Periodic(f,t1,T,n) should
% return the not-a-knot spline interpolant of f at linspace(t1,t1+T,n).

M-Files and References

Script Files
ShowpwL1 Illustrates pwL and pwLeval.
ShowpwL2 Compares pwLstatic and pwLadapt.
ShowHermite Illustrates the Hermite interpolation idea.
ShowpwC Illustrates pwC and pwCeval.
ShowSpline Illustrates CubicSpline.
ShowSplineErr Explores the not-a-knot spline interpolant error.
ShowMatSplineTools Illustrates Matlab spline tools.
26 CHAPTER 3. PIECEWISE POLYNOMIAL INTERPOLATION

Function Files

Locate Determines the subinterval in a mesh that houses a given x-value


pwL Sets up a piecewise linear interpolant.
pwLeval Evaluates a piecewise linear function.
pwLstatic A priori determination of a mesh for a pwL approximation.
pwLadapt Dynamic determination of a mesh for a pwL approximation.
HCubic Constructs the cubic Hermite interpolant.
pwC Sets up a piecewise cubic Hermite interpolant.
pwCeval Evaluates a piecewise cubic function.
CubicSpline Constructs complete, natural, or not-a-knot spline.

References

R. Bartels, J. Beatty, and B. Barsky (1987). An Introduction to Splines for Use in Computer Graphics and
Geometric Modeling, Morgan Kaufmann, Los Altos, CA.
C. de Boor (1978). A Practical Guide to Splines, Springer, Berlin.
Chapter 4

Numerical Integration

§1 The Newton-Cotes Rules


§2 Composite Rules
§3 Adaptive Quadrature
§4 Gauss Quadrature and Spline Quadrature
§5 Matlab’s Quadrature Tools

An m-point quadrature rule Q for the definite integral


Z b
I(f, a, b) = f(x)dx (4.1)
a

is an approximation of the form


m
X
IQ (f, a, b) = (b − a) wk f(xk ). (4.2)
k=1

The xk are the abscissas and the wk are the weights. The abscissas and weights define the rule and are chosen
so that IQ (f, a, b) ≈ I(f, a, b). Efficiency essentially depends upon the number of function evaluations. This
is because the time needed to evaluate f at the xi is typically much greater than the time needed to form
the required linear combination of function values. Thus, a six-point quadrature rule is twice as expensive
as a three-point rule.
We start by presenting the the Newton-Cotes family of quadrature rules. These rules are derived by
integrating a polynomial interpolant of the integrand f(x). Composite rules based on a partition of [a, b]
into subintervals are then discussed in §4.2. In a composite rule, a simple rule is applied to each subintegral
and the result summed. The adaptive determination of the partition with error control is presented in §4.3.
The partition is determined recursively using heuristic estimates of the integrand’s behavior. In §4.4 we
discuss the “super accuracte” Gauss quadrature idea and also how to approach the quadrature problem
using splines when the integrand is only known through a discrete set of sample points.

4.1 The Newton-Cotes Rules


One way to derive a quadrature rule Q is to integrate a polynomial approximation p(x) of the integrand
f(x). The philosophy is that p(x) ≈ f(x) implies

1
2 CHAPTER 4. NUMERICAL INTEGRATION

Z b Z b
f(x)dx ≈ p(x)dx.
a a
(See Figure 4.1.) The Newton-Cotes quadrature rules are obtained by integrating uniformly spaced polyno-
mial interpolants of the integrand. The m-point Newton-Cotes rule (m ≥ 2) is defined by
Z b
QNC(m) = pm−1 (x)dx, (4.3)
a

f(x) The Interpolant


1 1

0.5 0.5

0 0

−0.5 −0.5

−1 −1
0 1 2 3 0 1 2 3
m=4
Integral of f(x) Integral of Interpolant
1 1

0.5 0.5

0 0

−0.5 −0.5

−1 −1
0 1 2 3 0 1 2 3
Error = 9.324e−002

Figure 4.1 The Newton-Cotes idea

where pm−1 (x) interpolates f(x) at


i−1
xi = a + (b − a), i = 1:m.
m−1
If m = 2, then we obtain the trapezoidal rule:
Z b 
f(b) − f(a)
QNC(2) = f(a) + (x − a) dx
a b−a
 
1 1
= (b − a) f(a) + f(b) .
2 2
If m = 3 and c = (a + b)/2, then we obtain the Simpson rule:
 
Z b f(b) − f(c) f(c) − f(a)
 f(c) − f(a) − c−a 
QNC(3) = b−c
f(a) + (x − a) + (x − a)(x − c)dx
a c − a b − a
   
b−a a+b
= f(a) + 4f + f(b) .
6 2
From these low-degree examples, it appears that a linear combination of f-evaluations is obtained upon
expansion of the right-hand side in (4.3).
4.1. THE NEWTON-COTES RULES 3

4.1.1 Derivation
For general m, we proceed by substituting the Newton representation
m k−1
!
X Y
pm−1 (x) = ck (x − xi )
k=1 i=1

into (4.3):
Z m Z k−1
!
b X b Y
QNC(m) = pm−1 (x)dx = ck (x − xi ) dx.
a k=1 a i=1

If we set x = a + sh, where h = (b − a)/(m − 1), then this transforms to


Z b Z m−1 m
X
QNC(m) = pm−1 (x)dx = h pm−1 (a + sh)ds = ck hk Smk ,
a 0 k=1

where !
Z m−1 k−1
Y
Smk = (s − i + 1) ds.
0 i=1

The ck are divided differences. Because of the equal spacing, they have a particularly simple form in terms
of the fi , as was shown in §2.4.1. For example,

c1 = f1
c2 = (f2 − f1 )/h
c3 = (f3 − 2f2 + f1 )/(2h2 )
c4 = (f4 − 3f3 + 3f2 − f1 )/(3!h3 ).

Recipes for the Smk can also be derived. Here are a few examples:
R m−1
Sm1 = 0
1 · ds = (m − 1)
R m−1
Sm2 = 0 sds = (m − 1)2 /2
R m−1
Sm3 = 0
s(s − 1)ds = (m − 1)2 (m − 5/2)/3
R m−1
Sm4 = 0
s(s − 1)(s − 2)ds = (m − 1)2 (m − 3)2 /4

Using these tabulations we can readily derive the weights for any particular m-point rule. For example, if
m = 4, then
S41 = 3 S42 = 9/2 S43 = 9/2 S44 = 9/4.
Thus,

QNC(4) = S41 c1 h + S42 c2 h2 + S43 c3 h3 + S44 c4 h4


9 f2 − f1 2 9 f3 − 2f2 + f1 3 9 f4 − 3f3 + 3f2 − f1 4
= 3f1 h + h + h + h
2 h 2 2h2 4 6h3
3h
= (f1 + 3f2 + 3f3 + f4 )
8

= (b − a)(f1 + 3f2 + 3f3 + f4 )/8

showing that [1 3 3 1]/8 is the weight vector for QNC(4) .


4 CHAPTER 4. NUMERICAL INTEGRATION

4.1.2 Implementation
For convenience in subsequent computations, we “package” the Newton-Cotes weight vectors in the following
function:

function w = NCWeights(m)
% w = NCWeights(m)
%
% w is a column m-vector consisting of the weights for the m-point Newton-Cotes rule.
% m is an integer that satisfies 2 <= m <= 11.

if m==2
w=[1 1]’/2;
elseif m==3
w=[1 4 1]’/6;
elseif m==4
w=[1 3 3 1]’/8;
elseif m==5
w=[7 32 12 32 7]’/90;
:
end

Notice that the weight vectors are symmetric about their middle in that w(1:m) = w(m: − 1:1).
Turning now to the evaluation of QNC(m) itself, we see from
 
m f(x1 )
X   .. 
QNC(m) = (b − a) wi fi = (b − a) w1 ··· wm  . 
i=1 f(xm )

that it is a scaled inner product of the weight vector w and the vector of function values. Therefore, we
obtain

function numI = QNC(f,a,b,m)


% m-point Newton-Cotes quadrature across the interval [a b].
% f is a handle that points to a function of the form f(x) where x is a
% scalar. f must be defined on [a,b] and it must return a column vector if
% x is a column vector.
% m is an integer that satisfies 2 <= m <= 11.
% numI is the m-point Newton-Cotes approximation of the integral of f from
% a to b.
w = NCweights(m);
x = linspace(a,b,m)’;
fvals = f(x);
numI = (b-a)*(w’*fvals);

We mention that QNC(2) and QNC(3) are referred to as the trapezoidal rule and Simpson’s rule respectively.
Let us see how well QNC does when it is applied to the problems
Z 1
I1 = e−x dx = 1 − e−1
0

and

Z 1
I2 = e−20x dx = (1 − e−20 )/20
0

Setting Q1 = QNC(@(x) exp(-x),0,1,m) and Q2 = QNC(@(x) exp(-20*x),0,1,m) we find


4.1. THE NEWTON-COTES RULES 5

m |Q1 - I1| |Q2 - I2|


-----------------------------------------------
2 0.0518191617571635 0.4500000011336345
3 0.0002131211751050 0.1166969337330916
4 0.0000950324202655 0.0754778453850014
5 0.0000003161797660 0.0301796546189490
6 0.0000001782491539 0.0208012561376684
7 0.0000000003894651 0.0080385105198381
8 0.0000000002389524 0.0056365811921616
9 0.0000000000003593 0.0019118765020265
10 0.0000000000002303 0.0013508599157407
11 0.0000000000000003 0.0003884845483225

We need a theory that explains why the results for I2 are so inferior!

4.1.3 Newton-Cotes Error


How good are the Newton-Cotes rules? Since they are based on the integration of a polynomial interpolant,
the answer clearly depends on the quality of the interpolant. Here is a result for Simpson’s rule:

Theorem 4 If f(x) and its first four derivatives are continuous on [a, b], then
Z
b (b − a)5

f(x)dx − QNC(3) ≤ M4 ,
a 2880

where M4 is an upper bound on |f (4) (x)| on [a, b].

Proof Suppose

p(x) = c1 + c2 (x − a) + c3 (x − a)(x − b) + c4 (x − a)(x − b)(x − c)

is the Newton form of the cubic interpolant to f(x) at the points a, b, c, and d. If c is the midpoint of the
interval [a, b], then
Z b
(c1 + c2 (x − a) + c3 (x − a)(x − b)) dx = QNC(3) ,
a
because the first three terms in the expression for p(x) specify the quadratic interpolant of (a, f(a)), (c, f(c)),
and (b, f(b)), on which the three-point Newton-Cotes rule is based. By symmetry we have
Z b
(x − a)(x − b)(x − c)dx = 0
a

and so Z b
p(x)dx = QNC(3) .
a
The error in p(x) is given by Theorem 2,

f (4) (ηx)
f(x) − p(x) = (x − a)(x − b)(x − c)(x − d)
24
and thus,
Z b Z b 
f (4) (ηx )
f(x)dx − QNC(3) = (x − a)(x − b)(x − c)(x − d) dx.
a a 24
Taking absolute values, we obtain
Z
b M Z b
4
f(x)dx − Q NC(3) ≤ |(x − a)(x − b)(x − c)(x − d)| dx.
a 24 a
6 CHAPTER 4. NUMERICAL INTEGRATION

If we set d = c, then (x − a)(x − b)(x − c)(x − d) is always negative and it is easy to verify that
Z b
(b − a)5
|(x − a)(x − b)(x − c)(x − d)| dx =
a 120
and so Z
b M (b − a)5 M4
4
f(x)dx − QNC(3) ≤ = (b − a)5 . 
a 24 120 2880

Note that if f(x) is a cubic polynomial, then f (4) = 0 and so Simpson’s rule is exact. This is somewhat
surprising because the rule is based on the integration of a quadratic interpolant.
In general, it can be shown that
Z b  d+2
b−a
f(x)dx = QNC(m) + cm f (d+1) (η) , (4.4)
a m−1

where cm is a small constant, η is in the interval [a, b], and


(
m − 1 if m is even
d = .
m if m is odd

Notice that if m is odd, as in Simpson’s rule, then an extra degree of accuracy results. See P4.1.3 for details.
From (4.4), we see that knowledge of f (d+1) is required in order to say something about the error in
QNC(m) . For example, if |f (d+1) (x)| ≤ Md+1 on [a, b], then
Z b  d+2
b−a

QNC(m) − f(x)dx ≤ |cm |Md+1 . (4.5)
a m−1

The following function can be used to return this upper bound given the interval [a, b], m, and the appropriate
derivative bound:

function error = QNCError(a,b,m,M)


% The error bound for the m-point Newton-Cotes rule when applied to
% the integral from a to b of a function f(x). It is assumed that
% a<=b and 2<=m<=11. M is an upper bound for the (d+1)-st derivative of the
% function f(x) on [a,b] where d = m if m is odd, and m-1 if m is even.
if m==2, d=1; c = -1/12;
elseif m==3, d=3; c = -1/90;
elseif m==4, d=3; c = -3/80;
elseif m==5, d=5; c = -8/945;
elseif m==6, d=5; c = -275/12096;
elseif m==7, d=7; c = -9/1400;
elseif m==8, d=7; c = -8183/518400;
elseif m==9, d=9; c = -2368/467775;
elseif m==10, d=9; c = -173/14620;
else d=11; c = -1346350/326918592;
end
error = abs( c*M*((b-a)/(m-1))^(d+2));

From this we see that if you are contemplating an even m rule, then the (m−1)-point rule is probably just as
good and requires one less function evaluation. The following table summarizes the error when the m-point
Newton-Cotes rule is applied to
Z π/2
I = sin(x)dx.
0
4.2. COMPOSITE RULES 7

m QNC(@sin,0,pi/2,m) Actual Error Error Bound


2 0.7853981633974483 2.146e-01 3.230e-01
3 1.0022798774922104 2.280e-03 3.321e-03
4 1.0010049233142790 1.005e-03 1.476e-03
5 0.9999915654729927 8.435e-06 1.219e-05
6 0.9999952613861667 4.739e-06 6.867e-06
7 1.0000000258372355 2.584e-08 3.714e-08
8 1.0000000158229039 1.582e-08 2.277e-08
9 0.9999999999408976 5.910e-11 8.466e-11
10 0.9999999999621675 3.783e-11 5.417e-11
11 1.0000000000001021 1.021e-13 1.460e-13

Problems

P4.1.1 Let C(x) be the cubic Hermite interpolant of f (x) at x = a and b. Show that

b h h2 0
Z
C(x)dx = (f (a) + f (b)) + (f (a) − f 0(b)).
a 2 12

This is sometimes called the corrected trapezoidal rule. Write a function CorrTrap(f,fp,a,b) that computes this value. Here,
f and fp are handles that reference the integrand and its derivative respectively. The error in this rule has the form ch4 f (4) (η).
Determine c (approximately) through experimentation.

P4.1.2 This problem is about the computation of the closed Newton-Cotes weights by solving an appropriate linear system.
Observe that the m-point rule should compute the integral
Z 1 1
xi−1 dx =
0 i
exactly for i = 1:m. For this calculation, the abscissas are given by xj = (j − 1)/(m − 1), i = 1:m. Thus the weights w1 , . . . , wm
satisfy
1
w1 xi−1
1 + w2 xi−1
2 + · · · + wm xi−1
m =
i
for i = 1:m. This defines a linear system whose solution is the weight vector for the m-point rule. Write a function
MyNCweights(m) that computes the weights by setting up the preceding linear system and solving for w using the backslash
operation. Compare the output of NCweights and MyNCweights for m = 2:11.

P4.1.3 (a) Suppose m is odd and that c = (a + b)/2. Show that QNC(m) is exact if applied to
Z b
I= (x − c)k dx
a

when k is odd. (b) If p(x) has degree m, then it can be written in the form p(x) = q(x) + α(x − c)m where q has degree m − 1
and α is a scalar. Use this fact with c = (a + b)/2 to show that if m is odd, then QNC(m) is exact when applied to
Z b
I= p(x)dx.
a

P4.1.4 Augment ShowQNCError so that it also prints a table of errors and error bounds for the integral
Z 1
dx
I= .
0 1 + 10x

Explain clearly the derivative bounds that are used.

4.2 Composite Rules


We will not be happy with the error bound (4.5) unless b − a is sufficiently small. Fortunately, there is an
easy way to organize the computation of an integral so that small-interval quadratures prevail.
8 CHAPTER 4. NUMERICAL INTEGRATION

x=a x=b
d t t t d t t t d t t t d t t t d
| {z }| {z }| {z }| {z }
∆ ∆ ∆ ∆

(4)
Figure 4.2 Function evaluations in QNC(5)

4.2.1 Derivation
If we have a partition
a = z1 < z2 < · · · < zn+1 = b,
then Z b n Z
X zi+1
f(x)dx = f(x)dx.
a i=1 zi

If we apply QNC(m) to each of the subintegrals, then a composite quadrature rule based on QNC(m) results.
For example, if ∆i = zi+1 − zi and zi+1/2 = (zi + zi+1 )/2, i = 1:n, then
n
X ∆i 
Q= f(zi ) + 4f(zi+1/2 ) + f(zi+1 ) (4.6)
i=1
6

is a composite Simpson rule. In general, if z houses a partition of [a, b] and f is a handle that references a
function, then
numI=0
for i=1:length(z)-1
numI = numI + QNC(@f,z(i),z(i+1),m);
end
assigns to numI the composite m-point Newton-Cotes estimate of the integral based on the partition housed
in z.
In §4.4 we will show how to automate the choice of a good partition. In the remainder of this section,
we focus on composite rules that are based on uniform partitions. In these rules, n ≥ 1,
b−a
zi = a + (i − 1)∆, ∆=
n
for i = 1:n + 1, and the composite rule evaluation has the form
numI = 0;
Delta=(b-a)/n;
for i=1:n
numI = numI + QNC(@f,a+(i-1)*Delta,a+i*Delta,m);
end
(n)
We designate the estimate produced by this quadrature rule by QNC(m) . The computation is a little inefficient
because it involves n − 1 extra function evaluations and a for-loop. The rightmost f-evaluation in the ith
call to QNC is the same as the leftmost f-evaluation in the i + 1st call. Figure 4.2 depicts the situation in the
four-subinterval, five-point rule case.
To avoid redundant f-evaluation and a for-loop with repeated function calls, it is better not to apply
QNC to each of the n subintegrals. Instead, we precompute all the required function evaluations and store
them in a single column vector fval(1:n(m-1)+1). The linear combination that defines the composite rule
(4)
is then calculated. In the preceding QNC(5) example, the 17 required function evaluations are assembled in
fval(1:17). If w is the weight vector for QNC(5) , then
(4) 
QNC(5) = ∆ w T fval(1:5) + w T fval(5:9) + w T fval(9:13) + w T fval(13:17) .
4.2. COMPOSITE RULES 9

(n)
From this we conclude that QNC(m) is a summation of n inner products, each of which involves the weight
vector w of the underlying rule and a portion of the fval-vector. The following function is organized around
this principle:
function numI = CompQNC(f,a,b,m,n)
% Composite Newton-Cotes rule for the integral of f from a to b.
% f is a handle that points to a function of the form f(x) where x is a
% scalar. f must be defined on [a,b] and it must return a column vector if x is a
% column vector.
% m is an integer that satisfies 2 <= m <= 11.
% n is a positive integer.
% numI is the composite m-point Newton-Cotes approximation of the integral of f
% from a to b with n equal length subintervals.

w = NCweights(m);
x = linspace(a,b,n*(m-1)+1)’;
f = f(x);
numI = 0; first = 1; last = m;
for i=1:n
%Add in the inner product for the i-th subintegral.
numI = numI + w’*f(first:last);
first = last;
last = last+m-1;
end
numI = Delta*numI;

4.2.2 Error
Let us examine the error. Suppose Qi is the m-point Newton-Cotes estimate of the ith subintegral. If this
rule is exact for polynomials of degree d, then using (4.4) we obtain
Z b Xn Z zi+1 Xn  d+2 !
(d+1) zi+1 − zi
f(x)dx = f(x)dx = Q i + cm f (ηi ) .
a i=1 zi i=1
m−1

By definition
n
X
(n)
QNC(m) = Qi
i=1

and
b−a
zi+1 − zi = ∆ = .
n
Moreover, it can be shown that
n
1 X (d+1)
f (ηi ) = f (d+1) (η)
n i=1

for some η ∈ [a, b] and so


Z b  d+2
(n) b−a
f(x)dx = QNC(m) + cm nf (d+1) (η). (4.7)
a n(m − 1)

If |f (d+1) (x)| ≤ Md+1 for all x ∈ [a, b], then


Z b "  d+2 #
b−a 1
(n)
QNC(m) − f(x)dx ≤ |cm |Md+1 d+1
. (4.8)
a m − 1 n

Comparing with (4.5), we see that the error in the composite rule is the error in the corresponding “simple”
rule divided by nd+1 . Thus, with m fixed it is possible to exercise error control by choosing n sufficiently
10 CHAPTER 4. NUMERICAL INTEGRATION

large. For example, suppose that we want to approximate the integral with a uniformly spaced composite
Simpson rule so that the error is less than a prescribed tolerance tol. If we know that the fourth derivative
of f is bounded by M4 , then we choose n so that
 5
1 b−a 1
M4 ≤ tol.
90 2 n4
To keep the number of function evaluations as small as possible, n should be the smallest positive integer
that satisfies r
4 M4 (b − a)
n ≥ (b − a) .
2880 · tol
The script file ShowCompQNC displays the error properties of the composite Newton-Cotes rules for three
different integrands. (See Figure 4.3.)
Example 1. QNC(m,n) error for integral of sin from 0 to pi/2
0
10

−2
10

−4
10

−6
10
Error in QNC(m,n)

−8
10
m=3

−10
10

−12
10

−14
10 m=5

−16 m=7
10

0 5 10 15 20 25 30 35 40
n = number of subintervals.

Figure 4.3 Error in composite Newton-Cotes rules

Problems

P4.2.1 Write a function error = CompQNCerror(a,b,m,DerBound,n) that returns an upper bound for the error in the uniformly
spaced composite m-point Newton-Cotes quadrature rule applied to the integral of f (x) from a to b. Use errNC.

P4.2.2 Rewrite CompQNC so that only one call to the integrand function is required.
(n)
P4.2.3 Write a function: n = nBest(a,b,m,DerBound,tol) that returns an integer n such that the error bound for QNC(m) is
less than tol.

P4.2.4 Let C(x) be the piecewise cubic Hermite interpolant of of f (x) on [a, b]. Develop a uniformly spaced composite rule
based on this interpolant.

P4.2.5 What can you say about the approximate value of T2 /T1 , where T1 is the time required to compute a certain integral
using a composite m-point Newton-Cotes rule with n subintervals, and T2 is the time required to compute the same integral
using a composite 2m-point Newton-Cotes rule with 10n subintervals.

4.3 Adaptive Quadrature


Uniformly spaced composite rules that are exact for degree d polynomials are efficient if f (d+1) is uniformly
behaved across [a, b]. However, if the magnitude of this derivative varies widely across the interval of
integration, then the error control process discussed in §4.2 may result in an unnecessary number of function
evaluations. This is because n is determined by an interval-wide derivative bound Md+1 . In regions where
f (d+1) is small compared to this value, the subintervals are (possibly) much shorter than necessary. Adaptive
quadrature methods address this problem by “discovering” where the integrand is ill behaved and shortening
the subintervals accordingly.
4.3. ADAPTIVE QUADRATURE 11

4.3.1 An Adaptive Newton-Cotes Procedure


To obtain a good partition of [a, b], we need to be able to estimate error. That way the partition can be
refined if the error is not small enough. One idea is to use two different quadrature rules. The difference
between the two predicted values of the integral could be taken as a measure of their inaccuracy:

function numI = AdaptQNC(f,a,b,...)


Compute the integral from a to b in two ways. Call the values A1 and A2
and assume that A2 is better.
Estimate the error in A2 based on |A1 − A2 |.
If the error is sufficiently small, then
numI = A2 ;
else
mid = (a+b)/2;
numI = AdaptQNC(f,a,mid,...) + AdaptQNC(f,mid,b,...);
end

This divide-and-conquer framework is similar to the one we developed for adaptive piecewise linear approx-
imation.
The filling in of the details begins with the development of a method for estimating the error. Fix m and
(1) (2)
set A1 = QNC(m) and A2 = QNC(m) . Thus A1 is the “simple” m-point rule estimate and A2 is the two-interval,
m-point rule estimate. If these rules are exact for degree d polynomials, then it can be shown that
"  d+2 #
(d+1) b−a
I = A1 + cm f (η1 ) (4.9)
m−1

"  d+2 #
(d+1) b−a 1
I = A2 + cm f (η2 ) (4.10)
m−1 2d+1

where η1 and η2 are in the interval [a, b]. We now make the assumption f (d+1) (η1 ) = f (d+1) (η2 ). This is
reasonable if f (d+1) does not vary much on [a, b]. (The shorter the interval, the more likely this is to be the
case.) Thus
I = A1 + C

and
I = A2 + C/2d+1 ,

where "  d+2 #


(d+1) b−a
C = cm f (η1 ) .
m−1

By subtracting these two equations for I from each other and solving for C, we get

A2 − A1
C=
1
1 − d+1
2
and so
|A1 − A2 |
|I − A2 | ≈ .
2d+1 − 1
Thus, the discrepancy between the two estimates divided by 2d+1 − 1 provides a reasonable estimate of the
error in A2 . If our goal is to produce an estimate of I that has absolute error tol or less, then the recursion
may be organized as follows:
12 CHAPTER 4. NUMERICAL INTEGRATION

function numI = AdaptQNC(f,a,b,m,tol)


% f is a handle that points to a function of the form f(x) where x is a
% scalar. f must be defined on [a,b] and it must return a column vector if x is a
% column vector.
% a,b are real scalars, m is an integer that satisfies 2 <= m <=11, and
% tol is a positive real.
% numI is a composite m-point Newton-Cotes approximation of the
% integral of f(x) from a to b, where the subinterval partition is
% determined adaptively.

% Estimates based on composite rule with 1 and 2 subintervals...


A1 = CompQNC(f,a,b,m,1);
A2 = CompQNC(f,a,b,m,2);
% The error estimate...
d = 2*floor((m-1)/2)+1;
error = (A2-A1)/(2^(d+1)-1);
% Accept of reject A2?
if abs(error) <= tol
% A2 is acceptable
numI = A2+error;
else
% Subdivide the problem...
mid = (a+b)/2;
numI = AdaptQNC(f,a,mid,m,tol/2) + AdaptQNC(f,mid,b,m,tol/2);
end

If the heuristic estimate of the error is greater than tol, then two recursive calls are initiated to obtain
estimates Z mid
QL ≈ f(x)dx = IL
a
and Z b
QR ≈ f(x)dx = IR
mid
that satisfy

|IL − QL | ≤ tol/2
and
|IR − QR | ≤ tol/2.
Setting Q = QL + QR , we see that

|I − Q| = |(IL − QL ) + (IR − QR )| ≤ |IL − QL| + |IR − QR | ≤ (tol/2) + (tol/2) = tol.

Insight into the economies that are realized by the adaptive framework can be obtained by applying
AdaptQNC to the integral of the built-in function

1 1
humps(x) = 2
+ − 6
0.01 + (x − 0.3) 0.04 + (x − 0.9)2

from 0 to 1. The tables in Figure 4.4 and Figure 4.5 report on the number of required function evaluations
associated with the call AdaptQNC(@humps,0,1,m,tol) for various choices of m and tol. These values would
be much higher if we used CompQNC(f,a,b,m,n) to attain the same level of accuracy. This is because higher
derivatives of humps are modest in size except near x = .3 and x = .9. To handle these “rough spots” we
would need a large number of subintervals, i.e., a large value for n in the call to CompQNC.
4.3. ADAPTIVE QUADRATURE 13

m = 3 m = 5 m = 7 m = 9
tol = .01 26 14 6 2
tol = .001 54 22 6 2
tol = .0001 94 30 14 10
tol = .00001 174 46 26 14

Figure 4.4 Number of Scalar f-evaluations required by QNC(@humps,0,1,m,tol)

m = 3 m = 5 m = 7 m = 9
tol = .01 104 98 60 26
tol = .001 216 154 60 26
tol = .0001 376 210 140 130
tol = .00001 696 322 260 182

Figure 4.5 Number of Vector f-evaluations required by QNC(@humps,0,1,m,tol)

Problems

P4.3.1 The one-panel midpoint rule Q1 for the integral


Z b
I= f (x)dx
a

is defined by „ «
a+b
Q1 = (b − a)f .
2
The two-panel midpoint rule Q2 for I is given by
b−a 3a + b a + 3b
„ „ « „ ««
Q2 = f +f .
2 4 4
Using the heuristic |I−Q2 | ≤ |Q2−Q1 |, write an efficient Matlab adaptive quadrature routine of the form Adapt(f,a,b,tol,...)
that returns an estimate of I that is accurate to within the tolerance given by tol. You may extend the parameter list, and you
may use nargin as required. You may ignore the possibility of infinite recursion.

P4.3.2 A number of efficiency improvements can be made to AdaptQNC. A casual glance at AdaptQNC reveals two sources
of redundant function evaluations: First, each function evaluation required in the assignment to A1 is also required in the
assignment to A2. Second, the recursive calls could (but do not) make use of previous function evaluations. In addressing these
deficiencies, you are to follow these ground rules:
• A call of the form AdaptQNC1(@f,a,b,m,tol) must produce the same value as a call of the form AdaptQNC(@f,a,b,m,tol).
• No global variables are allowed.
To “transmit” appropriate function values in the recursive calls, you will want to design AdaptQNC1 so that it has an “optional”
sixth argument fValues. By making this argument optional, the same five-parameter calls at the top level are permitted.

P4.3.3 An implementation y = MyF(x) of the function f (x) has the property that it returns f (xi ) in yi for i = 1:n where n is
the length of x. Assume that the cost of a MyF evaluation is constant and independent of the length of the input vector x. We
want to compute
Z b
I= f (x)dx
a
with specified accuracy. Explain why it might be more efficient to use a composite trapezoidal rule with uniform length
subintervals than an adaptive trapezoidal rule if we have information about the second derivative of f .

P4.3.4 Assume that MyF is a given implementation of the function f (x) and that f has positive period T . Write an efficient
Matlab script for computing the integral
Z b
I= f (x)dx
a
with absolute error ≤ 10−6 . Assume that a and b are given and make effective use of the Matlab quadrature function quad.
The absolute error is no bigger than tol.
14 CHAPTER 4. NUMERICAL INTEGRATION

P4.3.5 Let Qn be the equal spacing composite trapezoidal rule:


„ «
1 1 b−a
Qn = h f (x1 ) + f (x2) + · · · + f (xn−1 ) + f (xn ) h= ,
2 2 n−1
where x = linspace(a, b, n) and we assume that n ≥ 2. Assume that there is a constant C (independent of n), such that
Z b
I= f (x)f x = Qn + Ch2 .
a
(a) Give an expression for |I − Q2n | in terms of |Q2n − Qn |. (b) Write an efficient function Q = TrapRecur(f,a,b,tol that
returns in Q the value of Q2k+1 , where k is the smallest positive integer so that |I − Q2k+1 | is smaller than the given positive
tolerance tol.

P4.3.6 Assume that the function f (x) is available and define


Z z
φ(z) = f (x)dx.
−z

Using quad, show how to compute an array phiVals(1:100) with the property that φ(k) is assigned to phiVals(k) for k=1:100.

P4.3.7 Give a solution procedure for computing


Z b „Z x «
I= f (x, y)dy dx,
a a
where f(x,y) is a given. All integrals in your method must be computed using quad. Clearly define the functions that are
required by your method. Note: The built-in Matlab function dblquad can be used to evaluate double integrals of the form
Z bZ d
I= f (x, y)dxdy,
a c
but this does not help in this problem.

4.4 Gauss Quadrature and Spline Quadrature


We discuss two other approaches to the quadrature problem. Gauss quadrature rules are of great interest
because they optimize accuracy for a given number of f-evaluations. They also have merit in certain prob-
lems where the integrand has singularities. In situations where the function evaluations are experimentally
determined, spline quadrature has a certain appeal.

4.4.1 Gauss Quadrature


In the Newton-Cotes framework, the integrand is sampled at regular intervals across [a, b]. In the Gauss
quadrature framework, the abscissas are positioned in such a way that the rule is correct for polynomials of
maximal degree.
A simple example clarifies the main idea. Let us try to determine weights w1 and w2 and abscissas x1
and x2 so that Z 1
w1 f(x1 ) + w2 f(x2 ) = f(x)dx
−1

for polynomials of degree 3 or less. This is plausible since there are four parameters to choose (w1 , w2 , x1 ,
x2 ) and four constraints obtained by forcing the rule to be exact for the functions 1, x, x2 , and x3 :

w1 + w2 = 2
w1 x1 + w2 x2 = 0
w1 x21 + w2 x22 = 2/3
w1 x31 + w2 x32 = 0

By multiplying the second equation by x21 and subtracting it from the fourth equation we get w2 x2 (x21 −x22 ) =
0, and so x2 = −x1 . It follows from the second equation that w1 = √ w2 and thus, √ from the first equation,
w1 = w2 = 1. From the third equation, x21 = 1/3 and so x1 = −1/ 3 and x2 = 1/ 3. Thus, for any f(x)
we have Z 1 √ √
f(x)dx ≈ f(−1/ 3) + f(1/ 3).
−1
4.4. GAUSS QUADRATURE AND SPLINE QUADRATURE 15

This is the two-point Gauss-Legendre rule.


The m-point Gauss-Legendre rule has the form

QGL(m) = w1 f(x1 ) + · · · + wm f(xm ),

where the wi and xi are chosen to make the rule exact for polynomials of degree 2m − 1. One way to define
these 2m parameters is by the 2m nonlinear equations

1 − (−1)k+1
w1 xk1 + w2 xk2 + · · · + wm xkm = , k = 0:2m − 1.
k +1
The kth equation is the requirement that the rule
Z 1
w1 f(x1 ) + · · · + wm f(xm ) = f(x)dx
−1

be exact for f(x) = xk . It turns out that this system has a unique solution, which we encapsulate in the
following function for the cases m = 2:6:

function [w,x] = GLweights(m)


% [w,x] = GLWeights(m)
% w is a column m-vector consisting of the weights for the m-point Gauss-Legendre rule.
% x is a column m-vector consisting of the abscissae.
% m is an integer that satisfies 2 <= m <= 6.
w = ones(m,1);
x = ones(m,1);
if m==2
w(1) = 1.000000000000000; w(2) = w(1);
x(1) = -0.577350269189626; x(2) = -x(1);
elseif m==3
:
end

The Gauss-Legendre rules


Z 1
QGL(m) = w1 f(x1 ) + · · · + wm f(xm ) ≈ f(x)dx
−1

are not restrictive even though they pertain to integrals from −1 to 1. By a change of variable, we have

Z b Z 1
b−a
f(x)dx = g(x)dx,
a 2 −1

where

 
a+b b−a
g(x) = f + x ,
2 2

and so

     Z b
b−a a+b b−a a+b b−a
w1 f + x1 + · · · + wm f + xm ≈ f(x)dx.
2 2 2 2 2 a

This gives
16 CHAPTER 4. NUMERICAL INTEGRATION

function numI = QGL(f,a,b,m)


% f is a handle that references a function of the form f(x) that
% is defined on [a,b]. f should return a column vector if x is a column vector.
% a,b are real scalars.
% m is an integer that satisfies 2 <= m <= 6.
% numI is the m-point Gauss-Legendre approximation of the
% integral of f(x) from a to b.
[w,x] = GLWeights(m);
fvals = f((b-a)/2)*x + ((a+b)/2)*ones(m,1));
numI = ((b-a)/2)*w’*fvals;

It can be shown that Z


b (b − a)2m+1 (m!)4

f(x)dx − QGL(m) ≤ M2m ,
a (2m + 1)[(2m)!]3

where M2m is a constant that bounds |f 2m (x)| on [a, b]. The script file GLvsNC compares the QNC(m) and
QGL(m) rules when they are applied to the integral of sin(x) from 0 to π/2:

m NC(m) GL(m)
------------------------------------------------
2 0.7853981633974483 0.9984726134041148
3 1.0022798774922104 1.0000081215555008
4 1.0010049233142790 0.9999999771971151
5 0.9999915654729927 1.0000000000395670
6 0.9999952613861668 0.9999999999999533

Notice that for this particularly easy problem, QGL(m) has approximately the accuracy of QNC(2m) .

It is possible to formulate an adaptive quadrature procedure that is based on a Gauss-Legendre rule.


However, the “weird” location of the abscissae creates a problem. The f-evaluations that are required
when we apply an m-point rule across [a, b] are not shared by the m-point rules applied to the half-interval
problems. The Gauss-Kronrod framework circumvents this problem. The basic idea is to work with a pair
of rules that share f-evaluations. The (15,7) Gauss-Kronrod procedure, works with a 15-point rule
Z 1 15
X (15) (15)
f(x)dx ≈ QGK(15) = ωk f(xk )
−1 k=1

and a 7-point rule,


Z 1 7
X (7) (7)
f(x)dx ≈ QGK(7) = ωk f(xk ).
−1 k=1
(15) (7)
The key connection between x and x is this:

x(7) = x(15)(2:2:15).

See Figure 4.x. Moreover, there is a heuristic argument that says


Z 1


f(x)dx − QGK(15) ≈ 200|QGK(15) − QGK(7) |1.5. (4.11)
−1

The demo function ShowGK affirms this result.


One can formulate an adaptive procedure based on these two rules that use these facts. We compute
QGK(15) and get QGK(7) “for free” because of the shared f-evaluations. If the discrepancy between the two
rules is too large, then we subdivide the problem and repeat the process on each half-interval. The Matlab
procedure quadgk is based on this idea.
4.4. GAUSS QUADRATURE AND SPLINE QUADRATURE 17

The 7−point rule

The 15−point rule

−1 −0.8 −0.6 −0.4 −0.2 0 0.2 0.4 0.6 0.8 1

Figure 4.6 Abscissa location for the (15,7) Gauss-Kronrod Pair

Problems

P4.4.1 If QGL(m) is the m-point Gauss-Legendre estimate for


Z b
I= f (x)dx,
a

then it can be shown that


(b − a)2m+1 (m!)4
|I − QGL(m) | ≤ M2m ≡ Em ,
(2m + 1)[(2m)!]3
where the constant M2m satisfies |f (2m) (x)| ≤ M2m for all x ∈ [a, b]. The following questions apply to the case when
f (x) = ecx , where c > 0. Assume that a < b. (a) Give a good choice for M2m . (b) Give an expression for Em+1 /Em . (c) Write
a Matlab script that determines the smallest positive integer m so that Em is less than tol.

P4.4.2 Write a function numI = CompQGL(f,a,b,m,n) that approximates the integral of a function from a to b by applying the
m-point Gauss-Legendre rule on n equal-length subintervals of [a, b].

P4.4.3 Develop an addaptive quadrature procedure numI = AdapkGK(f,a,b,tol) that is based on the (15,7) Gauss-Kronrod
pair and the error heuristic (4.11).

4.4.2 Spline Quadrature


Suppose S(x) is a cubic spline interpolant of (xi , yi ), i = 1:n and that we wish to compute
Z xn
I= S(x)dx.
x1

If the ith local cubic is represented by

qi (x) = ρi4 + ρi,3 (x − xi ) + ρi,2 (x − xi )2 + ρi,1 (x − xi )3 ,

then
Z xi+1
ρi,3 2 ρi,2 3 ρi,1 4
qi (x)dx = ρi,4 hi + h + h + h ,
xi 2 i 3 i 4 i

where hi = xi+1 − xi . By summing these quantities from i = 1:n − 1, we obtain the sought-after spline
integral:

function numI = SplineQ(x,y)


% Integrates the spline interpolant of the data specified by the
% column n-vectors x and y. It is a assumed that x(1) < ... < x(n)
% and that the spline is produced by the Matlab function spline.
% The integral is from x(1) to x(n).
18 CHAPTER 4. NUMERICAL INTEGRATION

S = spline(x,y);
[x,rho,L,k] = unmkpp(S);
sum = 0;
for i=1:L
% Add in the integral from x(i) to x(i+1).
h = x(i+1)-x(i);
subI = h*(((rho(i,1)*h/4 + rho(i,2)/3)*h + rho(i,3)/2)*h + rho(i,4));
sum = sum + subI;
end
numI = sum;

The script file ShowSplineQ uses this function to produce the following estimates for the integral of sine
from 0 to π/2:

m Spline Quadrature
----------------------------
5 1.0001345849741938
50 0.9999999990552404
500 0.9999999999998678

Here, the spline interpolates the sine function at x = linspace(0,pi/2,m).


Problems
P4.4.4 Modify SplineQ so that a four-argument call SplineQ(x,y,a,b) returns the integral of the spline interpolant from a to
b. Assume that x1 ≤ a ≤ b ≤ xn .

P4.4.5 Let a(t) denote the acceleration of an object at time t. If v0 is the object’s velocity at t = 0, then the velocity at time
t is prescribed by Z t
v(t) = v0 + a(τ )dτ.
0
Likewise, if x0 is the position at t = 0, then the position at time t is given by
Z t
x(t) = x0 + v(τ )dτ.
0
Now suppose that we have snapshots a(ti ) of the acceleration at times ti , i = 1:m, t1 = 0. Assume that we know the initial
position x0 and velocity v0 . Our goal is to estimate position from this data. Spline quadrature will be used to approximate the
preceding integrals. Let Sa (t) be the not-a-knot spline interpolant of the acceleration data (ti, a(ti)), i = 1:m, and define
Z t
ṽ(t) = v0 + Sa (τ )dτ.
0

Let Sv (t) be the not-a-knot spline interpolant of the data (ti , ṽ(ti )), i = 1:m, and define
Z t
x̃(t) = x0 + Sv (τ )dτ.
0

The spline interpolant Sx (t) of the data (ti , x̃(ti )) is then an approximation of the true position. Write a function

function Sx = PosVel(a,t,x0,v0)}
%
% t is an m-vector of equally spaced time values with t(1) = 0, m>=2.
% a is an m-vector of accelerations, a(i) = acceleration at time t(i).
% x0 and v0 are the position and velocity at t=0
%
% Sx the pp-representation of a spline that approximates position.

Try it out on the data t = linspace(0,50,500), with a(t) = 10e−t/25 sin(t). However, before you turn the a vector over to
PosVel, contaminate it with noise: a = a + .01*randn(size(a)). Produce a plot of the exact and estimated positions across
[0,50] and a separate plot of x(t) − Sx (t) across [0, 50]. Also print the value of Sx (t) at t = 50. Repeat with m = 50 instead of
500. Use the Matlab spline function.
4.5. MATLAB’S QUADRATURE TOOLS 19

P4.4.6 Assume that we have a vectorized implementation f.m of a positive-valued function f (x) and that x is a given column
n-vector with x1 < ... < xn . (a) Write a Matlab fragment that sets up a column n-vector q with the property that
˛ Z xi ˛
˛ ˛
˛qi − f (x)dx ˛ ≤ tol
˛ ˛
x1

for i = 1:n. Assume that tol is a given positive tolerance. Make effective use of quad. (By setting the relative error tolerance
to zero, quad will return an approximation of the integral that satisfies the absolute error tolerance.) (b) Assume that the array
q has been successfully computed in (a). Making effective use of spline, ppval, and the idea of inverse interpolation, show how
to estimate x∗ so that Z x∗
1 xn
Z
f (x) = f (x)dx.
x1 2 x1
P4.4.7 Let (x1 , y1 ), . . . , (xn , yn ) be given points in the plane. Let di be the straight-line distance between (xi , yi ) and
(xi+1 , yi+1 ), i = 1:n − 1. Set ti = d1 + · · · + di−1 , i = 1:n. Suppose Sx (t) is a spline interpolant of (t1 , x1 ), . . . , (tn, xn ) and
that Sy (t) is a spline interpolant of (t1 , y1 ), . . . , (tn , yn ). It follows that the curve Λ = {(Sx (t), Sy (t)) : t1 ≤ t ≤ tn } is smooth
and passes through the n points. Write a Matlab function [Sx,Sy,L] = Arc(x,y) that returns the two splines interpolants (in
pp-form) and the length of Λ, i.e.,
Z tn q
L= [Sx0 (t)]2 + [Sy0 (t)]2 dt.
t1
Use quad for the integral with the default tolerance. You will have to set up an integrand function that accesses the piecewise
quadratic functions Sx0 (t) and Sy0 (t). Write a script that displays the curve Λ where the input points are prescribed by
x = [ 3 2 1 2 4 5 4 3 2 4 5 5 3];
y = [ 7 6 5 4 3 2 1 1 2 4 5 6 7];
Print the curve length in the title of the plot.

4.5 Matlab’s Quadrature Tools


Consider the function f(x) = humps(x) where humps is the built-in Matlab function

1 1
humps(x) = 2
+ − 6.
(x − .3) + .01 (x − .9)2 + .04

This function’s higher derivatives are large near x = .3 and x = .9:

f(x) = humps(x)
100

90

80

70

60

50

40

30

20

10

0
0 0.2 0.4 0.6 0.8 1

The function quad can be used to approximate the integral of this function from 0 to 1:

>> Q = quad(@humps,0,1)
Q = 29.858326128427638
20 CHAPTER 4. NUMERICAL INTEGRATION

The number of f-evaluations can be obtained by supplying a second output parameter:

>> [Q,fevals] = quad(@humps,0,1)


Q = 29.858326128427638
fevals = 145

Unless it is told otherwise, quad aims to compute the required integral with absolute error bounded by
.000001. The error tolerance can be modified:

>> [Q,fevals] = quad(@humps,0,1,10^-12)


Q = 29.858325395498067
fevals = 2321

The function quad implements an adaptive version of the composite Simpson rule. See §4.4. If high accuracy
is required, then it is sometimes more economical to use the Matlab quadrature function quadl:

>> [Q,fevals] = quadl(@humps,0,1,10^-12)


Q = 29.858325395498671
fevals = 1608

The function ShowQUADs(f,a,b) approximates I(f, a, b) and can be used to experiment with these two
quadrature procedures for various choices of error tolerance. ShowQUADs(@sin,0,pi) tells us that quadl is
to be preferred for very smooth integrands like f(x) = sin(x):
quad quadl
tol Approximation f-evals Approximation f-evals
-------------------------------------------------------------------
1.0e-003 1.999993496535 13 1.999999977471 18
1.0e-006 1.999999996398 33 1.999999977471 18
1.0e-009 1.999999999999 129 2.000000000000 48
1.0e-012 2.000000000000 497 2.000000000000 48

On the other hand, ShowQuads(@(x) sin(1./x),.01,1) reveals that for nasty integrands like sin(1/x) it is
better to use a low-order rule like quad, especially for modest tolerances:
quad quadl
tol Approximation f-evals Approximation f-evals
-------------------------------------------------------------------
1.0e-003 0.463673444706 25 0.504011796906 138
1.0e-006 0.504041285733 237 0.503981893171 558
1.0e-009 0.503981892714 981 0.503981893175 1338
1.0e-012 0.503981893175 3985 0.503981893175 3648

The function quadgk offers greater control over error (absolute or relative) and can report back an
estimate of the error if required. A call of the form
[Q,est] = quadgk(@f,a,b,’AbsTol’,tol1,’RelTol’,tol2)

attempts to return a value in Q that satisfies


|I(f, a, b) − Q| ≤ max{AbsTol,RelTol}.
If relative error is critical, then set tol1=0. If absolute error is the concern, set tol2 = 0. In either case,
the estimate returned in est is an estimate of the absolute error. If quadgk spots a problem with its error
control, then it may suggest an increase in the value of MaxIntervalCount which permits the procedure to
get a more accurate answer by evaluating f and more points. In this case you can try again with a response
of t he form
4.5. MATLAB’S QUADRATURE TOOLS 21

[Q,est] = quadgk(@f,a,b,’AbsTol’,tol1,’RelTol’,tol2,’MaxIntervalCount’,BiggerValue)

Here are some results when quadgk is used to compute


Z 1  
1
I= 100 sin dx
0 x

with BiggerValue = 100000:


Result Via Error AbsTol RelTol
quadgk Estimate
-------------------------------------------------
50.40654795 0.00061442 0.0010 0.0000
50.40465490 0.03100950 0.0000 0.0010
50.40670252 0.00007224 0.0001 0.0000
50.40658290 0.00430233 0.0000 0.0001

In some cases, quadgk can handle endpoint singularities. For example,


Q = quadgk(@(x) 1./sqrt(x),0,1)
Q = 1.999999999999763

The procedure can also accommodate infinite endpoints as in


Z +∞
1 2 2
I = √ e−(x−µ) /(2σ ) dx.
2π −∞
Thus,
mu = 1;
sigma = 3;
Q = quadgk(@(x) exp(-((x-mu).^2/(2*sigma^2)))/(sigma*sqrt(2*pi)),-inf,inf)
Q = 1.000000000146726

thereby affirming that the area under the normal distribution N (µ, σ) equals one.

Problems
P4.5.1 Consider the function Z 2 „
α
«
I(α) = (2 + sin(10α)) xα sin dx
0 2−x
Write a script that confirms the fact that

max I(α) = I(.7859336743...)


0≤α≤5

Make effective use of Matlab’s quadrature software.


P4.5.2 It turns out that Z 1 „ «
1 ln(x)
lim→0 · cos dx = .3233674316...
 x x
Write the most efficient script you can that confirms this result. Make effective use of Matlab’s quadrature software.
22 CHAPTER 4. NUMERICAL INTEGRATION

Script Files
ShowNCError Illustrates NCerror.
ShowCompQNC Illustrates CompQNC on three examples.
ShowAdapts Illustrates AdaptQNC.
GLvsNC Compares Gauss-Legendre and Newton-Cotes rules.
ShowSplineQ Illustrates SplineQ.
ShowGK Illustrates the (15,7) Gauss-Kronrod rule.

Function Files
ShowQuads Illustrates quad, quadl, and quadgk.
ShowNCIdea Displays the idea behind the Newton-Cotes rules.
NCWeights Constructs the Newton-Cotes weight vector.
QNC The simple Newton-Cotes rule.
NCError Error in the simple Newton-Cotes rule.
CompQNC Equally-spaced, composite Newton-Cotes rule.
AdaptQNC Adaptive Newton-Cotes quadrature.
SpecHumps The humps function with function call counters.
GLWeights Constructs the Gauss-Legendre weight vector.
QGL The simple Gauss-Legendre rule.
SplineQ Spline quadrature.

References
P. Davis and P. Rabinowitz (1984). Methods of Numerical Integration, 2nd Ed., Academic Press, New York.
G.H. Golub and J.M. Ortega (1993). Scientific Computing: An Introduction with Parallel Computing,
Academic Press, Boston.
A. Stroud (1972). Approximate Calculation of Multiple Integrals, Prentice Hall, Englewood Cliffs, NJ.
Chapter 9

The Initial Value Problem

§9.1 Basic Concepts


§9.2 The Runge-Kutta Methods
§9.3 The Adams Methods

The goal in the initial value problem (IVP) is to find a function y(t) given its value at some initial time
t0 and a recipe f(t, y) for its slope:

y0 (t) = f(t, y(t)), y(t0 ) = y0 .

In applications we may want to plot an approximation to y(t) over a designated interval of interest [t0 , tmax]
in an effort to discover qualitative properties of the solution. Or we may require a highly accurate estimate
of y(t) at some single, prescribed value t = T .
The methods we develop produce a sequence of solution snapshots (t1 , y1 ), (t2 , y2 ), . . . that are regarded
as approximations to (t1 , y(t1 ), (t2 , y(t2 )), etc. All we have at our disposal is the “slope function” f(t, y), best
thought of as a Matlab function f(t,y), that can be called whenever we need information about where
y(t) is “headed.” IVP solvers differ in how they use the slope function.
In §9.1 we use the Euler methods to introduce the basic ideas associated with approximate IVP solving:
discretization, local error, global error, stability, etc. In practice the IVP usually involves a vector of unknown
functions, and the treatment of such problems is also covered in §9.1. In this setting the given slope function
f(t, y) is a vector of scalar slope functions, and its evaluation tells us how each component in the unknown
y(t) vector is changing with t.
The Runge-Kutta and Adams methods are then presented in §9.2 and §9.3 together with the built-in
Matlab IVP solvers ode23 and ode45. We also discuss stepsize control, a topic of great practical importance
and another occasion to show off the role of calculus-based heuristics in scientific computing.
Quality software for the IVP is very complex. Years of research and development stand behind codes like
ode23 and ode45. The implementations that we develop in this chapter are designed to build intuition and,
if anything, are just the first step in the long journey from textbook formula to production software.

9.1 Basic Concepts


A “family” of functions generally satisfies a differential equation of the form y0 (t) = f(t, y). The initial
condition y(t0 ) = y0 singles out one of these family members for the solution to the IVP. For example,
functions of the form y(t) = ce−5t satisfy y0 (t) = −5y(t). If we stipulate that y(0) = 1, then y(t) = e−5t is
the unique solution to the IVP. (See Figure 9.1.) Our goal is to produce a sequence of points (ti , yi ) that
reasonably track the solution curve as time evolves. The Euler methods that we develop in this section
organize this tracking process around a linear model.

1
2 CHAPTER 9. THE INITIAL VALUE PROBLEM

Solutions to y’(t) = −5 y(t)


2

1.8

1.6

1.4

1.2

0.8

0.6

0.4

0.2

0
−0.1 −0.05 0 0.05 0.1 0.15 0.2 0.25 0.3 0.35 0.4

Figure 9.1 Solution curves

9.1.1 Derivation of the Euler Method


From the initial condition, we know that (t0 , y0 ) is on the solution curve. At this point the slope of the
solution is computable via the function f:
f0 = f(t0 , y0 ).
To estimate y(t) at some future time t1 = t0 + h0 we consider the following Taylor expansion:

y(t0 + h0 ) ≈ y(t0 ) + h0 y0 (t0 ) = y0 + h0 f(t0 , y0 ).

This suggests that we use


y1 = y0 + h0 f(t0 , y0 )
as our approximation to the solution at time t1 . The parameter h0 > 0 is the step, and it can be said that
with the production of y1 we have “integrated the IVP forward” to t = t1 .
With y1 ≈ y(t1 ) in hand, we try to push our knowledge of the solution one step further into the future.
Let h1 be the next step. A Taylor expansion about t = t1 says that

y(t1 + h1 ) ≈ y(t1 ) + h1 y0 (t1 ) = y(t1 ) + h1 f(t1 , y(t1 )).

Note that in this case the right-hand side is not computable because we do not know the exact solution at
t = t1 . However, if we are willing to use the approximations

y1 ≈ y(t1 )

and
f1 = f(t1 , y1 ) ≈ f(t1 , y(t1 )),
then at time t2 = t1 + h1 we have
y(t2 ) ≈ y2 = y1 + h1 f1 .
The pattern is now clear. At each step we evaluate f at the current approximate solution point (tn , yn ) and
then use that slope information to get yn+1 . The key equation is

yn+1 = yn + hn f(tn , yn ),

and its repeated application defines the Euler method:


9.1. BASIC CONCEPTS 3

n=0
Repeat:
fn = f(tn , yn )
Determine the step hn > 0 and set tn+1 = tn + hn .
yn+1 = yn + hn fn .
n= n+1

The script file ShowEuler solicits the time steps interactively and applies the Euler method to the problem
y0 = −5y, y(0) = 1. (See Figure 9.2.) The determination of the step size is crucial.

Five Steps of Euler Method (y’=−5y, y(0)=1)


2

1.8

1.6

1.4

1.2

0.8

0.6

0.4

0.2

0
−0.1 −0.05 0 0.05 0.1 0.15 0.2 0.25 0.3 0.35 0.4

Figure 9.2 Five steps of Euler’s method

Our intuition says that we can control error by choosing hn appropriately. Accuracy should increase
with shorter steps. On the other hand, shorter steps mean more f-evaluations as we integrate across the
interval of interest. As in the quadrature problem and the nonlinear equation-solving problem, the number
of f-evaluations usually determines execution time, and the efficiency analysis of any IVP method must
include a tabulation of this statistic. The basic game to be played is to get the required snapshots of y(t)
with sufficient accuracy, evaluating f(t, y) as infrequently as possible. To see what we are up against, we
need to understand how the errors in the local model compound as we integrate across the time interval of
interest.

9.1.2 Local Error, Global Error, and Stability


Assume in the Euler method that yn−1 is exact and let h = hn−1 . By subtracting yn = yn−1 + hfn−1 from
the Taylor expansion
h2
y(tn ) = yn−1 + hy0 (tn−1 ) + y(2) (η), η ∈ [tn−1 , tn ],
2
we find that
h2 (2)
y(tn ) − yn = y (η).
2
This is called the local truncation error (LTE) In general, the LTE for an IVP method is the error that
results when a single step is performed with exact “input data.” It is a key attribute of any IVP solver, and
the order of the method is used to designate its form. A method has order k if its LTE goes to zero like
hk+1 . Thus, the Euler method has order 1. The error in an individual Euler step depends on the square
of the step and the behavior of the second derivative. Higher-order methods are pursued in the next two
sections.
4 CHAPTER 9. THE INITIAL VALUE PROBLEM

A good way to visualize the LTE is to recognize that at each step, (tn , yn ) sits on some solution curve
yn (t) that satisfies the differential equation y0 (t) = f(t, y(t)). With each step we jump to a new solution
curve, and the size of the jump is the LTE. (See Figure 9.3.)
Euler Solution (h=0.1) of y’=−5y, y(0) = 1
1.8

1.6

1.4

1.2 y’ = −5.0y, y(0) = 1


* = exact solution
1 o = computed solution

0.8

0.6

0.4

0.2

0
−0.1 −0.05 0 0.05 0.1 0.15 0.2 0.25 0.3 0.35 0.4

Figure 9.3 Jumping trajectories

Distinct from the local truncation error is the global error. The global error gn is the actual difference
between the t = tn solution yn produced by the IVP solver and the true IVP solution y(tn ):

gn = y(tn ) − yn .

As we have mentioned, the local truncation error in getting yn is defined by

LT En = yn−1 (tn ) − yn ,

where yn−1 (t) satisfies the IVP

y0 (t) = f(t, y(t)), y(tn−1 ) = yn−1 .

LTE is tractable analytically and, as we shall see, it can be estimated in practice. However, in applications
it is the global error that is usually of interest. It turns out that it is possible to control global error by
controlling the individual LTEs if the underlying IVP is stable. We discuss this after we prove the following
result.

Theorem 9 consec Assume that, for n = 0:N , a function yn (t) exists that solves the IVP

y0 (t) = f(t, y(t)), y(tn ) = yn ,

where (t0 , y0 ), . . . , (tN , yN ) are given and t0 < t1 < · · · < tN . Define the global error by

gn = y0 (tn ) − yn

and the local truncation error by


LT En = yn−1 (tn ) − yn .
If
∂f(t, y)
fy = ≤0
∂y
for all t ∈ [t0 , tN ] and none of the trajectories

{(t, yn (t)) : t0 ≤ t ≤ tN }, n = 0:N


9.1. BASIC CONCEPTS 5

intersect, then for n = 1:N


n
X
|gn | ≤ |LT Ek |.
k=1

Proof If y0 (tn ) > yn−1 (tn ), then because fy is negative we have


Z tn
(f(t, y0 (t)) − f(t, yn−1 (t)) dt < 0.
tn−1

It follows that
Z tn
0 < y0 (tn ) − yn−1 (tn ) = (y0 (tn−1 ) − yn−1 (tn−1 )) + (f(t, y0 (t)) − f(t, yn−1 (t)) dt
tn−1

< (y0 (tn−1 ) − yn−1 (tn−1 )) ,

and so
|y0 (tn ) − yn−1 (tn )| ≤ |y0 (tn−1 ) − yn−1 (tn−1 )|. (9.1)
Likewise, if y0 (tn ) < yn−1 (tn ), then
Z tn
(f(t, yn−1 (t)) − f(t, y0 (t)) dt < 0,
tn−1

and so
Z tn
0 < yn−1 (tn ) − y0 (tn ) = (yn−1 (tn−1 ) − y0 (tn−1 )) + (f(t, yn−1 (t)) − f(t, y0 (t)) dt
tn−1

< yn−1 (tn−1 ) − y0 (tn−1 ).

Thus, in either case (9.1) holds and so

|gn| = |y0 (tn ) − yn |

≤ |y0 (tn ) − yn−1 (tn )| + |yn−1 (tn ) − yn |

< |y0 (tn−1 ) − yn−1 (tn−1 )| + |yn−1 (tn ) − yn |

= |gn−1 | + |LT En |.

The theorem follows by induction since g1 = LT E1 . 

The theorem essentially says that if ∂f/∂y is negative across the interval of interest, then global error at
t = tn is less than the sum of the local errors made by the IVP solver in reaching tn . The sign of this partial
derivative is tied up with the stability of the IVP. Roughly speaking, if small changes in the initial value
induce correspondingly small changes in the IVP solution, then we say that the IVP is stable. The concept is
much more involved than the condition/stability issues that we talked about in connection with the Ax = b
problem. The mathematics is deep and interesting but beyond what we can do here.
So instead we look at the model problem y0 (t) = ay(t), y(0) = c and deduce some of the key ideas. In
this example, ∂f/∂y = a and so Theorem 9 applies if a < 0. We know that the solution y(t) = ceat decays
if and only if a is negative. If ỹ(t) solves the same differential equation with initial value y(0) = c̃, then

|ỹ(t) − y(t)| = |c̃ − c|eat ,

showing how “earlier error” is damped out as t increases.


To illustrate how global error might be controlled in practice, consider the problem of computing y(tmax )
to within a tolerance tol, where y(t) solves a stable IVP y0 (t) = f(t, y(t)), y(t0 ) = y0 . Assume that a
6 CHAPTER 9. THE INITIAL VALUE PROBLEM

fixed-step Euler method is to be used and that we have a bound M2 for |y(2) (t)| on the interval [t0 , tmax ]. If
h = (tmax − t0 )/N is the step size, then from what we know about the local truncation error of the method,
h2
|LT En | ≤ M2 .
2
Assuming that Theorem 9 applies,
N
X h2 tmax − t0
|y(tmax ) − yN | ≤ |LT En | = M2 N = M2 h.
n=1
2 2

Thus, to make this upper bound less than a prescribed tol > 0, we merely set N to be the smallest integer
that satisfies
(tmax − t0 )2
M2 ≤ tol.
2N
Here is an implementation of the overall process:

function [tvals,yvals] = FixedEuler(f,y0,t0,tmax,M2,tol)


% Fixed step Euler method.
%
% f is a handle that references a function of the form f(t,y).
% M2 a bound on the second derivative of the solution to
% y’ = f(t,y), y(t0) = y0
% on the interval [t0,tmax].

% Determine positive n so that if tvals = linspace(t0,tmax,n), then


% y(i) is within tol of the true solution y(tvals(i)) for i=1:n.
n = ceil(((tmax-t0)^2*M2)/(2*tol))+1;
h = (tmax-t0)/(n-1);
yvals = zeros(n,1);
tvals = linspace(t0,tmax,n)’;
yvals(1) = y0;
for k=1:n-1
fval = f(tvals(k),yvals(k));
yvals(k+1) = yvals(k)+h*fval;
end

Figure 9.4 shows the error when this solution framework is applied to the model problem y0 = −y across the
interval [0, 5]. The trouble with this approach to global error control is that (1) we rarely have good bound
information about |y(2) | and (2) it would be better to determine h adaptively so that longer step sizes can
be taken in regions where the solution is smooth. This matter is pursued in §9.3.5.
Rounding errors are also an issue in IVP solving, especially when lots of very short steps are taken. In
Figure 9.5 we plot the errors sustained when we solve y0 = −y, y(0) = 1 across [0, 1] with Euler’s method in a
three-digit floating point environment. The results for steps h =1/140, 1/160, and 1/180 are reported. Note
that the error gets worse as h gets smaller because the step sizes are in the neighborhood of unit roundoff.
However, for the kind of problems that we are looking at, it is the discretization errors that dominate the
discussion of accuracy.
Another issue that colors the performance of an IVP solver is the stability of the method itself. This
is quite distinct from the notion of problem stability discussed earlier. It is possible for a method with a
particular h to be unstable when it is applied to a stable IVP. For example, if we apply the Euler method
to y0 (t) = −10y(t), then the iteration takes the form

yn+1 = (1 − 10h)yn .

To ensure that the errors are not magnified as the iteration progresses, we must insist that

|1 − 10h| < 1
9.1. BASIC CONCEPTS 7

−4 Fixed h Euler Error for y’=−y, y(0) = 1


x 10
8

0
0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 5
tol = 0.010, n = 1251

Figure 9.4 Error in fixed-step Euler

Euler in 3−Digit Arithmetic


0.05

0.045

0.04

0.035

0.03
Error

0.025
n = 180
0.02

0.015
n = 160

0.01

0.005 n = 140

0
0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1

Figure 9.5 Roundoff error in fixed-step Euler

(i.e., h < 1/5). For all h that satisfy this criterion, the method is stable. If h > 1/5, then any error δ in
the initial condition will result in a (1 − 10h)n δ contamination of the nth iterate. With this kind of error
magnification, we say that the method is unstable. Different methods have different h restrictions in order to
guarantee stability, and sometimes these restrictions force us to choose h much smaller than we would like.

9.1.3 The Backward Euler Method


To clarify this point about method stability, we examine the backward Euler method. The (forward) Euler
method is derived from a Taylor expansion of the solution y(t) about t = tn . If instead we work with the
approximation
y(tn+1 + h) ≈ y(tn+1 ) + y0 (tn+1 )h = y(tn+1 ) + f(tn+1 , y(tn+1 ))h
and set h = −hn = (tn − tn+1 ), then we get

y(tn ) ≈ y(tn+1 ) − hn f(tn+1 , y(tn+1 )).

Substituting yn for y(tn ) and yn+1 for y(tn+1 ), we are led to


8 CHAPTER 9. THE INITIAL VALUE PROBLEM

yn+1 = yn + hn f(tn+1 , yn+1 )

and, with repetition, the backward Euler framework:

n=0
Repeat:
Determine the step hn > 0.
tn+1 = tn + hn .
Let yn+1 solve F (z) = z − hn f(tn+1 , z) − yn = 0.
n= n+1
Like the Euler method, the backward Euler method is first order. However, the two techniques differ in a
very important aspect. Backward Euler is an implicit method because it defines yn+1 implicitly. For a simple
problem like y0 = ay this poses no difficulty:
1
yn+1 = yn + hn ayn+1 = yn .
1 − hn a
Observe that if a < 0, then the method is stable for all choices of positive step size. This should be contrasted
with the situation in the Euler setting, where |1 + ah| < 1 is required for stability.
Euler’s is an example of an explicit method, because yn+1 is defined explicity in terms of quantities already
computed. [e.g., yn , f(tn , yn )]. Implicit methods tend to have better stability properties than their explicit
counterparts. But there is an implementation penalty to be paid, because yn+1 is defined as a zero of a
nonlinear function. In backward Euler, yn+1 is a zero of F (z) = z − hn f(tn+1 , z). Fortunately, this does
not necessarily require the application of the Chapter 8 root finders. A simpler, more effective approach is
presented in §9.3.

9.1.4 Systems
We complete the discussion of IVP solving basics with comments about systems of differential equations. In
this case the unknown function y(t) is a vector of unknown functions:
 
z1 (t)
 
y(t) =  ...  .
zd (t)

(We name the component functions with a z instead of a y to avoid confusion with earlier notation.) In
this case, we are given an initial value for each component function and a recipe for its slope. This recipe
generally involves the value of all the component functions:
 0   
z1 (t) f1 (t, z1 (t), . . . , zd (t)) z1 (t0 ) = z10
 ..   .
..  ..
 .  =   . .
zd0 (t) fm (t, z1 (t), . . . , zd (t)) zd (t0 ) = zd0

In vector language, y0 (t) = f(t, y(t)), y(t0 ) = y0 , where the y’s are now column d-vectors. Here is a d = 2
example:
u0 (t) = 2u(t) − .01u(t)v(t)
, u(0) = u0 , v(0) = v0 .
v0 (t) = −v(t) + .01u(t)v(t)
It describes the density of rabbit and fox populations in a classical predator-prey model. The rate of change
of the rabbit density u(t) and the fox density v(t) depend on the current rabbit/fox densities.
Let’s see how the derivation of Euler’s method proceeds for a systems problem like this. We start with
a pair of time-honored Taylor expansions:

u(tn+1 ) ≈ u(tn ) + u0 (tn )hn = u(tn ) + hn (2u(tn ) − .01u(tn)v(tn ))


v(tn+1 ) ≈ v(tn ) + v0 (tn )hn = v(tn ) + hn (−v(tn ) + .01u(tn)v(tn ))
9.1. BASIC CONCEPTS 9

Here (as usual), tn+1 = tn + hn . With the definitions


   
un u(tn )
yn = ≈ = y(tn )
vn v(tn )
and    
2un − .01unvn 2u(tn ) − .01u(tn)v(tn )
fn = f(tn , yn ) = ≈ = f(tn , y(tn )),
−vn + .01unvn −v(tn ) + .01u(tn)v(tn )
we obtain he following vector implementation of the Euler method:
     
un+1 un 2un − .01unvn
= + hn .
vn+1 vn −vn + .01un vn
In full vector notation, this can be written as
yn+1 = yn + hn fn ,
which is exactly the same formula that we developed in the scalar case.
As we go through the next two sections presenting more sophisticated IVP solvers, we shall do so for
scalar (d = 1) problems, being mindful that all method-defining equations apply at the system level with no
modification.
Systems can arise in practice from the conversion of higher-order IVPs. In a kth order IVP, we seek a
function y(t) that satisfies

 y(t0 ) = y0

 (1)
 y(1) (t0 ) = y0
(k) (1) (k−1)
y (t) = f(t, y(t), y (t), . . . , y (t)) where ..

 .

 (k−1) (k−1)
y (t0 ) = y0
(1) (k−1)
and y0 , y0 , . . . , y0 are given initial values. Higher order IVPs can be solved through conversion to a
system of first-order IVPs. For example, to solve
v00 (t) = 2v(t) + v0 (t) sin(t), v(0) = α, v0 (0) = β,
we define z1 (t) = v(t) and z2 (t) = v0 (t). The problem then transforms to
z10 (t) = z2 (t)
, z1 (0) = α, z2 (0) = β.
z20 (t) = 2z1 (t) + z2 (t) sin(t)

Problems
P9.1.1 Produce a plot of the solution to
1
y0 (t) = −ty + , y(1) = 1
y2
across the interval [1, 2]. Use the Euler method.

P9.1.2 Compute an approximation to y(1) where


x00 (t) = (3 − sin(t))x0(t) + x(t)/(1 + [y(t)]2 ),

y0 (t) = − cos(t)y(t) − x0 (t)/(1 + t2 ),


x(0) = 3, x0 (0) = −1, and y(0) = 4. Use the forward Euler method with fixed step determined so that three significant digits of
accuracy are obtained. Hint: Define z(t) = x0 (t) and rewrite the recipe for x00 as a function of x, y, and z. This yields a d = 3
system.

P9.1.3 Plot the solutions to » – » –


−1 4 2
y0 (t) = y(t), y(0) =
−4 −1 −1
across the interval [0, 3].

P9.1.4 Consider the initial value problem


Ay0 (t) = By(t), y(0) = y0 ,
where A and B are given n-by-n matrices with A nonsingular. For fixed step size h, explain how the backwards Euler method
can be used to compute approximate solutions at t = kh, k = 1:100.
10 CHAPTER 9. THE INITIAL VALUE PROBLEM

9.2 The Runge-Kutta Methods


In an Euler step, we “extrapolate into the future” with only a single sampling of the slope function f(t, y).
The method has order 1 because its LTE goes to zero as h2 . Just as we moved beyond the trapezoidal
rule in Chapter 4, so we must now move beyond the Euler framework with more involved models of the
slope function. In the Runge-Kutta framework, we sample f at several judiciously chosen spots and use the
information to obtain yn+1 from yn with the highest possible order of accuracy.

9.2.1 Derivation
The Euler methods evaluate f once per step and have order 1. Let’s sample f twice per step and see if we
can obtain a second-order method. We arrange it so that the second evaluation depends on the first:

k1 = hf(tn , yn )
k2 = hf(tn + αh, yn + βk1 )
yn+1 = yn + ak1 + bk2 .
Our goal is to choose the parameters α, β, a, and b so that the LTE is O(h3 ). From the Taylor series we
have
h2
y(tn+1 ) = y(tn ) + y(1) (tn )h + y(2) (tn ) + O(h3 ).
2
Since

y(1) (tn ) = f

y(2) (tn ) = ft + fy f

where

f = f(tn , yn )
∂f(tn , yn )
ft =
∂t
∂f(tn , yn )
fy = ,
∂y

it follows that
h2
y(tn+1 ) = y(tn ) + fh + (ft + fy f) + O(h3 ). (9.2)
2
On the other hand,

k2 = hf(tn + αh, yn + βk1 ) = h f + αhft + βk1 fy + O(h2 )

and so
yn+1 = yn + ak1 + bk2 = yn + (a + b) fh + b (αft + βffy ) h2 + O(h3 ). (9.3)
For the LTE to be O(h3 ), the equation

y(tn+1 ) − yn+1 = O(h3 )

must hold. To accomplish this, we compare terms in (9.2) and (9.3) and require

a+b = 1
2bα = 1
2bβ = 1.
9.2. THE RUNGE-KUTTA METHODS 11

There are an infinite number of solutions to this system, the canonical one being a = b = 1/2 and α = β = 1.
With this choice the LTE is O(h3 ), and we obtain a second-order Runge-Kutta method:

k1 = hf(tn , yn )
k2 = hf(tn + h, yn + k1 )
yn+1 = yn + (k1 + k2 )/2 .
The actual expression for the LTE is given by
h3
LTE(RK2) = (ftt + 2ffty + f 2 fyy − 2ft fy − 2ffy2 ),
12
where the partials on the right are evaluated at some point in [tn , tn + h]. Notice that two f-evaluations are
required per step.
The most famous Runge-Kutta method is the classical fourth order method:
k1 = hf(tn , yn )
k2 = hf(tn + h2 , yn + 12 k1 )
k3 = hf(tn + h2 , yn + 12 k2 )
k4 = hf(tn + h, yn + k3 )
yn+1 = yn + 16 (k1 + 2k2 + 2k3 + k4 ) .
This can be derived using the same Taylor expansion technique illustrated previously. It requires four
f-evaluations per step.
The function RKStep can be used to carry out a Runge-Kutta step of prescribed order. Here is its
specification along with an abbreviated portion of the implementation:

function [tnew,ynew,fnew] = RKstep(f,tc,yc,fc,h,k)


% f is a handle that references a function of the form f(t,y)
% where t is a scalar and y is a column d-vector.
% yc is an approximate solution to y’(t) = f(t,y(t)) at t=tc.
% fc = f(tc,yc).
% h is the time step.
% k is the order of the Runge-Kutta method used, 1<=k<=5.
% tnew=tc+h, ynew is an approximate solution at t=tnew, and
% fnew = f(tnew,ynew).

if k==1
k1 = h*fc;
ynew = yc + k1;
elseif k==2
k1 = h*fc;
k2 = h*f(tc+(h),yc+(k1));
ynew = yc + (k1 + k2)/2;
elseif k==3
k1 = h*fc;
k2 = h*f(tc+(h/2),yc+(k1/2));
k3 = h*f(tc+h,yc-k1+2*k2);
ynew = yc + (k1 + 4*k2 + k3)/6;
elseif k==4
:
end
tnew = tc+h;
fnew = f(tnew,ynew);
12 CHAPTER 9. THE INITIAL VALUE PROBLEM

As can be imagined, symbolic algebra tools are useful in the derivation of such an involved sampling and
combination of f-values.

Problems
P9.2.1 The RKF45 method produces both a fourth order estimate and a fifth order estimate using six function evaluations:

k1 = hf (tn, yn )
h
k2 = hf (tn + 4
, yn + 14 k1 )
3h 3 9
k3 = hf (tn + 8
, yn + k
32 1
+ k )
32 2
12h 1932 7200 7296
k4 = hf (tn + 13
, yn + k
2197 1
− k
2197 2
+ k )
2197 3
439 3680 845
k5 = hf (tn + h, yn + k
216 1
− 8k2 + k
513 3
− k )
4104 4
h 8 3544 1859 11
k6 = hf (tn + 2
, yn − k
27 1
+ 2k2 − k
2565 3
+ k
4104 4
− k )
40 5
25 1408 2197 1
yn+1 = yn + k
216 1
+ k
2565 3
+ k
4104 4
− k
5 5
16 6656 28561 9 2
zn+1 = yn + k
135 1
+ k
12825 3
+ k
56430 4
− k
50 5
+ k
55 6
.
Write a script that discovers which of yn+1 and zn+1 is the fourth order estimate and which is the fifth order estimate.

9.2.2 Implementation
Runge-Kutta steps can obviously be repeated, and if we keep the step size fixed, then we obtain the following
implementation:

function [tvals,yvals] = FixedRK(f,t0,y0,h,k,n)


% [tvals,yvals] = FixedRK(fname,t0,y0,h,k,n)
% Produces approximate solution to the initial value problem
%
% y’(t) = f(t,y(t)) y(t0) = y0
%
% using a strategy that is based upon a k-th order Runge-Kutta method. Stepsize
% is fixed. f is a handle that references the function f, t0 is the initial time,
% y0 is the initial condition vector, h is the stepsize, k is the order of
% method (1<=k<=5), and n is the number of steps to be taken,

% tvals(j) = t0 + (j-1)h, j=1:n+1


% yvals(j,:) = approximate solution at t = tvals(j), j=1:n+1

tc = t0; tvals = tc;


yc = y0; yvals = yc’;
fc = f(tc,yc);
for j=1:n
[tc,yc,fc] = RKstep(f,tc,yc,fc,h,k);
yvals = [yvals; yc’];
tvals = [tvals tc];
end
The function file ShowRK can be used to illustrate the performance of the Runge-Kutta methods on the
IVP y0 = −y, y(0) = 1. The results are reported in Figure 9.6. All the derivatives of f are “nice,” which
means that if we increase the order and keep the step size fixed, then the errors should diminish by a factor
of h. Thus for n = 500, h = 1/100 and we find that the error in the kth order method is about 100−k .
Do not conclude from the example that higher-order methods are necessarily more accurate. If the higher
derivatives of the solution are badly behaved, then it may well be the case that a lower-order method gives
more accurate results. One must also be mindful of the number of f-evaluations that are required to purchase
a given level of accuracy. The situation is analogous to what we found in the quadrature unit. Of course,
the best situation is for the IVP software to handle the selection of method and step.
9.2. THE RUNGE-KUTTA METHODS 13

Runge−Kutta on y’(t) = −y(t), y(0) = 1, 0<=t<=5, h = 5/n


0
10

−2
10
k=1

−4
10

Maximum absolute error


k=2
−6
10

−8
10 k=3

−10
10
k=4

−12
10

k=5
−14
10
0 100 200 300 400 500 600
n (Number of Steps)

Figure 9.6 Runge-Kutta error

Problems
P9.2.2 For k = 1:5, how many f -evaluations does the kth-order Runge-Kutta method require to solve y0 (t) = −y(t), y(0) = 1
with error ≤ 10−6 across [0, 1]?

9.2.3 The Matlab IVP Solving Tools


Matlab supplies a number of techniques for solving initial value problems. We start with ode23, which is
based on a pair of second- and third-order Runge-Kutta methods. With two methods for predicting yn+1 ,
it uses the discrepancy of the predictions to determine heuristically whether the current step size is “safe”
with respect to the given tolerances.
Both codes can be used to solve systems, and to illustrate how they are typically used, we apply them
to the following initial value problem:

x(t)
ẍ(t) = − x(0) = .4 ẋ(0) = 0
(x(t)2 + y(t)2 )3/2

y(t)
ÿ(t) = − y(0) = 0 ẏ(0) = 2 .
(x(t) + y(t)2 )3/2
2

These are Newton’s equations of motion for the two-body problem. As t ranges from 0 to 2π, (x(t), y(t))
defines an ellipse.
Both ode23 and ode45 require that we put this problem in the standard y0 = f(t, y) form. To that end,
we define u1 (t) = x(t), u2 (t) = ẋ(t), u3 (t) = y(t), u4 (t) = ẏ(t). The given IVP problem transforms to

u̇1 (t) = u2 (t) u1 (0) = .4


u̇2 (t) = −u1 (t)/(u1 (t)2 + u3 (t)2 )3/2 u2 (0) = 0
u̇3 (t) = u4 (t) u3 (0) = 0
u̇4 (t) = −u3 (t)/(u1 (t)2 + u3 (t)2 )3/2 u4 (0) = 2 .

We then write the following function, which returns the derivative of the u vector:
14 CHAPTER 9. THE INITIAL VALUE PROBLEM

function up = Kepler(t,u)
% up = Kepler(t,u)
% t (time) is a scalar and u is a 4-vector whose components satisfy
%
% u(1) = x(t) u(2) = (d/dt)x(t)
% u(3) = y(t) u(4) = (d/dt)y(t)
%
% where (x(t),y(t)) are the equations of motion in the 2-body problem.
%
% up is a 4-vector that is the derivative of u at time t.

r3 = (u(1)^2 + u(3)^2)^1.5;
up = [ u(2) ;...
-u(1)/r3 ;...
u(4) ;...
-u(3)/r3] ;

With this function available, we can call ode23 and plot various results:

tInitial = 0;
tFinal = 2*pi;
uInitial = [ .4; 0 ; 0 ; 2];
tSpan = [tInitial tFinal];
[t, u] = ode23(@Kepler, tSpan, uInitial);

ode23 requires that we pass the name of the “slope function”, the span of integration, and the initial
condition vector. The slope function must be of the form f(t,y) where t is a scalar and y is a vector. It
must return a column vector. In this call the tSpan vector simply specifies the initial and final times. The
output produced is a column vector of times t and a matrix u of solution snapshots. If n = length(t) then
(a) t(0) = tInitial, t(n) = tFinal, and u(k,:) is an approximation to the solution at time t(k). The
time step lengths and (therefore their number) is determined by the default error tolerance: Reltol = 10−3
and AbsTol = 10−6 . Basically, ode23 integrates from tInitial to tFinal “as quick as possible” subject to
these two tolerances. We can display the orbit via
plot(u(:,1),u(:,3))
i.e., by plotting the computed y-values against the computed x-values. (See Figure 9.7.) From the output
we display in Figure 9.8 the step lengths with
plot(t(2:length(t)),diff(t))

Kepler Problem: ode23 with Default Tolerances

0.6

0.4

0.2

−0.2

−0.4

−0.6

−0.8
−1.6 −1.4 −1.2 −1 −0.8 −0.6 −0.4 −0.2 0 0.2 0.4
Number of Steps = 53

Figure 9.7 Generation of the Orbit via ode23


9.2. THE RUNGE-KUTTA METHODS 15

Kepler Problem: ode23 with Default Tolerances


0.45

0.4

0.35

0.3

Step Length
0.25

0.2

0.15

0.1

0.05

0
0 1 2 3 4 5 6 7
t

Figure 9.8 Step length with ode23 default tolerances


x(t) y(t)
0.5 1

0
0.5

−0.5
0
−1

−0.5
−1.5

−2 −1
0 2 4 6 8 0 2 4 6 8

x’(t) y’(t)
1 2

1.5
0.5
1

0 0.5

0
−0.5
−0.5

−1 −1
0 2 4 6 8 0 2 4 6 8

Figure 9.9 Component solutions

and in Figure 9.9 the component solutions via


subplot(2,2,1), plot(t,u(:,1)), title(’x(t)’)
subplot(2,2,2), plot(t,u(:,2)), title(’y(t)’)
subplot(2,2,3), plot(t,u(:,3)), title(’x’’(t)’)
subplot(2,2,4), plot(t,u(:,4)), title(’y’’(t)’)
The function ode23 can also be asked to return the solution at specified times. Here is a script that generates
20 solution snapshots and does a spline fit of the output
tSpan = linspace(tInitial,tFinal,20);
[t, u] = ode23(’Kepler’, tSpan, uInitial);
xvals = spline(t,u(:,1),linspace(0,2*pi));
yvals = spline(t,u(:,3),linspace(0,2*pi));
plot(xvals,yvals,u(:,1),u(:,3),’o’)
(See Figure 9.10.)
Using the function odeset it is possible to specify various parameters that are used by ode23. For
example,

tSpan = [tInitial tFinal];


options = odeset(’AbsTol’,.00000001,’RelTol’,.000001,’stats’,’on’);
disp(sprintf(’\n Stats for ode23 Call:\n’))
[t, u] = ode23(’Kepler’, tSpan, uInitial,options);
16 CHAPTER 9. THE INITIAL VALUE PROBLEM

Kepler Problem: ode23 with Specified Output Times


0.8

0.6

0.4

0.2

0 Spline Fit
ode23 Output

−0.2

−0.4

−0.6

−0.8
−1.6 −1.4 −1.2 −1 −0.8 −0.6 −0.4 −0.2 0 0.2 0.4

Figure 9.10 Specified output times and spline fit

overrides the default tolerance for relative error and absolute error and activates the ’stats’ option. As
expected, the time steps are now shorter as shown in Figure 9.11.
−6 −8
Kepler Problem: ode23 with RelTol = 10 and AbsTol = 10
0.04

0.035

0.03

0.025
Step Length

0.02

0.015

0.01

0.005

0
0 1 2 3 4 5 6 7
t

Figure 9.11 ode23 Timesteps with more stringent tolerances

The “cost” statistics associated with the call are displayed in the command window:
517 successful steps
0 failed attempts
1552 function evaluations
0 partial derivatives
0 LU decompositions
0 solutions of linear systems
Sometimes a higher order method can achieve the same accuracy with fewer function evaluations. To
illustrate this we apply ode45 to the same problem:
tSpan = [tInitial tFinal];
options = odeset(’AbsTol’,.00000001,’RelTol’,.000001,’stats’,’on’);
disp(sprintf(’\n Stats for ode45 Call:\n’))
[t, u] = ode45(’Kepler’, tSpan, uInitial,options);
9.2. THE RUNGE-KUTTA METHODS 17

ode45 is just like ode23 except that it uses a mix of 4th and 5th order Runge-Kutta methods. For this
problem ode45 can take decidedly longer time steps in the “high curvature” regions of the orbit. (Compare
Figure 9.11 and Figure 9.12.) The output statistics reveal that just 337 function evaluations are required.
−6 −8
Kepler Problem: ode45 with RelTol = 10 and AbsTol = 10
0.08

0.07

0.06

0.05
Step Length

0.04

0.03

0.02

0.01

0
0 1 2 3 4 5 6 7
t

Figure 9.12 Stepsize with ode45

Problems
P9.2.3 This is about ode23 vs ode45. Suppose it takes T1 seconds to execute [t,y] = ode23(@MyF,[0,4],y0) and T2 seconds
to execute [t,y] = ode45(@MyF,[0,8],y0) What factors determine T2 /T1?

P9.2.4 Our goal is to produce a plot of an approximate solution to the boundary value problem
y00 (t) = f (t, y(t), y0 (t)), y(a) = α, y(b) = β.
Assume the availability of a Matlab function f(t,y,yp). (a) Write a function g(mu) that returns an estimate of y(b) where
y(t) solves
y00 (t) = f (t, y(t), y0 (t)), y(a) = α, y0 (a) = µ.
Use ode23 and define the function that must be passed to it. (b) How could µ∗ be computed so that g(µ∗ ) = β? (c) Finally,
how could a plot of the boundary value problem solution across [a, b] be obtained?

P9.2.5 Consider the following initial value problem

Aẏ = By + u(t), y(0) = y0 ,


where A ∈ IRn×n is nonsingular, B ∈ IRn×n, and u(t) ∈ IRn . Making effective use of ode45 with default tolerances, write a
Matlab fragment that assigns to yFinal an estimate of y(tf inal ). Write out completely the function that your script passes to
ode45. Assume that A, B, y0, and tfinal are given and that u.m implements u(t).

P9.2.6 Consider the following IVP:


µ∗ (x(t) + µ) µ( x(t) − µ∗ )
ẍ(t) = 2ẏ(t) + x(t) − − , x(0) = 1.2 ẋ(0) = 0,
r13 r23

µ∗ y(t) µy(t)
ÿ(t) = −2ẋ(t) + y(t) − − , y(0) = 0 ẏ(0) = −1.0493575,
r13 r23
where µ = 1/82.45, µ∗ = 1 − µ, and
q
r1 = ((x(t) + µ)2 + y(t)2
q
r2 = ((x(t) − µ∗ )2 + y(t)2

It describes the orbit of a spacecraft that starts behind the Moon (located at (1 − µ, 0)), swings by the Earth (located at
(−µ, 0)), does a large loop, and returns to the vicinity of the Earth before returning to its initial position behind the Moon at
time T0 = 6.19216933. Here, µ = 1/82.45.
(a) Apply ode45 with tinitial = 0, tf inal = T0 , and tol = 10−6 . Plot the orbit twice, once with the default “pen” and once
with ’.’ so that you can see how the time step varies. (b) Using the output from the ode45 call in part (a), plot the distance
18 CHAPTER 9. THE INITIAL VALUE PROBLEM

of the spacecraft to Earth as a function of time across [0, T0 ]. Use spline to fit the distance “snapshots.” To within a mile,
how close does the spacecraft get to the Earth’s surface? Assume that the Earth is a sphere of radius 4000 miles and that the
Earth-Moon separation is 238,000 miles. Use fmin with an appropriate spline for the objective function. Note that the IVP
is scaled so that one unit of distance is 238,000 miles. (c) Repeat Part (a) with ode23. (d) Apply ode45 with tinitial = 0,
tf inal = 2T0, and tol = 10−6 , but change ẏ(0) to and ẏ(0) = −.8. Plot the orbit. For a little more insight into what happens,
repeat with tf inal = 8 ∗ T0. (e) To the nearest minute, compute how long the spacecraft is hidden to an observer on earth
as it swings behind the Moon during its orbit. Assume that the observer is at (−µ, 0) and that the Moon has diameter 2160
miles. Make intelligent use of fzero. (f ) Find t∗ in the interval [0, T0 /2] so that at time t∗ , the spacecraft is equidistant from
the Moon and the Earth.

9.3 The Adams Methods


From the fundamental theorem of calculus, we have
Z tn+1
y(tn+1 ) = y(tn ) + y0 (t)dt,
tn

and so Z tn+1
y(tn+1 ) = y(tn ) + f(t, y(t))dt.
tn
The Adams methods are based on the idea of replacing the integrand with a polynomial that interpolates
f(t, y) at selected solution points (tj , yj ). The kth order Adams-Bashforth method is explicit and uses the
current point (tn , yn ) and k − 1 “historical” points. The kth order Adams-Moulton method is implicit and
uses the future point (tn+1 , yn+1 ), the current point, and k − 2 historical points. The implementation and
properties of these two IVP solution frameworks are presented in this section.

9.3.1 Derivation of the Adams-Bashforth Methods


In the kth order Adams-Bashforth (AB) method, we set
Z tn+1
yn+1 = yn + pk−1 (t)dt, (9.4)
tn

where pk−1 (t) interpolates f(t, y) at (tn−j , yn−j ), j = 0:k − 1. We are concerned with the first five members
of this family:
Order Interpolant AB Interpolation Points
1st constant (tn , fn )
2nd linear (tn , fn ), (tn−1, fn−1 )
3rd quadratic (tn , fn ), (tn−1, fn−1 ), (tn−2 , fn−2 )
4th cubic (tn , fn ), (tn−1, fn−1 ), (tn−2 , fn−2 ), (tn−3 , fn−3 )
5th quartic (tn , fn ), (tn−1, fn−1 ), (tn−2 , fn−2 ), (tn−3 , fn−3 ), (tn−3, fn−3 )
If k = 1, then the one-point Newton-Cotes rule is applied and we get
yn+1 = yn + hn f(tn , yn ) hn = tn+1 − tn .
Thus the first-order AB method is the Euler method.
In the second-order Adams-Bashforth method, we set
fn − fn−1
pk−1 (t) = fn−1 + (t − tn−1 )
hn−1
in (9.4). This is the linear interpolant of (tn−1 , fn−1 ) and (tn , fn ), and we obtain
Z tn+1 Z tn+1  
fn − fn−1
f(t, y(t))dt ≈ fn−1 + (t − tn−1 ) dt
tn tn hn−1

 
hn hn + 2hn−1 hn
= fn − fn−1 .
2 hn−1 hn−1
9.3. THE ADAMS METHODS 19

If hn = hn−1 = h, then from (9.4)


h
(3fn − fn−1 ) .
yn+1 = yn +
2
The derivation of higher-order AB methods is analogous. A table of the first five Adams-Bashforth
methods along with their respective local truncation errors is given in Figure 9.13. The derivation of the

Order Step LTE


2
1 yn+1 = yn + hfn h y(2) (η)
2
2 yn+1 = yn + h (3fn − fn−1 ) 5h3 y(3) (η)
2 12
3 yn+1 = yn + h (23fn − 16fn−1 + 5fn−2 ) 3h4 y(4) (η)
12 8
4 yn+1 = yn + h (55fn − 59fn−1 + 37fn−2 − 9fn−3 ) 251h5 y(5) (η)
24 720
5 yn+1 = yn + h (1901fn − 2774fn−1 + 2616fn−2 − 1274fn−3 + 251fn−4 ) 95h6 y(6) (η)
720 288
Figure 9.13 Adams-Bashforth family

LTEs for the AB methods is a straightforward computation that involves the Newton-Cotes error:
Z tn+1
y(tn+1 ) − yn = (f(t, yn (t)) − pk−1 (t))dt.
tn

9.3.2 Implementation
To facilitate experimentation with the AB method, here is a function that can carry out any of the methods
specified in Figure 9.13:

function [tnew,ynew,fnew] = ABstep(f,tc,yc,fvals,h,k)


% f is a handle that references a function of the form f(t,y)
% where t is a scalar and y is a column d-vector.
%
% yc is an approximate solution to y’(t) = f(t,y(t)) at t=tc.
%
% fvals is an d-by-k matrix where fvals(:,i) is an approximation
% to f(t,y) at t = tc +(1-i)h, i=1:k
%
% h = the time step.
% k = the order of the AB method used, 1<=k<=5.
% tnew = tc+h.
% ynew = an approximate solution at t=tnew.
% fnew = f(tnew,ynew).

if k==1, ynew = yc + h*fvals;


elseif k==2, ynew = yc + (h/2)*(fvals*[3;-1]);
elseif k==3, ynew = yc + (h/12)*(fvals*[23;-16;5]);
elseif k==4, ynew = yc + (h/24)*(fvals*[55;-59;37;-9]);
elseif k==5, ynew = yc + (h/720)*(fvals*[1901;-2774;2616;-1274;251]);
end
tnew = tc+h;
fnew = f(tnew,ynew);
20 CHAPTER 9. THE INITIAL VALUE PROBLEM

In the systems case, fval is a matrix and ynew is yc plus a matrix-vector product.
Note that k snapshots of f(t, y) are required, and this is why Adams methods are called multistep
methods. Because of this there is a “start-up” issue with the Adams-Bashforth method: How do we perform
the first step when there is no “history”? There are several approaches to this, and care must be taken to
ensure that the accuracy of the generated start-up values is consistent with the overall accuracy aims. For
a kth order Adams framework we use a kth order Runge-Kutta method, to get fj = f(tj , yj ), j = 1:k − 1.
See the function ABStart. Using ABStart we are able to formulate a fixed-step Adams-Bashforth solver:
function [tvals,yvals] = FixedAB(f,t0,y0,h,k,n)
% Produces an approximate solution to the initial value problem
% y’(t) = f(t,y(t)), y(t0) = y0 using a strategy that is based upon a k-th order
% Adams-Bashforth method. Stepsize is fixed.
%
% f = handle that references the function f.
% t0 = initial time.
% y0 = initial condition vector.
% h = stepsize.
% k = order of method. (1<=k<=5).
% n = number of steps to be taken,
%
% tvals(j) = t0 + (j-1)h, j=1:n+1
% yvals(j,:) = approximate solution at t = tvals(j), j=1:n+1

[tvals,yvals,fvals] = ABStart(f,t0,y0,h,k);
tc = tvals(k);
yc = yvals(k,:)’;
fc = fvals(:,k);

for j=k:n
% Take a step and then update.
[tc,yc,fc] = ABstep(f,tc,yc,fvals,h,k);
tvals = [tvals tc];
yvals = [yvals; yc’];
fvals = [fc fvals(:,1:k-1)];
end
If we apply this algorithm to the model problem, y0 = −y, y(0) = 1. (See Figure 9.14.) Notice that for the
kth-order method, the error goes to zero as hk , where h = 1/n.
Adams−Bashforth on y’(t) = −y(t), y(0) = 1, 0<=t<=5, h = 5/n

−2
10

k=1

−4
10

k=2
Maximum absolute error

−6
10

k=3

−8
10

k=4

−10
10

k=5

−12
10
0 100 200 300 400 500 600
n (Number of Steps)

Figure 9.14 kth order Adams-Bashforth error


9.3. THE ADAMS METHODS 21

9.3.3 The Adams-Moulton Methods


The kth order Adams-Moulton (AM) method is just like the kth-order Adams-Bashforth method, but the
points at which we interpolate the integrand in
Z tn+1
yn+1 = yn + pk−1(t)dt
tn

are “shifted” one time step into the future. In particular, the kth-order Adams-Moulton method uses a
degree k − 1 interpolant of the points (tn+1−j , fn+1−j ), j = 0:k − 1:

Order Interpolant AM Interpolation Points


1st constant (tn+1 , fn+1 )
2nd linear (tn+1 , fn+1 ), (tn , fn )
3rd quadratic (tn+1 , fn+1 ), (tn , fn ), (tn−1 , fn−1 )
4th cubic (tn+1 , fn+1 ), (tn , fn ), (tn−1 , fn−1 ), (tn−2, fn−2 )
5th quartic (tn+1 , fn+1 ), (tn , fn ), (tn−1 , fn−1 ), (tn−2, fn−2 ), (tn−3 , fn−3 )

For example, in the second-order Adams-Moulton method we set

fn+1 − fn
pk−1 (t) = fn + (t − tn ),
hn

Order Step LTE


2
1 yn+1 = yn + hf(tn+1 , yn+1 ) − h y(2) (η)
2
3
2 yn+1 = yn + h (f(tn+1 , yn+1 ) + fn ) h
− y(3) (η)
2 12
4
3 yn+1 = yn + h (5f(tn+1 , yn+1 ) + 8fn − fn−1 ) − h y(4) (η)
12 24
5
4 yn+1 = yn + h (9f(tn+1 , yn+1 ) + 19fn − 5fn−1 + fn−2 ) − 19h y(5) (η)
24 720
6
5 yn+1 = yn + h (251f(tn+1 , yn+1 ) + 646fn − 264fn−1 + 106fn−2 − 19fn−3 ) − 3h y(6) (η)
720 160

Figure 9.15 The Adams-Moulton methods

the linear interpolant of (tn , fn ) and (tn+1 , fn+1 ). We then obtain the approximation
Z tn+1 Z tn+1  
fn+1 − fn hn
f(t, y(t))dt ≈ fn + (t − tn ) = (fn + fn+1 ),
tn tn hn 2

and thus

hn
yn+1 = yn + (f(t, yn+1 ) + fn ).
2
As in the backward Euler method, which is just the first order Adams-Moulton method, yn+1 is specified
implicitly through a nonlinear equation. The higher-order Adams-Moulton methods are derived similarly,
and in Figure 9.15 we specify the first five members in the family.
The LTE coefficient for any AM method is slightly smaller than the LTE coefficients for the corresponding
AB method. Analogous to ABstep, we have
22 CHAPTER 9. THE INITIAL VALUE PROBLEM

function [tnew,ynew,fnew] = AMstep(f,tc,yc,fvals,h,k)


% Single step of the kth order Adams-Moulton method.
%
% f is a handle that references a function of the form f(t,y)
% where t is a scalar and y is a column d-vector.
%
% yc is an approximate solution to y’(t) = f(t,y(t)) at t=tc.
%
% fvals is an d-by-k matrix where fvals(:,i) is an approximation
% to f(t,y) at t = tc +(2-i)h, i=1:k.
%
% h is the time step.
%
% k is the order of the AM method used, 1<=k<=5.
%
% tnew=tc+h
% ynew is an approximate solution at t=tnew
% fnew = f(tnew,ynew).

if k==1, ynew = yc + h*fvals;


elseif k==2, ynew = yc + (h/2)*(fvals*[1;1]);
elseif k==3, ynew = yc + (h/12)*(fvals*[5;8;-1]);
elseif k==4, ynew = yc + (h/24)*(fvals*[9;19;-5;1]);
elseif k==5, ynew = yc + (h/720)*(fvals*[251;646;-264;106;-19]);
end
tnew = tc+h;
fnew = f(tnew,ynew);

We could discuss methods for the solution of the nonlinear F (z) = 0 that defines yn+1 . However, we have
other plans for the Adams-Moulton methods that circumvent this problem.

9.3.4 The Predictor-Corrector Idea


A very important framework for solving IVPs results when we couple an Adams-Bashforth method with an
Adams-Moulton method of the same order. The idea is to predict yn+1 using an Adams-Bashforth method
and then to correct its value using the corresponding Adams-Moulton method. In the second-order case,
AB2 gives

(P ) h
yn+1 = yn + (3fn − fn−1 ),
2
which then is used in the right-hand side of the AM2 recipe to render

(C) h (P )

yn+1 = yn + f(tn+1 , yn+1 ) + fn .
2
For general order we have developed a function
[tnew,yPred,fPred,yCorr,fCorr] = PCstep(f,tc,yc,fvals,h,k)
that implements this idea. It involves a simple combination of ABStep and AMStep:

[tnew,yPred,fPred] = ABstep(f,tc,yc,fvals,h,k);
[tnew,yCorr,fCorr] = AMstep(f,tc,yc,[fPred fvals(:,1:k-1)],h,k);
9.3. THE ADAMS METHODS 23

The repeated application of this function defines the fixed-step predictor-corrector framework:

function [tvals,yvals] = FixedPC(f,t0,y0,h,k,n)


% Produces an approximate solution to the initial value problem
% y’(t) = f(t,y(t)), y(t0) = y0 using a strategy that is based upon a k-th order
% Adams Predictor-Corrector framework. Stepsize is fixed.
%
% f = handle that references the function f.
% t0 = initial time.
% y0 = initial condition vector.
% h = stepsize.
% k = order of method. (1<=k<=5).
% n = number of steps to be taken,
%
% tvals(j) = t0 + (j-1)h, j=1:n+1
% yvals(j,:) = approximate solution at t = tvals(j), j=1:n+1

[tvals,yvals,fvals] = StartAB(f,t0,y0,h,k);
tc = tvals(k);
yc = yvals(:,k)’;
fc = fvals(:,k);

for j=k:n
% Take a step and then update.
[tc,yPred,fPred,yc,fc] = PCstep(f,tc,yc,fvals,h,k);
tvals = [tvals tc];
yvals = [yvals; yc’];
fvals = [fc fvals(:,1:k-1)];
end

The error associated with this method when applied to the model problem is given in Figure 9.16 on the
next page.

Adams PC on y’(t) = −y(t), y(0) = 1, 0<=t<=5, h = 5/n

−2
10
k=1

−4
10

k=2
Maximum absolute error

−6
10

−8
10 k=3

−10
10 k=4

−12
10
k=5

−14
10
0 100 200 300 400 500 600
n (Number of Steps)

Figure 9.16 kth Order predictor-corrector error

Problems

P9.3.1 Write functions


24 CHAPTER 9. THE INITIAL VALUE PROBLEM

[tvals,yvals] = AFixedAB(A,t0,y0,h,k,n)
[tvals,yvals] = AFixedAM(A,t0,y0,h,k,n)

that can be used to solve the IVP y0 (t) = Ay(t), y(t0 ) = y0 , where A is a d-by-d matrix. In AFixedAM a linear system will have
to be solved at each step. Get the factorization “out of the loop.”

P9.3.2 Use FixedAB and FixedPC to solve the IVP described in problem P9.2.3. Explore the connections between step size,
order, and the number of required function evaluations.

9.3.5 Stepsize Control


The idea behind error estimation in adaptive quadrature is to compute the integral in question in two ways,
and then accept or reject the better estimate based on the observed discrepancies. The predictor-corrector
(C) (C) (P )
framework presents us with a similar opportunity. The quality of yn+1 can be estimated from |yn+1 − yn+1 |.
If the error is too large, we can reduce the step. If the error is too small, then we can lengthen the step.
Properly handled, we can use this mechanism to integrate the IVP across the interval of interest with steps
that are as long as possible given a prescribed error tolerance. In this way we can compute the required
solution, more or less minimizing the number of f evaluations. The Matlab IVP solvers ode23 and ode45
are Runge-Kutta based and do just that. We develop a second-order adaptive step solver based on the
second-order AB and AM methods.
(P ) (C)
Do we accept y(C) as our chosen yn+1 ? If ∆ = |yn+1 − yn+1 | is small, then our intuition tells us that
(C)
yn+1 is probably fairly good and worth accepting as our approximation to y(tn+1 ). If not, there are two
(C)
possibilities. We could refine yn+1 through repeated application of the AM2 formula:
(C)
yn+1 = yn+1
Repeat:
yn+1 = yn + h2 (f(tn+1 , yn+1 ) + fn )
A reasonable termination criterion might be to quit as soon as two successive iterates differ by a small amount.
The goal of the iteration is to produce a solution to the AM2 equation. Alternatively, we could halve h and
try another predict/correct step [i.e., produce an estimate yn+1 of y(tn + h/2)]. The latter approach is more
constructive because it addresses the primary reason for discrepancy between the predicted and corrected
value: an overly long step h.
To implement a practical step size control process, we need to develop a heuristic for estimating the error
(c) (P )
in yn+1 based on the discrepancy between it and yn+1 . The idea is to manipulate the LTE expressions

(P ) 5 3 (3)
y(tn+1 ) = yn+1 + h y (η1 ), η1 ∈ [tn , tn + h]
12
(C) 1
y(tn+1 ) = yn+1 − h3 y(3) (η2 ), η2 ∈ [tn , tn + h]
12
We make the assumption that y(3) does not vary much across [tn , tn + h]. Subtracting the first equation
from the second leads to approximation
(C) (P ) 1 3 (3)
|yn+1 − yn+1 | ≈ h |y (η)|, η ∈ [tn , tn + h]
2
and so
(C) 1 (C) (P )
|yn+1 − y(tn+1 )| ≈
|y − yn+1 |.
6 n+1
This leads to the following framework for a second-order predictor-corrector scheme:

yn+1 = yn + h
(P )
2 (3f

n − fn−1 )

yn+1 = yn + h
(C) (P )
2 f(t n+1 , yn+1 + fn

(C) (P )
 = 61 |yn+1 − yn+1 |
9.3. THE ADAMS METHODS 25

If  is too big, then


reduce h and try again.
Else if  is about right, then
(C)
set yn+1 = yn+1 and keep h.
Else if  is too small, then
(C)
set yn+1 = yn+1 and increase h.
The definitions of “too big,” “about right,” and “too small” are central. Here is one approach. Suppose we
want the global error in the solution snapshots across [t0 , tmax ] to be less than δ. If it takes nmax steps to
integrate across [t0 , tmax ], then we can heuristically guarantee this if
nX
max

LTEn ≤ δ.
n=1

Thus if hn is the length of the nth step, and


hn δ
|LTEn | ≤ ,
tmax − t0
then
nX
max nX
max
hn δ
LTEn ≤ ≤ δ.
n=1 n=1
tmax − t0
This tells us when to accept a step. But if the estimated LTE is considerably smaller than the threshold,
say
1 δh
≤ ,
10 tmax − t0
then it might be worth doubling h.
If the  is too big, then our strategy is to halve h. But to carry out the predictor step with this step size,
we need f(tn − h/2, yn−1/2) where yn−1/2 is an estimate of y(tn − h/2). “Missing” values in in this setting
can be generated by interpolation or by using (for example) an appropriate Runge-Kutta estimate.
We mention that the Matlab IVP solver ode113 implements an Adams-Bashforth-Moulton predictor-
corrector framework.
Problems
(C)
P9.3.3 Derive an estimate for |yn+1 − y(tn+1 )| for the third-, fourth- and fifth-order predictor-corrector pairs.

M-Files and References

Script Files
ShowTraj Shows family of solutions.
ShowEuler Illustrates Euler method.
ShowFixedEuler Plots error in fixed step Euler for y’=y, y(0)=1.
ShowTrunc Shows effect of truncation error.
EulerRoundoff Illustrates Euler in three-digit floating point.
ShowAB Illustrates FixedAB.
ShowPC Illustrates FixedPC.
ShowRK Illustrates FixedRK.
ShowMatIVPTools Illustrates ode23 and ode45 on a system.
26 CHAPTER 9. THE INITIAL VALUE PROBLEM

Function Files
FixedEuler Fixed step Euler method.
ABStart Gets starting values for Adams methods.
ABStep Adams-Bashforth step (order <= 5).
FixedAB Fixed step size Adams-Bashforth.
AMStep Adams-Moulton step (order <= 5).
PCStep AB-AM predictor-corrector Step (order <= 5).
FixedPC Fixed stepsize AB-AM predictor-corrector.
RKStep Runge-Kutta step (order <= 5).
FixedRK Fixed step size Runge-Kutta.
Kepler For solving two-body IVP.
f1 The f function for the model problem y’=y.

References
C.W. Gear (1971). Numerical Initial Value Problems in Ordinary Differential Equations, Prentice Hall,
Englewood Cliffs, NJ.
J. Lambert (1973). Computational Methods in Ordinary Differential Equations, John Wiley, New York.
J. Ortega and W. Poole (1981). An Introduction to Numerical Methods for Differential Equations, Pitman,
Marshfield, MA.
L. Shampine and M. Gordon (1975). Computer Solution of Ordinary Differential Equations: The Initial
Value Problem, Freeman, San Francisco.
Chapter 1 M-Files
M-File Home

Script File Index

SineTable Prints a short table of sine evaluations.


SinePlot Displays a sequence of sin(x) plots.
ExpPlot Plots exp(x) and an approximation to exp(x).
TangentPlot Plots tan(x).
SineAndCosPlot Superimposes plots of sin(x) and cos(x).
Polygons Displays nine regular polygons, one per window.
SumOfSines Displays the sum of four sine functions.
SumOfSines2 Displays a pair of sum-of-sine functions.
UpDown Sample core exploratory environment.
RunUpDown Framework for running UpDown.
Histograms Displays the distribution of rand and randn.
Clouds Displays 2-dimensional rand and randn.
Dice Histogram of 1000 dice rolls.
Darts Monte Carlo computation of pi.
Smooth Polygon smoothing.
Stirling Relative and absolute error in Stirling formula.
ExpTaylor Plots relative error in Taylor approximation to exp(x).
Zoom Roundoff in the expansion of (x-1)^ 6.
FpFacts Examines precision, overflow, and underflow.
TestMyExp Examines MyExp1 , MyExp2 , MyExp3 , and MyExp4.
TestDerivative Applies Derivative to Sin10.
Euler Three-digit arithmetic sum of 1 + 1/2 + ... + 1/n.
ShowPadeArray Tests the function PadeArray.
ShowFonts Illustrates how to use fonts.
ShowSymbols Shows how to generate math symbols.
ShowGreek Shows how to generate Greek letters.
ShowText Shows how to align with text.
ShowLineWidth Shows how vary line width in a plot.
ShowAxes Shows how to set tick marks on axes.
ShowLegend Shows how to add a legend to a plot.
ShowColor Shows how to use built-in colors and user-defined colors.
Function File Index

MyExpF For-loop Taylor approximation to exp(x).


MyExp1 Vectorized version of MyExpF.
MyExp2 Better vectorized version of MyExpF.
MyExpW While-loop Taylor approximation to exp(x).
MyExp3 Vectorized version of MyExpW.
MyExp4 Better vectorized version of MyExpW.
Derivative Numerical differentiation.
Sin10 sin(10x).
Represent Sets up 3-digit arithmetic representation.
Convert Converts 3-digit representation to float.
Float Simulates 3-digit arithmetic.
Pretty Pretty prints a 3-digit representation.
PadeArray Builds a cell array of Pade coefficients.
PadeCoeff Generates Pade approximant coefficients

SineTable
% Script File: SineTable
% Prints a short table of sine evaluations.

clc
n = 21;
x = linspace(0,1,n);
y = sin(2*pi*x);
disp(' ')
disp(' k x(k) sin(x(k))')
disp('------------------------')
for k=1:21
degrees = (k-1)*360/(n-1);
disp(sprintf(' %2.0f %3.0f %6.3f ',k,degrees,y(k)));
end
disp( ' ');
disp('x(k) is given in degrees.')
disp(sprintf('One Degree = %5.3e Radians',pi/180))

SinePlot
% Script File: SinePlot
% Displays increasingly smooth plots of sin(2*pi*x).

close all
for n = [4 8 12 16 20 50 100 200 400]
x = linspace(0,1,n);
y = sin(2*pi*x);
plot(x,y)
title(sprintf('Plot of sin(2*pi*x) based upon n = %3.0f points.',n))
pause(1)
end

ExpPlot
% Script File: ExpPlot
% Examines the function
%
% f(x) = ((1 + x/24)/(1 - x/24 + x^2/384))^8
%
% as an approximation to exp(z) across [0,1].

close all
x = linspace(0,1,200);
num = 1 + x/24;
denom = 1 - x/12 + (x/384).*x;
quot = num./denom;
y = quot.^8;
plot(x,y,x,exp(x))

TangentPlot
% Script File: TangentPlot
% Plots the function tan(x), -pi/2 <= x <= 9pi/2

close all
ymax = 10;
x = linspace(-pi/2,pi/2,40);
y = tan(x);
plot(x,y)
axis([-pi/2 9*pi/2 -ymax ymax])
title('The Tangent Function')
xlabel('x')
ylabel('tan(x)')
hold on
for k=1:4
xnew = x+ k*pi;
plot(xnew,y);
end
hold off

SineAndCosPlot
% Script File: SineAndCosPlot
% Plots the functions sin(2*pi*x) and cos(2*pi*x) across [0,1]
% and marks their intersection.

close all
x = linspace(0,1,200);
y1 = sin(2*pi*x);
y2 = cos(2*pi*x);
plot(x,y1,x,y2,'--',[1/8 5/8],[1/sqrt(2) -1/sqrt(2)],'*')
Polygons
% Script File: Polygons
% Plots selected regular polygons.

close all
theta = linspace(0,2*pi,361);
c = cos(theta);
s = sin(theta);
k=0;
for sides = [3 4 5 6 8 10 12 18 24]
stride = 360/sides;
k=k+1;
subplot(3,3,k)
plot(c(1:stride:361),s(1:stride:361))
axis([-1.2 1.2 -1.2 1.2])
axis equal
end

SumOfSines
% Script File: SumOfSines
% Plots f(x) = 2sin(x) + 3sin(2x) + 7sin(3x) + 5sin(4x)
% across the interval [-10,10].

close all
x = linspace(-10,10,200)';
A = [sin(x) sin(2*x) sin(3*x) sin(4*x)];
y = A*[2;3;7;5];
plot(x,y)
title('f(x) = 2sin(x) + 3sin(2x) + 7sin(3x) + 5sin(4x)')

SumOfSines2
% Script File: SumOfSines2
% Plots the functions
% f(x) = 2sin(x) + 3sin(2x) + 7sin(3x) + 5sin(4x)
% g(x) = 8sin(x) + 2sin(2x) + 6sin(3x) + 9sin(4x)
% across the interval [-10,10].

close all
n = 200;
x = linspace(-10,10,n)';
A = [sin(x) sin(2*x) sin(3*x) sin(4*x)];
y = A*[2 8;3 2;7 6;5 9];
plot(x,y)

UpDown
% Script File: UpDown
% Generates a column vector x(1:n) of positive integers
% where x(1) is solicited and
%
% x(k+1) = x(k)/2 if x(k) is even.
% x(k+1) = 3x(k)+1 if x(k) is odd.
%
% The value of n is either 500 or the first index with the
% property that x(n) = 1, whichever comes first.

x = zeros(500,1);
x(1) = input('Enter initial positive integer:');
k = 1;
while ((x(k) ~= 1) & (k < 500))
if rem(x(k),2) == 0
x(k+1) = x(k)/2;
else
x(k+1) = 3*x(k)+1;
end
k = k+1;
end
n = k;
x = x(1:n);

clc
disp(sprintf('x(1:%1.0f) = \n',n))
disp(sprintf('%-8.0f',x))
[xmax,imax] = max(x);
disp(sprintf('\n x(%1.0f) = %1.0f is the max.',imax,xmax))
density = sum(x<=x(1))/x(1);
disp(sprintf(' The density is %5.3f.',density))

close all
figure
plot(x)
title(sprintf('x(1) = %1.0f, n = %1.0f',x(1),n));
figure
plot(-sort(-x))
title('Sequence values sorted.')
I = find(rem(x(1:n-1),2)==1);
if length(I)>1
figure
plot((1:n),zeros(1,n),I+1,x(I+1),I+1,x(I+1),'*')
title('Local Maxima')
end

RunUpDown
% Script File: RunUpDown
% Environment for studying the up/down sequence.
% Stores selected results in file UpDownOutput.

while(input('Another Example? (1=yes, 0=no)'))


diary UpDownOutput
UpDown
diary off
disp(' ')
if (input('Keep Output? (1=yes, 0=no)')~=1)
delete UpDownOutput
end
end
Histograms
% Script File: Histograms
% Histograms of rand(1000,1) and randn(1000,1).

close all
subplot(2,1,1)
x = rand(1000,1);
hist(x,30)
axis([-1 2 0 60])
title('Distribution of Values in rand(1000,1)')
xlabel(sprintf('Mean = %5.3f. Median = %5.3f.',mean(x),median(x)))
subplot(2,1,2)
x = randn(1000,1);
hist(x,linspace(-2.9,2.9,100));
title('Distribution of Values in randn(1000,1)')
xlabel(sprintf('Mean = %5.3f. Standard Deviation = %5.3f',mean(x),std(x)))

Clouds
% Script File: Clouds
% 2-dimensional pictures of the uniform and normal distributions.

close all
Points = rand(1000,2);
subplot(1,2,1)
plot(Points(:,1),Points(:,2),'.')
title('Uniform Distribution.')
axis([0 1 0 1])
axis square
Points = randn(1000,2);
subplot(1,2,2)
plot(Points(:,1),Points(:,2),'.')
title('Normal Distribution.')
axis([-3 3 -3 3])
axis square

Dice
% Script File: Dice
% Simulates 1000 rollings of a pair of dice.

close all
First = 1 + floor(6*rand(1000,1));
Second = 1 + floor(6*rand(1000,1));
Throws = First + Second;
hist(Throws, linspace(2,12,11));
title('Outcome of 1000 Dice Rolls.')

Darts
% Script File: Darts
% Estimates pi using random dart throws.
close all
rand('seed',.123456)
NumberInside = 0;
PiEstimate = zeros(500,1);
for k=1:500
x = -1+2*rand(100,1);
y = -1+2*rand(100,1);
NumberInside = NumberInside + sum(x.^2 + y.^2 <= 1);
PiEstimate(k) = (NumberInside/(k*100))*4;
end
plot(PiEstimate)
title(sprintf('Monte Carlo Estimate of Pi = %5.3f',PiEstimate(500)));
xlabel('Hundreds of Trials')

Smooth
% Script File: Smooth
% Solicits n, draws an n-gon, and then smooths it.

close all
n = input('Enter the number of edges:');
figure
axis([0 1 0 1])
axis square
hold on
x = zeros(n,1);
y = zeros(n,1);
for k=1:n
title(sprintf('Click in %2.0f more points.',n-k+1))
[x(k) y(k)] = ginput(1);
plot(x(1:k),y(1:k), x(1:k),y(1:k),'*')
end
x0 = x; y0 = y;
x = [x;x(1)];
y = [y;y(1)];
plot(x,y,x,y,'*')
title('The Original Polygon')
hold off
k=0;
xlabel('Click inside window to smooth, outside window to quit.')
[a,b] = ginput(1);
v = axis;
while (v(1)<=a) & (a<=v(2)) & (v(3)<=b) & (b<=v(4));
k = k+1;
x = [(x(1:n)+x(2:n+1))/2;(x(1)+x(2))/2];
y = [(y(1:n)+y(2:n+1))/2;(y(1)+y(2))/2];
m = max(abs([x;y]));
x = x/m;
y = y/m;
figure
plot(x,y,x,y,'*')

axis square
title(sprintf('Number of Smoothings = %1.0f',k))
xlabel('Click inside window to smooth, outside window to quit.')
v = axis;
[a,b] = ginput(1);
end

Stirling
% Script File: Stirling
% Prints a table showing error in Stirling's formula for n!

close all
clc
disp( ' ')
disp(' Stirling Absolute Relative')
disp(' n n! Approximation Error Error')
disp('----------------------------------------------------------------')
e=exp(1);
nfact=1;
for n=1:13
nfact = n*nfact;
s = sqrt(2*pi*n)*((n/e)^n);
abserror = abs(nfact - s);
relerror = abserror/nfact;
s1 = sprintf(' %2.0f %10.0f %13.2f',n,nfact,s);
s2 = sprintf(' %13.2f %5.2e',abserror,relerror);
disp([s1 s2])
end

ExpTaylor
% Script File: ExpTaylor
% Plots, as a function of n, the relative error in the
% Taylor approximation
%
% 1 + x + x^2/2! +...+ x^n/n!
%
% to exp(x).

close all
nTerms = 50;
for x=[10 5 1 -1 -5 -10]
figure
f = exp(x)*ones(nTerms,1);
s = 1;
term = 1;
for k=1:50
term = x.*term/k;
s = s+ term;
err(k) = abs(f(k) - s);
end
relerr = err/exp(x);
semilogy(1:nTerms,relerr)
ylabel('Relative Error in Partial Sum.')
xlabel('Order of Partial Sum.')
title(sprintf('x = %5.2f',x))
end
Zoom
% Script File: Zoom
% Plots (x-1)^6 near x=1 with increasingly refined scale.
% Evaluation via x^6 - 6x^5 + 15x^4 - 20x^3 + 15x^2 - 6x +1
% leads to severe cancellation.

close all
k=0;
n=100;
for delta = [.1 .01 .008 .007 .005 .003 ]
x = linspace(1-delta,1+delta,n)';
y = x.^6 - 6*x.^5 + 15*x.^4 - 20*x.^3 + 15*x.^2 - 6*x + ones(n,1);
k = k+1;
subplot(2,3,k)
plot(x,y,x,zeros(1,n))
axis([1-delta 1+delta -max(abs(y)) max(abs(y))])
end

FpFacts
% Script File: FpFacts
% Prints some facts about the underlying floating point system.

close all
clc

% p = smallest positive integer so 1+1/2^p = 1.


x=1; p=0; y=1; z=x+y;
while x~=z
y=y/2;
p=p+1;
z=x+y;
end
disp(sprintf('p = %2.0f is the smallest positive integer so 1+1/2^p = 1.',p))

% q = smallest positive integer so 1/2^q = 0.


x=1; q=0;
while x>0
x=x/2;
q=q+1;
end;
disp(sprintf('q = %2.0f is the smallest positive integer so 1/2^q = 0.',q))

% r = smallest positive integer so 2^r = inf.


x=1; r=0;
while x~=inf
x=2*x;
r=r+1;
end
disp(sprintf('r = %2.0f is the smallest positive integer so 2^r = inf.',r))

% The Kahan example. In exact arithmetic f/e = 0/0:

disp(' ')
echo on
h = 1./2.;
x = 2./3. - h;
y = 3./5. - h;
e = (x+x+x) - h;
f = (y+y+y+y+y)-h;
z = f/e;
echo off
disp(sprintf('\nz = %10.5f',z))

TestMyExp
% Script File: TestMyExp
% Checks MyExpF, MyExp1, MyExp2, MyExp3, and MyExp4.

close all
m = 50;
x = linspace(-1,1,m);
exact = exp(x);
figure
k=0;
y = zeros(1,m);
for n = [4 8 16 20]
for i=1:m
y(i) = MyExpF(x(i),n);
end
RelErr = abs(exact - y)./exact;
k=k+1;
subplot(2,2,k)
plot(x,RelErr)
title(sprintf('n = %2.0f',n))
end

clc
nRepeat = 1;

% Note: The following fragment may take a long time to execute


% with this value of nRepeat.

disp('T1 = MyExp1 benchmark.')


disp('T2 = MyExp2 benchmark.')
disp(' ')
disp(' Length(x) T2/T1')
disp('-----------------------')
for L = 1000:100:2000
xL = linspace(-1,1,L);
tic
for k=1:nRepeat
y = MyExp1(xL);
end
T1 = toc;
tic
for k=1:nRepeat
y = MyExp2(xL);
end
T2 = toc;
disp(sprintf('%6.0f %13.6f ',L,T2/T1))
end
disp(' ')
disp(' ')
disp(' Length(x) MyExp3(x) MyExp4(x) ')
disp(' Flops Flops')
disp('---------------------------------------')
for L = 50:50:300
xL = linspace(-1,1,L);
flops(0);
y = MyExp3(xL);
f3 = flops;
flops(0);
y = MyExp4(xL);
f4 = flops;
disp(sprintf('%6.0f %13.0f %13.0f ',L,f3,f4))
end

TestDerivative
% Script File: TestDerivative
% Numerical differentiation of f(x) = sin(10x)

close all
a = 10;
M2=100;

% Look at errors across [0,1].


% err1 = error with no derivative information.
% err2 = error with 2nd derivative information.
m = 100;
x=linspace(0,1,m);
for i=1:m
exactDer = a*cos(a*x(i));
err1(i) = abs(exactDer - Derivative('sin10',x(i)));
err2(i) = abs(exactDer - Derivative('sin10',x(i),eps,M2));
end

% Plot err1(1:m)
figure
plot(x,err1);
title('Derivative(''sin10'',a)')
xlabel('a')
ylabel('Der Error')

% Plot err2(1:m)
figure
plot(x,err2);
title('Derivative(''sin10'',a,eps,100)')
xlabel('a')
ylabel('Der Error')

Euler
% Script File: Euler
% Sums the series 1 + 1/2 + 1/3 + .. in 3-digit floating point arithmetic.
% Terminates when the addition of the next term does not change
% the value of the running sum.

oldsum = represent(0);
one = represent(1);
sum = one;
k = 1;
while convert(sum) ~= convert(oldsum)
k = k+1;
kay = represent(k);
term = float(one,kay,'/');
oldsum = sum;
sum = float(sum,term,'+');
end
clc
disp(['The sum for ' num2str(k) ' or more terms is ' pretty(sum)])

ShowPadeArray
% Script File: ShowPadeArray
% Illustrates the function PadeArray that creates a cell array.

clc
P = PadeArray(5,5);
for i=1:5
for j=1:5
disp(sprintf('\n (%1d,%1d) Pade Coefficients:\n',i-1,j-1))
disp([' ' sprintf('%7.4f ',P{i,j}.num)])
disp([' ' sprintf('%7.4f ',P{i,j}.den)])
end
end

ShowFonts
% Script File: ShowFonts

close all
HA = 'HorizontalAlign';

fonts = {'Times-Roman' 'Helvetica' 'AvantGarde' 'Bookman' 'Palatino'...


'ZapfChancery' 'Courier' 'NewCenturySchlbk' 'Helvetica-Narrow'};
for k=1:9
figure
plot([-10 100 100 -10 -10],[0 0 60 60 0])
axis([-10 100 0 60])
axis off
v=38;
F = fonts{k};
text(45,55,F,'FontName',F,'FontSize',24,HA,'center')
text(10,47,'Plain','FontName',F,'FontSize',22,HA,'center')
text(45,47,'Bold','FontName',F,'Fontweight','bold','FontSize',22,HA,'center')
text(82,47,'Oblique','FontName',F,'FontAngle','oblique','FontSize',22,HA,'center')
for size=[22 18 14 12 11 10 9]
text(10,v,'Matlab','FontName',F,'FontSize',size,HA,'center')
text(45,v,'Matlab','FontName',F,'FontSize',size,HA,'center','FontWeight','bold')
text(82,v,'Matlab','FontName',F,'FontSize',size,HA,'center','FontAngle','oblique')
v = v-6;
end
end

ShowSymbols
% Script File: ShowSymbols

close all
plot([0 12 12 0 0],[0 0 12 12 0])
axis off
text(6,10.5,'Math Symbols','FontSize',18,'HorizontalAlign','center')
x = 1; x1 = x+.7;
y = 4.6; y1 = y+.7;
z = 9; z1 = z+.7;
n = 12;

text(y,9,'\leftarrow','Fontsize',n);
text(y,8,'\rightarrow','Fontsize',n);
text(y,7,'\uparrow','Fontsize',n)
text(y,6,'\downarrow','Fontsize',n)
text(y,5,'\Leftarrow','FontSize',n)
text(y,4,'\Rightarrow','FontSize',n)
text(y,3,'\Leftrightarrow','FontSize',n)
text(y,2,'\partial','FontSize',n)

text(x,9,'\neq','FontSize',n)
text(x,8,'\geq','FontSize',n)
text(x,7,'\approx','FontSize',n)
text(x,6,'\equiv','Fontsize',n)
text(x,5,'\cong','Fontsize',n)
text(x,4,'\pm','Fontsize',n)
text(x,3,'\nabla','FontSize',n)
text(x,2,'\angle','FontSize',n)

text(z,9,'\in','FontSize',n)
text(z,8,'\subset','Fontsize',n)
text(z,7,'\cup','Fontsize',n)
text(z,6,'\cap','Fontsize',n)
text(z,5,'\perp','FontSize',n)
text(z,4,'\infty','FontSize',n)
text(z,3,'\int','Fontsize',n)
text(z,2,'\times','FontSize',n)

text(y1,9,'\\leftarrow','Fontsize',n);
text(y1,8,'\\rightarrow','Fontsize',n);
text(y1,7,'\\uparrow','Fontsize',n)
text(y1,6,'\\downarrow','Fontsize',n)
text(y1,5,'\\Leftarrow','FontSize',n)
text(y1,4,'\\Rightarrow','FontSize',n)
text(y1,3,'\\Leftrightarrow','FontSize',n)
text(y1,2,'\\partial','FontSize',n)

text(x1,9,'\\neq','FontSize',n)
text(x1,8,'\\geq','FontSize',n)
text(x1,7,'\\approx','FontSize',n)
text(x1,6,'\\equiv','Fontsize',n)
text(x1,5,'\\cong','Fontsize',n)
text(x1,4,'\\pm','Fontsize',n)
text(x1,3,'\\nabla','FontSize',n)
text(x1,2,'\\angle','FontSize',n)

text(z1,9,'\\in','FontSize',n)
text(z1,8,'\\subset','Fontsize',n)
text(z1,7,'\\cup','Fontsize',n)
text(z1,6,'\\cap','Fontsize',n)
text(z1,5,'\\perp','FontSize',n)
text(z1,4,'\\infty','FontSize',n)
text(z1,3,'\\int','Fontsize',n)
text(z1,2,'\\times','FontSize',n)

ShowGreek
% Script File: ShowGreek

close all
plot([-1 12 12 -1 -1],[-1 -1 12 12 -1])
axis off
text(4,10,'Greek Symbols','FontSize',18)
x = 0; x1 = x+.7;
y = 4; y1 = y+.7;
z = 8; z1 = z+.7;
n = 12;

text(x,8,'\alpha','Fontsize',n);
text(x,7,'\beta','Fontsize',n);
text(x,6,'\gamma','Fontsize',n)
text(x,5,'\delta','Fontsize',n)
text(x,4,'\epsilon','FontSize',n)
text(x,3,'\kappa','FontSize',n)
text(x,2,'\lambda','Fontsize',n)
text(x,1,'\mu','Fontsize',n)
text(x,0,'\nu','Fontsize',n)

text(x1,8,'\\alpha','Fontsize',n);
text(x1,7,'\\beta','Fontsize',n);
text(x1,6,'\\gamma','Fontsize',n)
text(x1,5,'\\delta','Fontsize',n)
text(x1,4,'\\epsilon','FontSize',n)
text(x1,3,'\\kappa','FontSize',n)
text(x1,2,'\\lambda','Fontsize',n)
text(x1,1,'\\mu','Fontsize',n)
text(x1,0,'\\nu','Fontsize',n)

text(y,8,'\omega','Fontsize',n)
text(y,7,'\phi','Fontsize',n)
text(y,6,'\pi','FontSize',n)
text(y,5,'\chi','Fontsize',n)
text(y,4,'\psi','Fontsize',n)
text(y,3,'\rho','FontSize',n)
text(y,2,'\sigma','Fontsize',n)
text(y,1,'\tau','FontSize',n)
text(y,0,'\upsilon','FontSize',n)

text(y1,8,'\\omega','Fontsize',n)
text(y1,7,'\\phi','Fontsize',n)
text(y1,6,'\\pi','FontSize',n)
text(y1,5,'\\chi','Fontsize',n)
text(y1,4,'\\psi','Fontsize',n)
text(y1,3,'\\rho','FontSize',n)
text(y1,2,'\\sigma','Fontsize',n)
text(y1,1,'\\tau','FontSize',n)
text(y1,0,'\\upsilon','FontSize',n)

text(z,8,'\Sigma','FontSize',n)
text(z,7,'\Pi','FontSize',n)
text(z,6,'\Lambda','FontSize',n)
text(z,5,'\Omega','FontSize',n)
text(z,4,'\Gamma','FontSize',n)

text(z1,8,'\\Sigma','FontSize',n)
text(z1,7,'\\Pi','FontSize',n)
text(z1,6,'\\Lambda','FontSize',n)
text(z1,5,'\\Omega','FontSize',n)
text(z1,4,'\\Gamma','FontSize',n)

ShowText
% Script File: ShowText

close all
r = 1;
t = pi/6 + linspace(0,2*pi,7);
x = r*cos(t);
y = r*sin(t);
plot(x,y);

axis equal off


HA = 'HorizontalAlignment';
VA = 'VerticalAlignment';
text(x(1),y(1),'\leftarrow {\itP}_{1}', HA,'left','FontSize',14)
text(x(2),y(2),'\downarrow', HA,'center',VA,'baseline','FontSize',14)
text(x(2),y(2),'{ \itP}_{2}', HA,'left',VA,'bottom','FontSize',14)
text(x(3),y(3),'{\itP}_{3} \rightarrow', HA,'right','FontSize',14)
text(x(4),y(4),'{\itP}_{4} \rightarrow', HA,'right','FontSize',14)
text(x(5),y(5),'\uparrow', HA,'center',VA,'top','FontSize',14)
text(x(5),y(5),'{\itP}_{5} ', HA,'right',VA,'top','FontSize',14)
text(x(6),y(6),'\leftarrow {\itP}_{6}', HA,'left','FontSize',14)

text(0,1.4*r,'A Labeled Hexagon^{1}',HA,'center','FontSize',14)


text(0,-1.4*r,'^{1} A hexagon has six sides.',HA,'center','FontSize',10)

ShowLineWidth
% Script File: ShowLineWidth

close all
plot([0 14 14 0 0],[0 0 14 14 0])
text(7,13,'LineWidth','FontSize',18,'HorizontalAlign','center')
axis off
hold on
for width=0:10
h = plot([3 12],[11-width 11-width]);
if width>0
set(h,'LineWidth',width)
text(1,11-width,sprintf('%3d',width),'FontSize',14)
else
text(1,11-width,'default','FontSize',14)

ShowAxes
% Script File: ShowAxes

F = 'Times-Roman'; n = 12;
close all
t = linspace(0,2*pi); c = cos(t); s = sin(t);
plot(c,s)
axis([-1.3 1.3,-1.3 1.3])
axis equal

title('The Circle ({\itx-a})^{2} + ({\ity-b})^{2} = {\itr}^{2}','FontName',F,'FontSize',n)


xlabel('x','FontName',F,'FontSize',n)
ylabel('y','FontName',F,'FontSize',n)
set(gca,'XTick',[-.5 0 .5])
set(gca,'YTick',[-.5 0 .5])
grid on

ShowLegend
% Script File: ShowLegend

close all
t = linspace(0,2);
axis([0 2 -1.5 1.5])
y1 = sin(t*pi);
y2 = cos(t*pi);
plot(t,y1,t,y2,'--',[0 .5 1 1.5 2],[0 0 0 0 0],'o')
set(gca,'XTick',[])
set(gca,'YTick',[0])
grid on
legend('sin(\pi t)','cos(\pi t)','roots',0)

ShowColor
% Script File: ShowColor

close all
plot([-1 16 16 -1 -1],[-3 -3 18 18 -3])
axis off
text(0.5,16.5,'Built-In Colors','FontSize',14)
text(9.5,16.5,'A Gradient','FontSize',14)

% The predefined colors.


x = [3 6 6 3 3];
y = [-.5 -.5 .5 .5 -.5];
hold on
fill(x,y,'c')
text(0,0,'cyan','FontSize',12)
fill(x,y+2,'m')
text(0,2,'magenta','FontSize',12)
fill(x,y+4,'y')
text(0,4,'yellow','FontSize',12)
fill(x,y+6,'r')
text(0,6,'red','FontSize',12)
fill(x,y+8,'g')
text(0,8,'green','FontSize',12)
fill(x,y+10,'b')
text(0,10,'blue','FontSize',12)
fill(x,y+12,'k')
text(0,12,'black','FontSize',12)
fill(x,y+14,'w')
text(0,14,'white','FontSize',12)

% Making your own colors.


a = -.5;
b = 14.5;
L = 11;
R = 15;
n = 100;
k=1;
for f = [.4 .6 .7 .8 .85 .90 .95 1]
color = [f 1 1];
text(x(1)+5,y(1)+(2*(k-1)),sprintf('[ %4.2f , %1d , %1d ]',f,1,1),'VerticalAlign','bottom')
fill(x+9,y+(2*(k-1)),color)
k = k+1;
end
hold off

MyExpF
function y = MyExpF(x,n)
% y = MyExpF(x,n)
% x is a scalar, n is a positive integer
% and y = n-th order Taylor approximation to exp(x).
y = 1;
term = 1;
for k = 1:n
term = x*term/k;
y = y + term;
end

MyExp1
function y = MyExp1(x)
% y = MyExp1(x)
% x is a column n-vector and y is a column n-vector with the property that
% y(i) is a Taylor approximation to exp(x(i)) for i=1:n.

nTerms = 17;
n = length(x);
y = ones(n,1);
for i=1:n
y(i) = MyExpF(x(i),nTerms);
end

MyExp2
function y = MyExp2(x)
% y = MyExp2(x)
% x is an n-vector and y is an n-vector with with the same shape
% and the property that y(i) is a Taylor approximation to
% exp(x(i)) for i=1:n.

y = ones(size(x));
nTerms = 17;
term = ones(size(x));
for k=1:nTerms
term = x.*term/k;
y = y + term;
end

MyExpW
function y = MyExpW(x)
% y = MyExpW(x)
% x is a scalar and y is a Taylor approximation to exp(x).

y = 0;
term = 1;
k=0;
while abs(term) > eps*abs(y)
k = k + 1;
y = y + term;
term = x*term/k;
end

MyExp3
function y = MyExp3(x)
% y = MyExp3(x)
% x is a column n-vector and y is a column n-vector with the property that
% y(i) is a Taylor approximation to exp(x(i)) for i=1:n.

n = length(x);
y = ones(n,1);
for i=1:n
y(i) = MyExpW(x(i));
end
MyExp4
function y = MyExp4(x)
% y = MyExp4(x)
% x is an n-vector and y is an n-vector with the same shape and the
% property that y(i) is a Taylor approximation to exp(x(i)) for i=1:n.

y = zeros(size(x));
term = ones(size(x));
k = 0;
while any(abs(term) > eps*abs(y))
y = y + term;
k = k+1;
term = x.*term/k;
end

Derivative
function [d,err] = Derivative(fname,a,delta,M2)
% d = Derivative(fname,a,delta,M2);
% fname a string that names a function f(x) whose derivative
% at x = a is sought. delta is the absolute error associated with
% an f-evaluation and M2 is an estimate of the second derivative
% magnitude near a. d is an approximation to f'(a) and err is an estimate
% of its absolute error.
%
% Usage:
% [d,err] = Derivative(fname,a)
% [d,err] = Derivative(fname,a,delta)
% [d,err] = Derivative(fname,a,delta,M2)

if nargin &= 3
% No derivative bound supplied, so assume the
% second derivative bound is 1.
M2 = 1;
end
if nargin == 2
% No function evaluation error supplied, so
% set delta to eps.
delta = eps;
end
% Compute optimum h and divided difference
hopt = 2*sqrt(delta/M2);
d = (feval(fname,a+hopt) - feval(fname,a))/hopt;
err = 2*sqrt(delta*M2);

Sin10
function y = sin10(x)
y = sin(10*x);

Represent
function f = Represent(x)
% f = Represent(x)
% Yields a 3-digit mantissa floating point representation of f:
%
% f.mSignBit mantissa sign bit (0 if x>=0, 1 otherwise)
% f.m mantissa (= f.m(1)/10 + f.m(2)/100 + f.m(3)/1000)
% f.eSignBit the exponent sign bit (0 if exponent nonnegative, 1 otherwise)
% f.e the exponent (-9<=f.e<=9)
%
% If x is out side of [-.999*10^9,.999*10^9], f.m is set to inf.
% If x is in the range [-.100*10^-9,.100*10^-9] f is the representation of zero
% in which both sign bits are 0, e is zero, and m = [0 0 0].

f = struct('mSignBit',0,'m',[0 0 0],'eSignBit',0,'e',0);

if abs(x)<.100*10^-9
% Underflow. Return 0
return
end

if x>.999*10^9
% Overflow. Return inf
f.m = inf;
return
end
if x<-.999*10^9
% Overflow. Return -inf
f.mSignBit = 1;
f.m = inf;
return
end

% Set the mantissa sign bit


if x>0
f.mSignBit = 0;
else
f.mSignBit = 1;
end

% Determine m and e so .1 <= m < 1 and abs(x) = m*10^e


m = abs(x); e = 0;
while m >= 1, m = m/10; e = e+1; end
while m < 1/10,m = 10*m; e = e-1; end

% Determine nearest integer to 1000m


z = round(1000*m);
% Set the mantissa
f.m(1) = floor(z/100);
f.m(2) = floor((z - f.m(1)*100)/10);
f.m(3) = z - f.m(1)*100 - f.m(2)*10;
% Set the exponent and its sign bit.
if e>=0
f.eSignBit = 0;
f.e = e;
else
f.eSignBit = 1;
f.e = -e;
end
Convert
function x = Convert(f)
% x = Convert(f)
% f is a is a representation of a 3-digit floating point number. (For details
% type help represent. x is the value of v.

% Overflow situations
if (f.m == inf) & (f.mSignBit==0)
x = inf;
return
end
if (f.m == inf) & (f.mSignBit==1)
x = -inf;
return
end

% Mantissa value
mValue = (100*f.m(1) + 10*f.m(2) + f.m(3))/1000;
if f.mSignBit==1
mValue = -mValue;
end

% Exponent value
eValue = f.e;
if f.eSignBit==1
eValue = -eValue;
end

x = mValue * 10^eValue;

Float
function z = Float(x,y,op)
% z = Float(x,y,op)
% x and y are representations of a 3-digit floating point number. (For details
% type help represent.
% op is one of the strings '+', '-', '*', or '/'.
% z is the 3-digit floating point representation of x op y.

sx = num2str(convert(x));
sy = num2str(convert(y));
z = represent(eval(['(' sx ')' op '(' sy ')' ]));

Pretty
function s = Pretty(f)
% s = Pretty(f)
% f is a is a representation of a 3-digit floating point number. (For details
% type help represent.
% s is a string so that disp(s) "pretty prints" the value of f.

if (f.m == inf) & (f.mSignBit==0)


s = 'inf';
elseif (f.m==inf) & (f.mSignBit==1)
s = '-inf';
else
% Convert the mantissa.
m = ['.' num2str(f.m(1)) num2str(f.m(2)) num2str(f.m(3))];
if f.mSignBit == 1
m = ['-' m];
end
% Convert the exponent.
e = num2str(f.e);
if f.eSignBit == 0
e = ['+' num2str(f.e)];
else
e = ['-' num2str(f.e)];
end
s = [m ' x 10^' e ];
end

PadeArray
function P = PadeArray(m,n)
% P = PadeArray(m,n)
% m and n are nonnegative integers.
% P is an (m+1)-by-(n+1) cell array.
%
% P{i,j} represents the (i-1,j-1) Pade approximation N(x)/D(x) to exp(x).

P = cell(m+1,n+1);
for i=1:m+1
for j=1:n+1
P{i,j} = PadeCoeff(i-1,j-1);
end
end

PadeCoeff
function R = PadeCoeff(p,q)
% R = PadeCoeff(p,q)
% p and q are nonnegative integers and R is a representation of the
% (p,q)-Pade approximation N(x)/D(x) to exp(x):
%
% R.num is a row (p+1)-vector whose entries are the coefficients of the
% p-degree numerator polynomial N(x).
%
% R.den is a row (q+1)-vector whose entries are the coefficients of the
% q-degree denominator polynomial D(x).
%
% Thus,
% R.num(1) + R.num(2)x + R.num(3)x^2
% ------------------------------------
% R.den(1) + R.den(2)x
%
% is the (2,1) Pade approximation.

a(1) = 1; for k=1:p, a(k+1) = a(k)*(p-k+1)/(k*(p+q-k+1)); end


b(1) = 1; for k=1:q, b(k+1) = -b(k)*(q-k+1)/(k*(p+q-k+1)); end
R = struct('num',a,'den',b);
Chapter 2 M-Files
M-File Home

Script File Index

ShowV Illustrates InterpV and HornerV.


ShowN Illustrates InterpN and HornerN.
InterpEff Compares Vandermonde and Newton approaches.
RungeEg Examines accuracy of interpolating polynomial.
TableLookUp2D Illustrates SetUp and LinInterp2D.
ShowPolyTools Illustrates Matlab's polynomial tools.
Pallas Fits periodic data with CSInterp and CSEval.

Function File Index

InterpV Construction of Vandermonde interpolating polynomial.


HornerV Evaluates the Vandermonde interpolating polynomial.
InterpNRecur Recursive construction of the Newton interpolating polynomial.
InterpN Nonrecursive construction of the Newton interpolating polynomial.
InterpN2 Another nonrecursive construction of the Newton interpolating polynomial.
HornerN Evaluates the Newton interpolating polynomial.
SetUp Sets up matrix of f(x,y) evaluation.
LinInterp2D 2-Dimensional Linear Interpolation.
Humps2D A sample function of two variables.
CSInterp Fits periodic data with sines and cosines.
CSEval Evaluates sums of sines and cosines.

ShowV
% Script File: ShowV
% Plots 4 random cubic interpolants of sin(x) on [0,2pi].
% Uses the Vandermonde method.

close all
x0 = linspace(0,2*pi,100)';
y0 = sin(x0);
for eg=1:4
x = 2*pi*sort(rand(4,1));
y = sin(x);
a = InterpV(x,y);
pVal = HornerV(a,x0);
subplot(2,2,eg)
plot(x0,y0,x0,pVal,'--',x,y,'*')
axis([0 2*pi -2 2])
end

ShowN
% Script File: ShowN
% Random cubic interpolants of sin(x) on [0,2pi].
% Uses the Newton representation.

close all
x0 = linspace(0,2*pi,100)';
y0 = sin(x0);
for eg=1:20
x = 2*pi*sort(rand(4,1));
y = sin(x);
c = InterpN(x,y);
pvals = HornerN(c,x,x0);
plot(x0,y0,x0,pvals,x,y,'*')
axis([0 2*pi -2 2])
pause(1)
end

InterpEff
% Script File: InterpEff
% Compares the Vandermonde and Newton Approaches

clc
disp('Flop Counts:')
disp(' ')
disp(' n InterpV InterpN InterpNRecur')
disp('--------------------------------------')
for n = [4 8 16]
x = linspace(0,1,n)'; y = sin(2*pi*x);
flops(0); a = InterpV(x,y); f1 = flops;
flops(0); c = InterpN(x,y); f2 = flops;
flops(0); c = InterpNRecur(x,y); f3 = flops;
disp(sprintf('%3.0f %7.0f %7.0f %7.0f',n,f1,f2,f3));
end

RungeEg
% Script File: RungeEg
% For n = 7:2:15, the equal-spacing interpolants of f(x) = 1/(1+25x^2) on [-1,1]'
% are of plotted.

close all
x = linspace(-1,1,100)';
y = ones(100,1)./(1 + 25*x.^2);
for n=7:2:15
figure
xEqual = linspace(-1,1,n)';
yEqual = ones(n,1)./(1+25*xEqual.^2);
cEqual=InterpN(xEqual,yEqual);
pValsEqual = HornerN(cEqual,xEqual,x);
plot(x,y,x,pValsEqual,xEqual,yEqual,'*')
title(sprintf('Equal Spacing (n = %2.0f)',n))
end

TableLookUp2D
% Script File: TableLookUp2D
% Illustrates SetUp and LinearInterp2D.

close all
clc
a = 0; b = 5; n = 11;
c = 0; d = 3; m = 7;
fA = SetUp('Humps2D',a,b,n,c,d,m);
contour(fA)
x = input(sprintf('Enter x (%3.1f < = x < ="%3.1f" ):',a,b));
y = input(sprintf('Enter" y (%3.1f < ="y" < ="%3.1f" ):',c,d));
z= LinInterp2D(x,y,a,b,c,d,fA);
disp(sprintf('f(x,y)="%20.16f\n',z))"

ShowPolyTools
% Script File: ShowPolyTools

close all

x = linspace(0,4*pi);
y = exp(-.2*x).*sin(x);

x0 = [.5 4 8 12];
y0 = exp(-.2*x0).*sin(x0);

p = polyfit(x0,y0,length(x0)-1);
pvals = polyval(p,x);

plot(x,y,x,pvals,x0,y0,'o')
axis([0 4*pi -1 1])
set(gca,'XTick',[])
set(gca,'YTick',[])
title('Interpolating {\ite}^{-.2{\itx}}sin({\itx}) with a Cubic Polynomial')

Pallas
% Script File: Pallas
% Plots the trigonometric interpolant of the Gauss Pallas data.

A = linspace(0,360,13)';
D = [ 408 89 -66 10 338 807 1238 1511 1583 1462 1183 804 408]';

Avals = linspace(0,360,200)';
F = CSInterp(D(1:12));
Fvals = CSeval(F,360,Avals);

plot(Avals,Fvals,A,D,'o')
axis([-10 370 -200 1700])
set(gca,'xTick',linspace(0,360,13))
xlabel('Ascension (Degrees)')
ylabel('Declination (minutes)')

InterpV
function a = InterpV(x,y)
% a = InverpV(x,y)
% This computes the Vandermonde polynomial interpolant where
% x is a column n-vector with distinct components and y is a
% column n-vector.
%
% a is a column n-vector with the property that if
%
% p(x) = a(1) + a(2)x + ... a(n)x^(n-1)
% then
% p(x(i)) = y(i), i=1:n

n = length(x);
V = ones(n,n);
for j=2:n
% Set up column j.
V(:,j) = x.*V(:,j-1);
end
a = V\y;

HornerV
function pVal = HornerV(a,z)
% pVal = HornerV(a,z)
% evaluates the Vandermonde interpolant on z where
% a is an n-vector and z is an m-vector.
%
% pVal is a vector the same size as z with the property that if
%
% p(x) = a(1) + .. +a(n)x^(n-1)
% then
% pVal(i) = p(z(i)) , i=1:m.

n = length(a);
m = length(z);
pVal = a(n)*ones(size(z));
for k=n-1:-1:1
pVal = z.*pVal + a(k);
end

InterpNRecur
function c = InterpNRecur(x,y)
% c = InterpNRecur(x,y)
% The Newton polynomial interpolant.
% x is a column n-vector with distinct components and y is
% a column n-vector. c is a column n-vector with the property that if
%
% p(x) = c(1) + c(2)(x-x(1))+...+ c(n)(x-x(1))...(x-x(n-1))
% then
% p(x(i)) = y(i), i=1:n.

n = length(x);
c = zeros(n,1);
c(1) = y(1);
if n > 1
c(2:n) = InterpNRecur(x(2:n),(y(2:n)-y(1))./(x(2:n)-x(1)));
end

InterpN
function c = InterpN(x,y)
% c = InterpN(x,y)
% The Newton polynomial interpolant.
% x is a column n-vector with distinct components and y is
% a column n-vector. c is a column n-vector with the property that if
%
% p(x) = c(1) + c(2)(x-x(1))+...+ c(n)(x-x(1))...(x-x(n-1))
% then
% p(x(i)) = y(i), i=1:n.

n = length(x);
for k = 1:n-1
y(k+1:n) = (y(k+1:n)-y(k)) ./ (x(k+1:n) - x(k));
end
c = y;

InterpN2
function c = InterpN2(x,y)
% c = InterpN2(x,y)
% The Newton polynomial interpolant.
% x is a column n-vector with distinct components and y is
% a column n-vector. c is a column n-vector with the property that if
%
% p(x) = c(1) + c(2)(x-x(1))+...+ c(n)(x-x(1))...(x-x(n-1))
% then
% p(x(i)) = y(i), i=1:n.

n = length(x);
for k = 1:n-1
y(k+1:n) = (y(k+1:n)-y(k:n-1)) ./ (x(k+1:n) - x(1:n-k));
end
c = y;

HornerN
function pVal = HornerN(c,x,z)
% pVal = HornerN(c,x,z)
% Evaluates the Newton interpolant on z where
% c and x are n-vectors and z is an m-vector.
%
% pVal is a vector the same size as z with the property that if
%
% p(x) = c(1) + c(2)(x-x(1))+ ... + c(n)(x-x(1))...(x-x(n-1))
% then
% pval(i) = p(z(i)) , i=1:m.

n = length(c);
pVal = c(n)*ones(size(z));
for k=n-1:-1:1
pVal = (z-x(k)).*pVal + c(k);
end

SetUp
function fA = SetUp(fname,a,b,n,c,d,m)
% fA = SetUp(fname,a,b,n,c,d,m)
% Sets up a matrix of f(x,y) evaluations.
% fname is a string that names a function of the form f(x,y).
% a, b, c, and d are scalars that satisfy a<=b and c<="d." % n and m are integers>=2.
% fA is an n-by-m matrix with the property that
%
% A(i,j) = f(a+(i-1)(b-a)/(n-1),c+(j-1)(d-c)/(m-1)) , i=1:n, j=1:m

x = linspace(a,b,n);
y = linspace(c,d,m);
fA = zeros(n,m);
for i=1:n
for j=1:m
fA(i,j) = feval(fname,x(i),y(j));
end
end

LinInterp2D
function z = LinInterp2D(xc,yc,a,b,c,d,fA)
% z = LinInterp2D(xc,yc,a,b,n,c,d,m,fA)
% Linear interpolation on a grid of f(x,y) evaluations.
% xc, yc, a, b, c, and d are scalars that satisfy a<=xc<=b and c<=yc<=d.
% fA is an n-by-m matrix with the property that
%
% A(i,j)=f(a+(i-1)(b-a)/(n-1),c+(j-1)(d-c)/(m-1)) , i=1:n, j=1:m
%
% z is a linearly interpolated value of f(xc,yc).

[n,m]=size(fA);
% xc=a+(i-1+dx)*hx 0<=dx<=1
hx=(b-a)/(n-1);
i=max([1" ceil((xc-a)/hx)]);
dx=(xc (a+(i-1)*hx))/hx;
% yc=c+(j-1+dy)*hy 0<=dy<=1
hy=(d-c)/(m-1);
j=max([1 ceil((yc-c)/hy)]);
dy=(yc (c+(j-1)*hy))/hy;
z=(1-dy)*((1-dx)*fA(i,j)+dx*fA(i+1,j)) + dy*((1-dx)*fA(i,j+1)+dx*fA(i+1,j+1));

Humps2D
function z = Humps2D(x,y);
z = 1/( (.2*(x-3)^2 +.1) + (.3*(y-1)^2 + .1) );

CSInterp
function F = CSInterp(f)
% F = CSInterp(f)
% f is a column n vector and n = 2m.
% F.a is a column m+1 vector and F.b is a column m-1 vector so that if
% tau = (0:n-1)'*pi/m, then
%
% f = F.a(1)*cos(0*tau) +...+ F.a(m+1)*cos(m*tau) +
% F.b(1)*sin(tau) +...+ F.b(m-1)*sin((m-1)*tau)

n = length(f);
m = n/2;
tau = (pi/m)*(0:n-1)';
P = [];
for j=0:m, P = [P cos(j*tau)]; end
for j=1:m-1, P = [P sin(j*tau)]; end
y = P\f;
F = struct('a',y(1:m+1),'b',y(m+2:n));

CSEval
function Fvals = CSEval(F,T,tvals)
% Fvals = CSEval(F,T,tvals)
% F.a is a length m+1 column vector, F.b is a length m-1 column vector,
% T is a positive scalar, and tvals is a column vector.
% If
% F(t) = F.a(1) + F.a(2)*cos((2*pi/T)*t) +...+ F.a(m+1)*cos((2*m*pi/T)*t) +
% F.b(1)*sin((2*pi/T)*t) +...+ F.b(m-1)*sin((2*m*pi/T)*t)
%
% then Fvals = F(tvals).

Fvals = zeros(length(tvals),1);
tau = (2*pi/T)*tvals;
for j=0:length(F.a)-1, Fvals = Fvals + F.a(j+1)*cos(j*tau); end
for j=1:length(F.b), Fvals = Fvals + F.b(j)*sin(j*tau); end
Chapter 3 M-Files
M-File Home

Script File Index

ShowPWL1 Illustrates pwL and pwLEval.


ShowPWL2 Compares pwLStatic and pwLAdapt.
ShowHermite Illustrates the Hermite interpolation idea.
ShowpwCH Illustrates pwC and pwCEval.
ShowSpline Illustrates CubicSpline.
ShowSplineErr Explores the not-a-knot spline interpolant error.
ShowSplineTools Illustrates \Matlab spline tools.

Function File Index

pwL Sets up a piecewise linear interpolant.


Locate Determines the subinterval in a mesh that houses a given x-value.
pwLEval Evaluates a piecewise linear function.
pwLStatic A priori determination of a mesh for a pwL approximation.
pwLAdapt Dynamic determination of a mesh for a pwL approximation.
HCubic Constructs the cubic Hermite interpolant.
pwC Sets up a piecewise cubic Hermite interpolant.
pwCEval Evaluates a piecewise cubic function.
CubicSpline Constructs complete, natural, or not-a-knot spline.

ShowPWL1
% Script File: ShowPWL1
% Convergence of the piecewise linear interpolant to
% humps(x) on [0,3]

close all
z = linspace(0,3,200)';
fvals = humps(z);
for n = [5 10 25 50]
figure
x = linspace(0,3,n)';
y = humps(x);
[a,b] = pwL(x,y);
Lvals = pwLEval(a,b,x,z);
plot(z,Lvals,z,fvals,'--',x,y,'o');
title(sprintf('Interpolation of humps(x) with pwL, n = %2.0f',n))
end

ShowPWL2
% Script File: ShowPWL2
% Compares pwLStatic and pwLAdapt on [0,3] using the function
%
% humps(x) = 1/((x-.3)^2 + .01) + 1/((x-.9)^2+.04)
%

close all

% Second derivative estimate based on divided differences


z = linspace(0,1,101);
humpvals = humps(z);
M2 = max(abs(diff(humpvals,2)/(.01)^2));
for delta = [1 .5 .1 .05 .01]
figure
[x,y] = pwLStatic('humps',M2,0,3,delta);
subplot(1,2,1)
plot(x,y,'.');
title(sprintf('delta = %8.4f Static n= %2.0f',delta,length(x)))
[x,y] = pwLAdapt('humps',0,humps(0),3,humps(3),delta,.001);
subplot(1,2,2)
plot(x,y,'.');
title(sprintf('delta = %8.4f Adapt n= %2.0f',delta,length(x)))
end

ShowHermite
% Script File: ShowHermite
% Plots a succession of cubic interpolants to cos(x).
% x(2) converges to x(1) = 0 and x(3) converges to x(4) = 3pi/2.

close all
z = linspace(-pi/2,2*pi,100);
CosValues = cos(z);
for d = [1 .5 .3 .1 .05 .001]
figure
xvals = [0;d;(3*pi/2)-d;3*pi/2];
yvals = cos(xvals);
c = InterpN(xvals,yvals);
CubicValues = HornerN(c,xvals,z);
plot(z,CosValues,z,CubicValues,'--',xvals,yvals,'*')
axis([-.5 5 -1.5 1.5])
title(sprintf('Interpolation of cos(x). Separation = %5.3f',d))
end

ShowPWCH
% Script File: ShowpwCH
% Convergence of the piecewise cubic hermite interpolant to
% exp(-2x)sin(10*pi*x) on [0,1].)
close all
z = linspace(0,1,200)';
fvals = exp(-2*z).*sin(10*pi*z);
for n = [4 8 16 24]
x = linspace(0,1,n)';
y = exp(-2*x).*sin(10*pi*x);
s = 10*pi*exp(-2*x).*cos(10*pi*x)-2*y;
[a,b,c,d] = pwC(x,y,s);
Cvals = pwCEval(a,b,c,d,x,z);
figure
plot(z,fvals,z,Cvals,'--',x,y,'*');
title(sprintf('Interpolation of exp(-2x)sin(10pi*x) with pwCH, n = %2.0f',n))
end
legend('e^{-2z}sin(10\pi z)','The pwC interpolant')

ShowSpline

% Script File: ShowSpline


% Illustrates CubicSpline with various end conditions, some not so good.

close all
xvals = linspace(0,4*pi,100);
yvals = sin(xvals);
for n = 4:3:16
x = linspace(0,4*pi,n)';
y = sin(x);
[a,b,c,d] = CubicSpline(x,y,1,1,1);
svals = pwCEval(a,b,c,d,x,xvals);
figure
plot(xvals,yvals,xvals,svals,'--')
title(sprintf('''Good'' Complete spline interpolant of sin(x) with %2.0f subintervals',n-1))
end
for n = 4:3:16
x = linspace(0,4*pi,n)';
y = sin(x);
[a,b,c,d] = CubicSpline(x,y,1,-1,-1);
svals = pwCEval(a,b,c,d,x,xvals);
figure
plot(xvals,yvals,xvals,svals,'--')
title(sprintf('''Bad'' Complete spline interpolant of sin(x) with %2.0f subintervals',n-1))
end
for n = 4:3:16
x = linspace(0,4*pi,n)';
y = sin(x);
[a,b,c,d] = CubicSpline(x,y,2,0,0);
svals = pwCEval(a,b,c,d,x,xvals);
figure
plot(xvals,yvals,xvals,svals,'--')
title(sprintf('Natural spline interpolant of sin(x) with %2.0f subintervals',n-1))
end
for n = 4:3:16
x = linspace(0,4*pi,n)';
y = sin(x);
[a,b,c,d] = CubicSpline(x,y);
svals = pwCEval(a,b,c,d,x,xvals);
figure
plot(xvals,yvals,xvals,svals,'--')
title(sprintf('Not-a-Knot spline interpolant of sin(x) with %2.0f subintervals',n-1))
end

ShowSplineErr
% Script File: ShowSplineErr
% Examines error in the not-a-knot spline interpolant of
% sin(2pi*x) across the interval [0,1]. Two equally-spaced knot
% cases are plotted: h =.05 and h = .005.

close all
z = linspace(0,1,500)';
SinVals = sin(2*pi*z);
k=0;
for n=[21 201 ]

x = linspace(0,1,n)';
y = sin(2*pi*x);
[a,b,c,d] = CubicSpline(x,y);
Svals = pwCEval(a,b,c,d,x,z);
k=k+1;
subplot(1,2,k)
semilogy(z,abs(SinVals-Svals)+eps);
axis([0 1 10^(-12) 10^(-3)])
xlabel(sprintf('Knot Spacing = %5.3f',1/(n-1)))
end

ShowSplineTools

% Script File: ShowSplineTools


% Illustrates the Matlab functions spline, ppval, mkpp, unmkpp

close all

% Set Up Data:
n = 9;
x = linspace(-5,5,n);
y = atan(x);

% Compute the spline interpolant and its derivative:


S = spline(x,y);
[x,rho,L,k] = unmkpp(S);
drho = [3*rho(:,1) 2*rho(:,2) rho(:,3)];
dS = mkpp(x,drho);

% Evaluate S and dS:


z = linspace(-5,5);
Svals = ppval(S,z);
dSvals = ppval(dS,z);

% Plot:
atanvals = atan(z);
figure
plot(z,atanvals,z,Svals,x,y,'*');
title(sprintf('n = %2.0f Spline Interpolant of atan(x)',n))

datanvals = ones(size(z))./(1 + z.*z);


figure
plot(z,datanvals,z,dSvals)
title(sprintf('Derivative of n = %2.0f Spline Interpolant of atan(x)',n))

pwL
function [a,b] = pwL(x,y)
% [a,b] = pwL(x,y)
%
% Generates the piecewise linear interpolant of the data specified by the
% column n-vectors x and y. It is assumed that x(1) < x(2) < ... < x(n).
%
% a and b are column (n-1)-vectors with the property that for i=1:n-1, the
% line L(z) = a(i) + b(i)z passes though the points (x(i),y(i)) and
(x(i+1),y(i+1)).

n = length(x);
a = y(1:n-1);
b = diff(y) ./ diff(x);

Locate
function i = Locate(x,z,g)
% i = Locate(x,z,g)
% Locates z in a partition x.
%
% x is column n-vector with x(1) < x(2) <...<x(n) and
% z is a scalar with x(1) <= z <= x(n).
% g (1<=g<=n-1) is an optional input parameter
%
% i is an integer such that x(i) <= z <= x(i+1). Before the general
% search for i begins, the value i=g is tried.

if nargin==3
% Try the initial guess.
if (x(g)<=z) & (z<=x(g+1))
i = g;
return
end
end
n = length(x);
if z==x(n)
i = n-1;
else
% Binary Search
Left = 1;
Right = n;
while Right > Left+1
% x(Left) <= z <= x(Right)
mid = floor((Left+Right)/2);
if z < x(mid)
Right = mid;
else
Left = mid;
end
end
i = Left;
end

pwLEval
function LVals = pwLEval(a,b,x,zVals)
% LVals = pwLEval(a,b,x,zvals)
% Evaluates the piecewise linear polynomial defined by the column (n-1)-vectors
% a and b and the column n-vector x. It is assumed that x(1) < ... < x(n).
% zVals is a column m-vector with each component in [x(1),x(n)].
%
% LVals is a column m-vector with the property that LVals(j) = L(zVals(j))
% for j=1:m where L(z)= a(i) + b(i)(z-x(i)) for x(i)<=z<=x(i+1).

m = length(zVals);
LVals = zeros(m,1);
g = 1;
for j=1:m
i = Locate(x,zVals(j),g);
LVals(j) = a(i) + b(i)*(zVals(j)-x(i));
g = i;
end

pwLStatic
function [x,y] = pwLStatic(fname,M2,alpha,beta,delta)
% [x,y] = pwLStatic(fname,M2,alpha,beta,delta)
% Generates interpolation points for a piecewise linear approximation of
% prescribed accuracy.
%
% fname is string that names an available function f(x).
% Assume that f can take vector arguments.
% M2 is an upper bound for|f"(x)| on [alpha,beta].
% alpha and beta are scalars with alpha<beta.
% delta is a positive scalar.
%
% x and y column n-vectors with the property that y(i) = f(x(i)), i=1:n.
% The piecewise linear interpolant L(x) of this data satisfies
% |L(z) - f(z)| <= delta for x(1) <= z <= x(n).

n = max(2,ceil(1+(beta-alpha)*sqrt(M2/(8*delta))));
x = linspace(alpha,beta,n)';
y = feval(fname,x);

pwLAdapt
function [x,y] = pwLAdapt(fname,xL,fL,xR,fR,delta,hmin)
% [x,y] = pwLAdapt(fname,xL,fL,xR,fR,delta,hmin)
% Adaptively determines interpolation points for a piecewise linear
% approximation of a specified function.
%
% fname is a string that specifies a function of the form y = f(u).
% xL and xR are real scalars and fL = f(xL) and fR = f(xR).
% delta and hmin are positive real scalars that determine accuracy.
%
% x and y are column n-vector with the property that
% xL = x(1) < ... < x(n) = xR
% and y(i) = f(x(i)), i=1:n. Each subinterval [x(i),x(i+1)] is
% either <= hmin in length or has the property that at its midpoint m,
% |f(m) - L(m)| <= delta where L(x) is the line that connects (x(i),y(i))
% and (x(i+1),y(i+1)).

if (xR-xL) <= hmin


% Subinterval is acceptable
x = [xL;xR];
y = [fL;fR];
else
mid = (xL+xR)/2;
fmid = feval(fname,mid);
if (abs(((fL+fR)/2) - fmid) <= delta )
% Subinterval accepted.
x = [ xL;xR];
y = [fL;fR];
else
% Produce left and right partitions, then synthesize.
[xLeft,yLeft] = pwLAdapt(fname,xL,fL,mid,fmid,delta,hmin);
[xRight,yRight] = pwLAdapt(fname,mid,fmid,xR,fR,delta,hmin);
x = [ xLeft;xRight(2:length(xRight))];
y = [ yLeft;yRight(2:length(yRight))];
end
end

HCubic
function [a,b,c,d] = HCubic(xL,yL,sL,xR,yR,sR)
% [a,b,c,d] = HCubic(xL,yL,sL,xR,yR,sR)
% Cubic Hermite interpolation
%
% (xL,yL,sL) and (xR,yR,sR) are x-y-slope triplets with xL and xR distinct.
% a,b,c,d are real numbers with the property that if
% p(z) = a + b(z-xL) + c(z-xL)^2 + d(z-xL)^2(z-xR)
% then p(xL)=yL, p'(xL)=sL, p(xR)=yR, and p'(xR)=sR.

a = yL;
b = sL;
delx = xR - xL;
yp = (yR - yL)/delx;
c = (yp - sL)/delx;
d = (sL - 2*yp + sR)/(delx*delx);

pwC
function [a,b,c,d] = pwC(x,y,s)
% [a,b,c,d] = pwC(x,y,s)
% Piecewise cubic Hermite interpolation.
%
% x,y,s column n-vectors with x(1) < ... < x(n)
%
% a,b,c,d column (n-1)-vectors that define a continuous, piecewise
% cubic polynomial q(z) with the property that for i = 1:n,
%
% q(x(i)) = y(i) and q'(x(i)) = s(i).
%
% On the interval [x(i),x(i+1)],
%
% q(z) = a(i) + b(i)(z-x(i)) + c(i)(z-x(i))^2 + d(i)(z-x(i))^2(z-x(i+1)).

n = length(x);
a = y(1:n-1);
b = s(1:n-1);
Dx = diff(x);
Dy = diff(y);
yp = Dy ./ Dx;
c = (yp - s(1:n-1)) ./ Dx;
d = (s(2:n) + s(1:n-1) - 2*yp) ./ (Dx.* Dx);

pwCEval
function Cvals = pwCEval(a,b,c,d,x,zVals)
% Cvals = pwCEval(a,b,c,d,x,zVals)
%
% Evaluates the piecewise cubic polynomial defined by the column (n-1)-vectors
a,b,c, and
% d and the column n-vector x. It is assumed that x(1) < ... < x(n).
% zVals is a column m-vector with each component in [x(1),x(n)].
%
% CVals is a column m-vector with the property that CVals(j) = C(zVals(j))
% for j=1:m where on the interval [x(i),x(i+1)]
%
% C(z)= a(i) + b(i)(z-x(i)) + c(i)(z-x(i))^2 + d(i)(z-x(i))^2(z-x(i+1))

m = length(zVals);
Cvals = zeros(m,1);
g=1;
for j=1:m
i = Locate(x,zVals(j),g);
Cvals(j) = d(i)*(zVals(j)-x(i+1)) + c(i);
Cvals(j) = Cvals(j)*(zVals(j)-x(i)) + b(i);
Cvals(j) = Cvals(j)*(zVals(j)-x(i)) + a(i);
g = i;
end

CubicSpline

function [a,b,c,d] = CubicSpline(x,y,derivative,muL,muR)


% [a,b,c,d] = CubicSpline(x,y,derivative,muL,muR)
% Cubic spline interpolation with prescribed end conditions.
%
% x,y are column n-vectors. It is assumed that n >= 4 and x(1) < ... x(n).
% derivative is an integer (1 or 2) that specifies the order of the endpoint
derivatives.
% muL and muR are the endpoint values of this derivative.
%
% a,b,c, and d are column (n-1)-vectors that define the spline S(z). On
[x(i),x(i+1)],
%
% S(z) = a(i) + b(i)(z-x(i)) + c(i)(z-x(i))^2 + d(i)(z-x(i))^2(z-x(i+1).
%
% Usage:
% [a,b,c,d] = CubicSpline(x,y,1,muL,muR) S'(x(1)) = muL, S'(x(n)) = muR
% [a,b,c,d] = CubicSpline(x,y,2,muL,muR) S''(x(1)) = muL, S''(x(n)) = muR
% [a,b,c,d] = CubicSpline(x,y) S'''(z) continuous at x(2) and x(n-1)

% First, set up all but the first and last equations that
% define the vector of interior knot slopes s(2:n-1).

n = length(x);
Dx = diff(x);
yp = diff(y) ./ Dx;
T = zeros(n-2,n-2);
r = zeros(n-2,1);
for i=2:n-3
T(i,i) = 2*(Dx(i) + Dx(i+1));
T(i,i-1) = Dx(i+1);
T(i,i+1) = Dx(i);
r(i) = 3*(Dx(i+1)*yp(i) + Dx(i)*yp(i+1));
end

% For each of the 3 cases, finish setting up the linear system,


% solve the system, and set s(1:n) to be the vector of slopes.

if nargin==5
%Derivative information available.
if derivative==1
% End values for S'(z) specified.
T(1,1) = 2*(Dx(1) + Dx(2));
T(1,2) = Dx(1);
r(1) = 3*(Dx(2)*yp(1)+Dx(1)*yp(2)) - Dx(2)*muL;
T(n-2,n-2) = 2*(Dx(n-2)+Dx(n-1));
T(n-2,n-3) = Dx(n-1);
r(n-2) = 3*(Dx(n-1)*yp(n-2) + Dx(n-2)*yp(n-1)) -Dx(n-2)*muR;
s = [muL; T\r; muR];
end

if derivative==2
% End values for S''(z) specified.
T(1,1) = 2*Dx(1) + 1.5*Dx(2);
T(1,2) = Dx(1);
r(1) = 1.5*Dx(2)*yp(1) + 3*Dx(1)*yp(2) + Dx(1)*Dx(2)*muL/4;
T(n-2,n-2) = 1.5*Dx(n-2)+2*Dx(n-1);
T(n-2,n-3) = Dx(n-1);
r(n-2) = 3*Dx(n-1)*yp(n-2) + 1.5*Dx(n-2)*yp(n-1)-Dx(n-1)*Dx(n-2)*muR/4;
stilde = T\r;
s1 = (3*yp(1) - stilde(1) - muL*Dx(1)/2)/2;
sn = (3*yp(n-1) - stilde(n-2) + muR*Dx(n-1)/2)/2;
s = [s1;stilde;sn];
end;
else
% No derivative information. Compute the not-a-knot spline.
q = Dx(1)*Dx(1)/Dx(2);
T(1,1) = 2*Dx(1) +Dx(2) + q;
T(1,2) = Dx(1) + q;
r(1) = Dx(2)*yp(1) + Dx(1)*yp(2)+2*yp(2)*(q+Dx(1));
q = Dx(n-1)*Dx(n-1)/Dx(n-2);
T(n-2,n-2) = 2*Dx(n-1) + Dx(n-2)+q;
T(n-2,n-3) = Dx(n-1)+q;
r(n-2) = Dx(n-1)*yp(n-2) + Dx(n-2)*yp(n-1) +2*yp(n-2)*(Dx(n-1)+q);
stilde = T\r;
s1 = -stilde(1)+2*yp(1);
s1 = s1 + ((Dx(1)/Dx(2))^2)*(stilde(1)+stilde(2)-2*yp(2));
sn = -stilde(n-2) +2*yp(n-1);
sn = sn+((Dx(n-1)/Dx(n-2))^2)*(stilde(n-3)+stilde(n-2)-2*yp(n-2));
s = [s1;stilde;sn];
end

% Compute the a,b,c,d vectors.

a = y(1:n-1);
b = s(1:n-1);
c = (yp - s(1:n-1)) ./ Dx;
d = (s(2:n) + s(1:n-1) - 2*yp) ./ (Dx.* Dx);
Chapter 4 M-Files
M-File Home

Script File Index

ShowNCIdea Displays the idea behind the Newton-Cotes rules.


ShowQNC Computes Newton-Cotes estimates for specified integrands.
ShowNCError Illustrates NCerror.
ShowNCOpenError Illustrates NCOpenerror.
ShowCompQNC Illustrates CompQNC on three examples.
ShowAdapts Illustrates AdaptQNC.
ShowQuads Illustrates quad and quad8.
GLvsNC Compares Gauss-Legendre and Newton-Cotes rules.
ShowSplineQ Illustrates SplineQ.

Function File Index

NCWeights Constructs the Newton-Cotes weight vector.


QNC The simple Newton-Cotes rule.
NCError Error in the simple Newton-Cotes rule.
NCOpenWeights Constructs the open Newton-Cotes weight vector.
QNCOpen The simple open Newton-Cotes rule.
NCOpenError Error in the simple open Newton-Cotes rule.
CompQNC Equally-spaced, composite Newton-Cotes rule.
AdaptQNC Adaptive Newton-Cotes quadrature.
SpecHumps The humps function with function call counters.
GLWeights Constructs the Gauss-Legendre weight vector.
QGL The simple Gauss-Legendre rule.
SplineQ Spline quadrature.

ShowNCIdea

% Script File: ShowNCIdea


% Illustrates the idea behind the Newton-Cotes approach
% to quadrature.

close all

% Generate a function and its interpolant.


x = linspace(0,2);
y = exp(-x).*sin(2*pi*x);
x0 = linspace(0,2,8);
y0 = exp(-x0).*sin(2*pi*x0);
c = InterpN(x0,y0);
pvals = HornerN(c,x0,x);

% Plot the function.


subplot(2,2,1)
plot(x,y,x,zeros(1,100),'.')
title('f(x)');
axis([0 2 -.5 1])

% Plot the interpolant.


subplot(2,2,2)
plot(x,y,x,pvals,x0,y0,'*',x,zeros(1,100),'.')
title('The Interpolant');
axis([0 2 -.5 1])

% Display the integral of the function.


subplot(2,2,3)
plot(x,y)
fill(x,y,[.5 .5 .5])
title('Integral of f(x)');
axis([0 2 -.5 1])

% Display the integral of the interpolant


subplot(2,2,4)
plot(x,pvals)
fill(x,pvals,[.5 .5 .5])
title('Integral of Interpolant');
axis([0 2 -.5 1])

ShowQNC
% Script File: ShowQNC
% Examines the closed Newton-Cotes rules.

while input('Another exammple? (1=yes, 0=no)')


fname = input('Enter within quotes the name of the integrand function:');
a = input('Enter left endpoint: ');
b = input('Enter right endpoint: ');
s = ['QNC(''' fname '''' sprintf(',%6.3f,%6.3f,m )',a,b)];
clc
disp([' m ' s])
disp(' ')
for m=2:11
numI = QNC(fname,a,b,m);
disp(sprintf(' %2.0f %20.16f',m,numI))
end
end

ShowNCError
% Script File: ShowNCerror
% Examines the quality of the Newton-Cotes error bound.

clc
disp('Easy case: Integral from 0 to pi/2 of sin(x)')
disp(' ')
disp('Take DerBound = 1.')
disp(' ')
disp(' m QNC(m) Error Error Bound')
disp(' ')
for m=2:11
numI = QNC('sin',0,pi/2,m);
err = abs(numI-1);
errBound = NCerror(0,pi/2,m,1);
s = sprintf('%20.16f %10.3e %10.3e',numI,err,errBound);
disp([ sprintf(' %2.0f ',m) s])
end

ShowNCOpenError
% Script File: ShowNCOpenError
% Examines the quality of the Newton-Cotes error bound.

clc
disp('Easy case: Integral from 0 to pi/2 of sin(x)')
disp(' ')
disp('Take DerBound = 1.')
disp(' ')
disp(' m QNCOpen(m) Error Error Bound')
disp(' ')
for m=1:7
numI = QNCOpen('sin',0,pi/2,m);
err = abs(numI-1);
errBound = NCOpenError(0,pi/2,m,1);
s = sprintf('%20.16f %10.3e %10.3e',numI,err,errBound);
disp([ sprintf(' %2.0f ',m) s])
end

ShowCompQNC
% Script File: ShowCompQNC
% Illustrates composite Newton-Cotes rules on three different
% integrands.

% Show QNC(m,n) errors for integral of sin from 0 to pi/2.


close all
figure
for m = [3 5 7]
% m-point rule used.
err = [];
for n = [1 2 4 8 16 32]
% n = number of subintervals.
err = [err abs(compQNC('sin',0,pi/2,m,n) -1)+eps];
end
semilogy([1 2 4 8 16 32],err)
axis([0 40 10^(-17) 10^0])
text(33,err(6),sprintf('m = %1.0f',m))
hold on
end
title('Example 1. QNC(m,n) error for integral of sin from 0 to pi/2')
xlabel('n = number of subintervals.')
ylabel('Error in QNC(m,n)')

% Show QNC(m,n) errors for integral of sqrt from 0 to 1.


figure
for m = [3 5 7]
% m-point rule used.
err = [];
for n = [1 2 4 8 16 32]
% n = number of subintervals.
err = [err abs(compQNC('sqrt',0,1,m,n) - (2/3))+eps];
end
semilogy([1 2 4 8 16 32],err)
axis([0 40 10^(-5) 10^(-1)])
text(33,err(6),sprintf('m = %1.0f',m))
hold on
end
title('Example 2. QNC(m,n) error for integral of sqrt from 0 to 1')
xlabel('n = number of subintervals.')
ylabel('Error in QNC(m,n)')

% Show QNC(m,n) errors for integral of 1/(1+25x^2) from 0 to 1.


figure
for m = [3 5 7]
% m-point rule used.
err = [];
for n = [1 2 4 8 16 32]
% n = number of subintervals.
err = [err abs(compQNC('Runge',0,1,m,n) - (atan(5)/5))+eps;];
end
semilogy([1 2 4 8 16 32],err)
axis([0 40 10^(-17) 10^0])
text(33,err(6),sprintf('m = %1.0f',m))
hold on
end
title('Example 3. QNC(m,n) error for integral of 1/(1+25x^2) from 0 to 1')
xlabel('n = number of subintervals.')
ylabel('Error in QNC(m,n)')

ShowAdapts
% Script File: ShowAdapts
% Illustrates AdaptQNC for a range of tol values and a range of
% underlying NC rules. Uses the humps function for an integrand
% and displays information about the function evaluations.

global FunEvals VecFunEvals


close all
x = linspace(0,1,100);
y = humps(x);
plot(x,y)
u=[];
for tol = [.01 .001 .0001 .00001]
for m=3:2:9
figure
plot(x,y)
hold on
title(sprintf('m = %2.0f tol = %10.6f',m,tol))
FunEvals = 0;
VecFunEvals = 0;
num0 = AdaptQNC('SpecHumps',0,1,m,tol);
xLabel(sprintf('Scalar Evals = %3.0f Vector Evals = %3.0f',FunEvals,VecFunEvals))
hold off
u = [u; FunEvals VecFunEvals];
end
end
ShowQuads

% Script File: ShowQuads


% Uses quad and quad8 to estimate the integral of the
% humps function from 0 to 1.

clc
disp('Tolerance quad N-quad quad8 N-quad8')
disp('------------------------------------------------------')
for tol = logspace(-2,-6,5)
[Q1,count1] = quad('humps',0,1,tol);
s1 = sprintf(' %12.7f %5.0f',Q1,count1);
[Q2,count2] = quad8('humps',0,1,tol);
s2 = sprintf(' %12.7f %5.0f',Q2,count2);
disp([sprintf('%8.6f ',tol) s1 s2])
end

disp(sprintf('\n\n\n'))
disp(' alpha beta quad')
disp('----------------------------')
for alpha = [-3 -2 -1];
for beta = 1:3
G = quad('F4_3_2',0,1,.001,0,alpha,beta);
disp(sprintf(' %3.0f %3.0f %10.6f',alpha,beta,G))
end
end

GLvsNC

% Script File: GLvsNC


% Compares the m-point Newton-Cotes and Gauss-Legendre rules
% applied to the integral of sin(x) from 0 to pi/2.

clc
disp('Approximating the integral of sin from 0 to pi/2.')
disp(' ')
disp(' m NC(m) GL(m) ')
disp('------------------------------------------------')
for m=2:6
NC = QNC('sin',0,pi/2,m);
GL = QGL('sin',0,pi/2,m);
disp(sprintf(' %1.0f %20.16f %20.16f',m,NC,GL))
end

ShowSplineQ

% Script File: ShowSplineQ


% Examine spline quadrature on integral of sine from 0 to pi/2.

clc
disp(' m Spline Quadrature')
disp('----------------------------')
for m=[5 50 500]
x = linspace(0,pi/2,m);
y = sin(x);
numI = SplineQ(x,y);
disp(sprintf(' %3.0f %20.16f',m,numI))
end
NCWeights
function w = NCweights(m)
% w = NCweights(m)
%
% w is a column m-vector consisting of the weights for the m-point Newton-Cotes rule.
% m is an integer that satisfies 2 <= m <= 11.

if m==2, w=[1 1]'/2;


elseif m==3, w=[1 4 1]'/6;
elseif m==4, w=[1 3 3 1]'/8;
elseif m==5, w=[7 32 12 32 7]'/90;
elseif m==6, w=[19 75 50 50 75 19]'/288;
elseif m==7, w=[41 216 27 272 27 216 41]'/840;
elseif m==8, w=[751 3577 1323 2989 2989 1323 3577 751]'/17280;
elseif m==9, w=[989 5888 -928 10496 -4540 10496 -928 5888 989]'/28350;
elseif m==10, w=[2857 15741 1080 19344 5778 5778 19344 1080 15741 2857]'/89600;
else w=[16067 106300 -48525 272400 -260550 427368 -260550 272400 -48525 106300 16067]'/598752;
end

QNC
function numI = QNC(fname,a,b,m)
% numI = QNC(fname,a,b,m)
%
% Integrates a function of the form f(x) named by the string fname from a to b.
% f must be defined on [a,b] and it must return a column vector if x is a column vector.
% m is an integer that satisfies 2 <= m <= 11.
% numI is the m-point Newton-Cotes approximation of the integral of f from a to b.

w = NCweights(m);
x = linspace(a,b,m)';
f = feval(fname,x);
numI = (b-a)*(w'*f);

NCError
function error = NCerror(a,b,m,M)
% error = NCerror(a,b,m,M)
%
% The error bound for the m-point Newton Cotes rule when applied to
% the integral from a to b of a function f(x). It is assumed that
% a<=b and 2<=m<=11. M is an upper bound for the (d+1)-st derivative of the
% function f(x) on [a,b] where d = m if m is odd, and m-1 if m is even.

if m==2, d=1; c = -1/12;


elseif m==3, d=3; c = -1/90;
elseif m==4, d=3; c = -3/80;
elseif m==5, d=5; c = -8/945;
elseif m==6, d=5; c = -275/12096;
elseif m==7, d=7; c = -9/1400;
elseif m==8, d=7; c = -8183/518400;
elseif m==9, d=9; c = -2368/467775;
elseif m==10, d=9; c = -173/14620;
else d=11; c = -1346350/326918592;
end
h = (b-a)/(m-1);
error = abs(c*M*h^(d+2));
NCOpenWeights

function w = NCOpenWeights(m)
% w = NCOpenWeights(m)
%
% w is a column m-vector consisting of the weights for the m-point open
% Newton-Cotes rule. m is an integer that satisfies 1 <= m <= 7.

if m==1, w = [1];
elseif m==2, w = [1 1]'/2;
elseif m==3, w = [2 -1 2]'/3;
elseif m==4, w = [11 1 1 11]'/24;
elseif m==5, w = [11 -14 26 -14 11]'/20;
elseif m==6, w = [611 -453 562 562 -453 611]'/1440;
else w = [460 -954 2196 -2459 2196 -954 460]'/945;
end

QNCOpen

function numI = QNCOpen(fname,a,b,m)


% numI = QNC(fname,a,b,m,tol)
%
% Integrates a function of the form f(x) named by the string fname from a to b.
% f must be defined on [a,b] and it must return a column vector if x is a column vector.
% m is an integer that satisfies 1 <= m <= 9.
% numI is the m-point open Newton-Cotes approximation of the integral of f from a to b.

w = NCOpenWeights(m);
h = (b-a)/(m+1);
x = linspace(a+h,b-h,m)';
f = feval(fname,x);
numI = (b-a)*(w'*f);

NCOpenError
function error = NCOpenError(a,b,m,M)
% error = NCOpenError(a,b,m,M)
%
% The error bound for the m-point Newton Cotes rule when applied to
% the integral from a to b of a function f(x). It is assumed that
% a<=b and 1<=m<=7. M is an upper bound for the (d+1)-st derivative of the
% function f(x) on [a,b] where d = m if m is odd, and m-1 if m is even.

if m==1, d=1; c = 1/3;


elseif m==2, d=1; c = 1/4;
elseif m==3, d=3; c = 28/90;
elseif m==4, d=3; c = 95/144;
elseif m==5, d=5; c = 41/140;
elseif m==6, d=5; c = 5257/8640;
else d=7; c = 3956/14175;
end
h = (b-a)/(m+1);
error = c*M*h^(d+2);

CompQNC
function numI = CompQNC(fname,a,b,m,n)
% numI = CompQNC(fname,a,b,m)
%
% Integrates a function of the form f(x) named by the string fname from a to b.
% f must be defined on [a,b] and it must return a column vector if x is a column vector.
% m is an integer that satisfies 2 <= m <= 11.
% numI is the composite m-point Newton-Cotes approximation of the integral of f
% from a to b with n equal length subintervals.

Delta = (b-a)/n;
h = Delta/(m-1);
x = a+h*(0:(n*(m-1)))';
w = NCWeights(m);
x = linspace(a,b,n*(m-1)+1)';
f = feval(fname,x);
numI = 0;
first = 1;
last = m;
for i=1:n
%Add in the inner product for the i-th subintegral.
numI = numI + w'*f(first:last);
first = last;
last = last+m-1;
end
numI = Delta*numI;

AdaptQNC
function numI = AdaptQNC(fname,a,b,m,tol)
% numI = AdaptQNC(fname,a,b,m,tol)
%
% Integrates a function from a to b
% fname is a string that names an available function of the form f(x) that
% is defined on [a,b]. f should return a column vector if x is a column vector.
% a,b are real scalars, m is an integer that satisfies 2 <= m <=11, and
% tol is a positive real.
%
% numI is a composite m-point Newton-Cotes approximation of the
% integral of f(x) from a to b, with the abscissae chosen adaptively.

A1 = CompQNC(fname,a,b,m,1);
A2 = CompQNC(fname,a,b,m,2);
d = 2*floor((m-1)/2)+1;
error = (A2-A1)/(2^(d+1)-1);
if abs(error) <= tol
% A2 is acceptable
numI = A2+error;
else
% Sum suitably accurate left and right integrals
mid = (a+b)/2;
numI = AdaptQNC(fname,a,mid,m,tol/2) + AdaptQNC(fname,mid,b,m,tol/2);
end

SpecHumps

function y = SpecHumps(x)
% y = SpecHumps(x)
%
% Yields humps(x) where x is an n-vector.
% FunEvals is an initialized global scalar that is increased by length(x).
% VecFunEvals is an initialized global scalar that is increased by 1.

global FunEvals VecFunEvals;


y = 1 ./ ((x-.3).^2 + .01) + 1 ./ ((x-.9).^2 + .04) - 6;
hold on
plot(x,y,'*')
hold off
FunEvals = FunEvals + length(x);
VecFunEvals = VecFunEvals + 1;

GLWeights

function [w,x] = GLWeights(m)


% [w,x] = GLWeights(m)
%
% w is a column m-vector consisting of the weights for the m-point Gauss-Legendre rule.
% x is a column m-vector consisting of the abscissae.
% m is an integer that satisfies 2 <= m <= 6.

w = ones(m,1);
x = ones(m,1);
if m==2
w(1) = 1.000000000000000; w(2) = w(1);
x(1) = -0.577350269189626; x(2) = -x(1);
elseif m==3
w(1) = 0.555555555555558; w(3) = w(1);
w(2) = 0.888888888888889;
x(1) = -0.774596669241483; x(3) = -x(1);
x(2) = 0.000000000000000;
elseif m==4
w(1) = 0.347854845137454; w(4) = w(1);
w(2) = 0.652145154862546; w(3) = w(2);
x(1) = -0.861136311594053; x(4) = -x(1);
x(2) = -0.339981043584856; x(3) = -x(2);
elseif m==5
w(1) = 0.236926885056189; w(5) = w(1);
w(2) = 0.478628670499366; w(4) = w(2);
w(3) = 0.568888888888889;
x(1) = -0.906179845938664; x(5) = -x(1);
x(2) = -0.538469310105683; x(4) = -x(2);
x(3) = 0.000000000000000;
else
w(1) = 0.171324492379170; w(6) = w(1);
w(2) = 0.360761573048139; w(5) = w(2);
w(3) = 0.467913934572691; w(4) = w(3);
x(1) = -0.932469514203152; x(6) = -x(1);
x(2) = -0.661209386466265; x(5) = -x(2);
x(3) = -0.238619186083197; x(4) = -x(3);
end

QGL
function numI = QGL(fname,a,b,m)
% numI = QGL(fname,a,b,m,tol)
%
% Integrates a function from a to b
% fname is a string that names an available function of the form f(x) that
% is defined on [a,b]. f should return a column vector if x is a column vector.
% a,b are real scalars, m is an integer that satisfies 2 <= m <= 6.
%
% numI is the m-point Gauss-Legendre approximation of the
% integral of f(x) from a to b.

[w,x] = GLWeights(m);
fvals = feval(fname,((b-a)/2)*x + ((a+b)/2)*ones(m,1));
numI = ((b-a)/2)*w'*fvals;

SplineQ
function numI = SplineQ(x,y)
% numI = SplineQ(x,y)
%
% Integrates the spline interpolant of the data specified by the
% column n-vectors x and y. It is a assumed that x(1) < ... < x(n)
% and that the spline is produced by SPLINE. The integral is from
% x(1) to x(n).

S = spline(x,y);
[x,rho,L,k] = unmkpp(S);
sum = 0;
for i=1:L
% Add in the integral from x(i) to x(i+1).
h = x(i+1)-x(i);
subI = h*(((rho(i,1)*h/4 + rho(i,2)/3)*h + rho(i,3)/2)*h + rho(i,4));
sum = sum + subI;
end
numI = sum;
Chapter 5 M-Files
M-File Home

Script Files

CircBench Benchmarks Circulant1 and Circulant2.


MatBench Benchmarks various matrix-multiply methods.
AveNorms Compares various norms on random vectors.
ProdBound Examines the error in three-digit matrix multiplication.
ShowContour Displays various contour plots of SampleF.
Show2DQuad Illustrates CompQNC2D.
FFTflops Compares FFT and DFT flops.
StrassFlops Examines Strassen multiply.

Function Files

Circulant1 Scalar-level circulant matrix set-up.


Circulant2 Vector-level circulant matrix set-up.
MatVecRO Row-oriented matrix-vector product.
MatVecCO Column-Oriented matrix-vector product.
MatMatDot Dot-product matrix-matrix product.
MatMatSax Saxpy matrix-matrix product.
MatMatVec Matrix-vector matrix-matrix product.
MatMatOuter Outer product matrix-matrix product.
MakeBlock Makes a cell array representation of a block matrix.
ShowSparse Illustrates sparse.
ShowNonZeros Displays the sparsity pattern of a matrix.
Prod3Digit Three-digit matrix-matrix product.
SampleF A Gaussian type function of two variables.
CompQNC2D Two-dimensional Newton-Cotes rules.
wCompNC Weight vector for composite Newton-Cotes rules.
SampleF2 A function of two variables with strong local maxima.
DFT Discrete Fourier transform.
FFTRecur A recursive radix-2 FFT.
Strass Recursive Strassen matrix multiply.

CircBench
% Script File: CircBench
% Benchmarks Circulant1 and Circulant2.

clc
nRepeat = 20;
disp('t1 = time for Circulant1.')
disp('t2 = time for Circulant2.')
disp(' ')
disp(' n t1/t2 ')
disp('----------------')
for n=[100 200 400]
a = 1:n;
tic; for k=1:nRepeat, C = Circulant1(a); end, t1 = toc;
tic; for k=1:nRepeat, C = Circulant2(a); end, t2 = toc;
disp(sprintf(' %4.0f %5.3f',n,t1/t2))
end

MatBench
% Script File: MatBench
% Compare different matrix multiplication methods.

clc
nRepeat = 10;
disp(' n Dot Saxpy MatVec Outer Direct')
disp('------------------------------------------------')
for n = [100 200 400]
A = rand(n,n);
B = rand(n,n);
tic; for k=1:nRepeat,C = MatMatDot(A,B); end, t1 = toc/nRepeat;
tic; for k=1:nRepeat,C = MatMatSax(A,B); end, t2 = toc/nRepeat;
tic; for k=1:nRepeat,C = MatMatVec(A,B); end, t3 = toc/nRepeat;
tic; for k=1:nRepeat,C = MatMatOuter(A,B); end, t4 = toc/nRepeat;
tic; for k=1:nRepeat,C = A*B; end, t5 = toc/nRepeat;
disp(sprintf('%2.0f %7.4f %7.4f %7.4f %7.4f %7.4f ',n,t1,t2,t3,t4,t5))
end

AveNorms
% Script File: AveNorms
% Examines average value of norms.

clc
disp('r1 = norm(x,1)/norm(x,''inf'')')
disp('r2 = norm(x,2)/norm(x,''inf'')')
disp(' ')
disp(' n r1 r2')
disp('----------------------------------')
r = 100;
for n = 10:10:100
s1 = 0;
s2 = 0;
for k=1:r
x = randn(n,1);
s1 = s1 + norm(x,1)/norm(x,'inf');
s2 = s2 + norm(x,2)/norm(x,'inf');
end
disp(sprintf(' %4d %10.3f %10.3f',n,s1/r,s2/r))
end
ProdBound
% Script File: ProdBound
% Examines the error in 3-digit matrix multiplication.

clc
eps3 = .005; % 3-digit machine precision
nRepeat = 10; % Number of trials per n-value
disp(' n 1-norm factor ')
disp('------------------------')
for n = 2:10
s = 0;
for r=1:nRepeat
A = randn(n,n);
B = randn(n,n);
C = Prod3Digit(A,B);
E = C - A*B;
s = s+ norm(E,1)/(eps3*norm(A,1)*norm(B,1));
end
disp(sprintf('%4.0f %8.3f ',n,s/nRepeat))
end

ShowContour
% Script File: ShowContour
% Illustrates various contour plots.

close all
% Set up array of function values.
x = linspace(-2,2,50)';
y = linspace(-1.5,1.5,50)';
F = SampleF(x,y);

% Number of contours set to default value:


figure
Contour(x,y,F)
axis equal

% Five contours:
figure
contour(x,y,F,5);
axis equal

% Five contours with specified values:


figure
contour(x,y,F,[1 .8 .6 .4 .2])
axis equal

% Four contours with manual labeling:


figure
c= contour(x,y,F,4);
clabel(c,'manual');
axis equal

Show2DQuad
% Script File: Show2DQuad
% Integral of SampleF2 over [0,2]x[0,2] for various 2D composite
% QNC(7) rules.

clc
m = 7;
disp(' Subintervals Integral Time')
disp('------------------------------------------------')
for n = [32 64 128 256]
tic, numI2D = CompQNC2D('SampleF2',0,2,0,2,m,n,m,n); time = toc;
disp(sprintf(' %7.0f %17.15f %11.4f',n,numI2D,time))
end

FFTflops

% Script File: FFTflops


% Compares the ordinary DFT with the FFT.

clc
x = randn(1024,1)+sqrt(-1)*randn(1024,1);
disp(' n DFT FFTRecur FFT ')
disp(' Flops Flops Flops')
disp('------------------------------------------')
for n = [2 4 8 16 32 64 128 256 512 1024]
flops(0); y = dft(x(1:n)); F1 = flops;
flops(0); y = FFTRecur(x(1:n)); F2 = flops;
flops(0); y = fft(x(1:n)); F3 = flops;
disp(sprintf(' %4d %10d %10d %10d', n,F1,F2,F3))
end

StrassFlops

% Script File: StrassFlops


% Compares Strassen method flops with ordinary A*B flops.

clc
nmin = 32;
nmax = 1024;
A0 = rand(nmax,nmax);
B0 = rand(nmax,nmax);
disp(' n Strass Flops/Ordinary Flops')
disp('-------------------------------------')
n = 32;
while (n<=nmax)
A = A0(1:n,1:n);
B = B0(1:n,1:n);
flops(0);
C = Strass(A,B,nmin);
f = flops;
disp(sprintf(' %4d %6.3f', n,f/(2*n^3)))
n = 2*n;
end

Circulant1

function C = Circulant1(a)
% C = Circulant1(a) is a circulant matrix with first row equal to a.

n = length(a);
C = zeros(n,n);
for i=1:n
for j=1:n
C(i,j) = a(rem(n-i+j,n)+1);
end
end

Circulant2

function C = Circulant2(a)
% C = Circulant2(a) is a circulant matrix with first row equal to a.

n = length(a);
C = zeros(n,n);
C(1,:) = a;
for i=2:n
C(i,:) = [ C(i-1,n) C(i-1,1:n-1) ];
end

MatVecRO
function y = MatVecR0(A,x)
% y = MatVecRO(A,x)
% Computes the matrix-vector product y = A*x (via saxpys) where
% A is an m-by-n matrix and x is a column n-vector.

[m,n] = size(A);
y = zeros(m,1);
for i=1:m
y(i) = A(i,:)*x;
end

MatVecCO

function y = MatVecC0(A,x)
% y = MatVecCO(A,x)
% This computes the matrix-vector product y = A*x (via saxpys) where
% A is an m-by-n matrix and x is a columnn-vector.

[m,n] = size(A);
y = zeros(m,1);
for j=1:n
y = y + A(:,j)*x(j);
end

MatMatDot

function C = MatMatDot(A,B)
% C = MatMatDot(A,B)
% This computes the matrix-matrix product C =A*B (via dot products) where
% A is an m-by-p matrix, B is a p-by-n matrix.

[m,p] = size(A);
[p,n] = size(B);
C = zeros(m,n);
for j=1:n
% Compute j-th column of C.
for i=1:m
C(i,j) = A(i,:)*B(:,j);
end
end

MatMatSax

function C = MatMatSax(A,B)
% C = MatMatSax(A,B)
% This computes the matrix-matrix product C = A*B (via saxpys) where
% A is an m-by-p matrix, B is a p-by-n matrix.

[m,p] = size(A);
[p,n] = size(B);
C = zeros(m,n);
for j=1:n
% Compute j-th column of C.
for k=1:p
C(:,j) = C(:,j) + A(:,k)*B(k,j);
end
end

MatMatVec

function C = MatMatVec(A,B)
% C = MatMatVec(A,B)
% This computes the matrix-matrix product C = A*B (via matrix-vector products)
% where A is an m-by-p matrix, B is a p-by-n matrix.

[m,p] = size(A);
[p,n] = size(B);
C = zeros(m,n);
for j=1:n
% Compute j-th column of C.
C(:,j) = C(:,j) + A*B(:,j);
end

MatMatOuter

function C = MatMatOuter(A,B)
% C = MatMatOuter(A,B)
% This computes the matrix-matrix product C = A*B (via outer products) where
% A is an m-by-p matrix, B is a p-by-n matrix.

[m,p] = size(A);
[p,n] = size(B);
C = zeros(m,n);
for k=1:p
% Add in k-th outer product
C = C + A(:,k)*B(k,:);
end

MakeBlock

function A = MakeBlock(A_scalar,p)
% A = MakeBlock(A_scalar,p)
% A_scalar is an n-by-n matrix and p divides n. A is an (n/p)-by-(n/p)
% cell array that represents A_scalar as a block matrix with p-by-p blocks.

[n,n] = size(A_scalar);
m = n/p;
A = cell(m,m);
for i=1:m
for j=1:m
A{i,j} = A_scalar(1+(i-1)*p:i*p,1+(j-1)*p:j*p);
end
end

ShowSparse

% Script File: ShowSparse


% Looks at the effect of the sparse operation on tridiagonal matrix products.

close all
nvals = 100:100:1000;
repeatVal = [200 200 100 50 20 10 10 10 10 5];
for k=1:length(nvals)
n = nvals(k); x = randn(n,1);
A = diag(2*(1:n)) - diag(1:n-1,-1) + diag(1:n-1,1);
S = sparse(A);
flops(0), y = A*x; fullA_flops(k) = flops;
flops(0), y = S*x; sparseA_flops(k) = flops;
end
semilogy(nvals,fullA_flops,nvals,sparseA_flops)
xlabel('n')
title('(Tridiagonal A) \times (Vector)')
legend('Flops with Full A','Flops with Sparse A',2)

ShowNonZeros
function ShowNonZeros(A)
% Displays the sparsity structure of an n-by-n matrix A.
% It is best that n<=30.

close all
[n,n] = size(A);
s = 1/n;
m = round(200/n);
figure
axis([0 1 0 1])
axis ij equal off
HA = 'HorizontalAlignment';
VA = 'VerticalAlignment';
for i=1:n
for j=1:n
if A(i,j)==0
text(i*s,(j-.3)*s,'.',HA,'center');
else
text(i*s,j*s,'X','FontWeight','bold','FontSize',m,HA,'center');
end
end
end

Prod3Digit
function C = Prod3Digit(A,B)
% C = Prod3Digit(A,B)
% This computes the matrix product C = A*B in 3-digit floating point
% arithmetic. A is m-by-p and B is p-by-n.

[m,p] = size(A);
[p,n] = size(B);
C = zeros(m,n);
for i=1:m
for j=1:n
s = represent(0);
for k=1:p
aik = Represent(A(i,k));
bkj = Represent(B(k,j));
s = Float(s,Float(aik,bkj,'*'),'+');
end
C(i,j) = Convert(s);
end
end

SampleF
function F = SampleF(x,y)
% x is a column n-vector, y is a column m-vector and
% F is an m-by-n matrix with F(i,j) = exp(-(x(j)^2 + 3y(i)^2)).

n = length(x);
m = length(y);
A = -((2*y.^2)*ones(1,n) + ones(m,1)*(x.^2)')/4;
F = exp(A);

CompQNC2D
function numI2D = CompQNC2D(fname,a,b,c,d,mx,nx,my,ny)
% numI2D = CompQNC2D(fname,a,b,c,d,mx,nx,my,ny)
%
% fname is a string that names a function of the form f(x,y).
% If x and y are vectors, then it should return a matrix F with the
% property that F(i,j) = f(x(i),y(j)), i=1:length(x), j=1:length(y).
%
% a,b,c,d are real scalars.
% mx and my are integers that satisfy 2<=mx<=11, 2<=my<=11.
% nx and ny are positive integers
%
% numI2D approximation to the integral of f(x,y) over the rectangle [a,b]x[c,d].
% The compQNC(mx,nx) rule is used in the x-direction and the compQNC(my,ny)
% rule is used in the y-direction.

[omega,x] = wCompNC(a,b,mx,nx);
[mu,y] = wCompNC(c,d,my,ny);
F = feval(fname,x,y);
numI2D = (b-a)*(d-c)*(omega'*F*mu);

wCompNC
function [omega,x] = wCompNC(a,b,m,n)
% [omega,x] = wCompNC(a,b,m,n)
%
% m is an integer with 2<=m<=11 and n is a positive integer.
%
% omega is a column vector of weights for the composite m-point
% Newton-Cotes rule with n equal-length subintervals across [a,b]
% x is column vector of abscissas.

N = n*(m-1)+1;
x = linspace(a,b,N)';
w = NCWeights(m);
omega = zeros(N,1);
for k=1:(m-1):N-m+1
omega(k:k+m-1) = omega(k:k+m-1) + w;
end
omega = omega/n;

SampleF2

function F = SampleF2(x,y)
% A function of 2 variables with strong local maxima at (.3,.4) and (.7,.3).

F = zeros(length(y),length(x));
for j=1:length(x)
F(:,j) = 1./( ((x(j)-.3).^2 + .1)*((y-.4).^2 + .1)) + 1./( ((x(j)-.7).^2 + .2)*((y-.3).^2 + .2)) - 6;
end

DFT
function y = DFT(x)
% y = DFT(x)
% y is the discrete Fourier transform of a column n-vector x.

n = length(x);
y = x(1)*ones(n,1);
if n > 1
v = exp(-2*pi*sqrt(-1)/n).^(0:n-1)';
for k=2:n
z = rem((k-1)*(0:n-1)',n ) +1;
y = y + v(z)*x(k);
end
end

FFTRecur
function y = FFTRecur(x)
% y = FFTRecur(x)
% y is the discrete Fourier transform of a column n-vector x where
% n is a power of two.

n = length(x);
if n ==1
y = x;
else
m = n/2;
yT = FFTRecur(x(1:2:n));
yB = FFTRecur(x(2:2:n));
d = exp(-2*pi*sqrt(-1)/n).^(0:m-1)';
z = d.*yB;
y = [ yT+z ; yT-z ];
end

Strass
function C = Strass(A,B,nmin)
% C = Strass(A,B,nmin)
% This computes the matrix-matrix product C = A*B (via the Strassen Method) where
% A is an n-by-n matrix, B is a n-by-n matrix and n is a power of two. Conventional
% matrix multiplication is used if n < nmin where nmin is a positive integer.

[n,n] = size(A);
if n < nmin
C = A*B;
else
m = n/2; u = 1:m; v = m+1:n;
P1 = Strass(A(u,u)+A(v,v),B(u,u)+B(v,v),nmin);
P2 = Strass(A(v,u)+A(v,v),B(u,u),nmin);
P3 = Strass(A(u,u),B(u,v)-B(v,v),nmin);
P4 = Strass(A(v,v),B(v,u)-B(u,u),nmin);
P5 = Strass(A(u,u)+A(u,v),B(v,v),nmin);
P6 = Strass(A(v,u)-A(u,u),B(u,u) + B(u,v),nmin);
P7 = Strass(A(u,v)-A(v,v),B(v,u)+B(v,v),nmin);
C = [ P1+P4-P5+P7 P3+P5; P2+P4 P1+P3-P2+P6];
end
Chapter 6 M-Files
M-File Home

Script Files

ShowTri Uses LTriSol to get inverse of Forsythe Matrix.


ShowTriD Illustrates tridiagonal system solving.
ShowHessLU Illustrates Hessenberg LU factorization.
ShowGE Illustrates Gaussian Elimination.
ShowSparseSolve Examines $\backslash$ with sparse arrays.
NoPivot Illustrates the dangers of no pivoting.
ShowGEPiv Illustrates GEPiv.
ShowGE2 Applies GE2 to three different examples.
CondEgs Accuracy of some ill-conditioned Pascal linear systems.

Function Files

LTriSol Solves lower triangular system Lx = b.


UTriSol Solves upper triangular system Ux = b.
LTriSolM Solves multiple right-hand-side lower triangular systems.
TriDiLU LU factorization of a tridiagonal matrix.
LBiDiSol Solves lower bidiagonal systems.
UBiDiSol Solves upper bidiagonal systems.
HessLU Hessenberg LU factorization.
GE General LU factorization without pivoting.
GEpiv General LU factorization with pivoting.
GE2 Illustrates 2-by-2 GE in three-digit arithmetic.

ShowTri
% Script File: ShowTri
% Inverse of the 5-by-5 Forsythe Matrix.

n = 5;
L = eye(n,n) - tril(ones(n,n),-1)
X = LTriSolM(L,eye(n,n))
ShowTriD
% Script File: ShowTriD
% Tridiagonal Solver test

clc
disp('Set L and U as follows:')
L = [1 0 0 0 0; 2 1 0 0 0 ; 0 3 1 0 0; 0 0 4 1 0; 0 0 0 5 1]
U = [2 3 0 0 0; 0 1 -1 0 0; 0 0 2 1 0; 0 0 0 3 1; 0 0 0 0 6]
%
input('Strike Any Key To Continue');
clc
disp('Form A = LU and set b so solution to Ax=b is ones(5,1).')
pause(2)
A = L*U
b = A*ones(5,1)
input('Strike Any Key To Continue');
%
clc
disp('Extract diagonal part of A:')
A = A
d = diag(A)'
input('Strike Any Key To Continue');
clc
disp('Extract subdiagonal part of A:')
A = A
e(2:5) = diag(A,-1)
input('Strike Any Key To Continue');
clc
disp('Extract superdiagonal part of A:')
A = A
f(1:4) = diag(A,1)
input('Strike Any Key To Continue');
%
clc
disp('Solve Ax = b via TriDiLU, LBiDisol, and UBiDiSol')
[l,u] = TriDiLU(d,e,f);
y = LBiDiSol(l,b);
x = UBiDiSol(u,f,y)

ShowHessLU
% Script File: ShowHessLU
% Illustrates computation of a 5-by-5 LU factorization
% of upper Hessenberg system without pivoting.

clc
disp('Steps through the LU factorization of a 5-by-5')
disp('upper Hessenberg matrix.')
input('Strike Any Key to Continue');
clc
n = 5;
A = rand(n,n);
A = triu(A,-1);
[n,n] = size(A);
v = zeros(n,1);
for k=1:n-1
clc
Before = A
v(k+1) = A(k+1,k)/A(k,k);
disp(sprintf('Zero A(%1.0f,%1.0f)',k+1,k))
disp(sprintf('Multiplier = %7.4f / %7.4f = %7.4f',A(k+1,k),A(k,k),v(k+1)))
A(k+1,k:n) = A(k+1,k:n) - v(k+1)*A(k,k:n);
After = A
input('Strike Any Key to Continue')
end
U = A;

ShowGE
% Script File: ShowGE
% Illustrates computation of a 5-by-5 LU factorization
% without pivoting.

clc
format short
disp('Steps through the LU factorization of a 5-by-5 matrix')
input('Strike Any Key to Continue.');
clc
n = 5;
A = magic(5);
ASave = A;
[n,n] = size(A);
v = zeros(n,1);
L = eye(n,n);
for k=1:n-1
v(k+1:n) = A(k+1:n,k)/A(k,k);
for i=k+1:n
clc
Before = A
disp(sprintf('Multiply row %1.0f by %7.4f / %7.4f ',k,A(i,k),A(k,k)))
disp(sprintf('and subtract from row %1.0f:',i))
A(i,k:n) = A(i,k:n) - v(i)*A(k,k:n);
After = A
input('Strike Any Key to Continue.');
end
clc
disp(sprintf('Have computed column %1.0f of L.',k))
disp(' ')
disp('Here is L so far:')
L(k+1:n,k) = v(k+1:n)
input('Strike Any Key to Continue.');
end
clc
disp('Factorization is complete:')
L = L
U = triu(A)
pause
clc
disp('Error check. Look at A - L*U:')
Error = ASave - L*U

ShowSparseSolve
% Script File: ShowSparseSolve
% Illustrates how the \ operator can exploit sparsity

clc
disp(' n Flops Full Flops Sparse ')
disp('------------------------------------------')
for n=[25 50 100 200 400 800]
T = randn(n,n)+1000*eye(n,n);
T = triu(tril(T,1),-1); T(:,1) = .1; T(1,:) = .1;
b = randn(n,1);
flops(0); x = T\b; fullFlops = flops;
T_sparse = sparse(T);
flops(0); x = T_sparse\b; sparseFlops = flops;
disp(sprintf('%10d %10d %10d ',n,fullFlops,sparseFlops))
end

NoPivot
% Script File: NoPivot
% Examines solution to
%
% [ delta 1 ; 1 1][x1;x2] = [1+delta;2]
%
% for a sequence of diminishing delta values.

clc
disp(' Delta x(1) x(2) ' )
disp('-----------------------------------------------------')
for delta = logspace(-2,-18,9)
A = [delta 1; 1 1];
b = [1+delta; 2];
L = [ 1 0; A(2,1)/A(1,1) 1];
U = [ A(1,1) A(1,2) ; 0 A(2,2)-L(2,1)*A(1,2)];
y(1) = b(1);
y(2) = b(2) - L(2,1)*y(1);
x(2) = y(2)/U(2,2);
x(1) = (y(1) - U(1,2)*x(2))/U(1,1);
disp(sprintf(' %5.0e %20.15f %20.15f',delta,x(1),x(2)))
end

ShowGEPiv
% Script File: ShowGEpiv
% Illustrates computation of a 5-by-5 LU factorization
% with pivoting.

clc
format short
disp('Steps through Gaussian elimination of a 5-by-5')
disp('matrix showing pivoting.')
input('Strike Any Key to Continue.');
clc
n = 5;
A = magic(5);
[n,n] = size(A);
L = eye(n,n);
piv = 1:n;
for k=1:n-1
clc
[maxv,r] = max(abs(A(k:n,k)));
q = r+k-1;
Before = A
disp(sprintf('piv = [ %1.0f %1.0f %1.0f %1.0f %1.0f]',piv(1),piv(2),piv(3),piv(4),piv(5)))
disp(' ')
disp(sprintf('Interchange rows k = %1.0f and q = %1.0f',k,q))
piv([k q]) = piv([q k]);
A([k q],:) = A([q k],:);
After = A
disp(sprintf('piv = [ %1.0f %1.0f %1.0f %1.0f %1.0f]',piv(1),piv(2),piv(3),piv(4),piv(5)))
disp(' ')
disp(sprintf('Zero A(%1.0f:%1.0f,%1.0f):',k,k+1,k))
input('Strike Any Key to Continue.');
clc
if A(k,k) ~= 0
L(k+1:n,k) = A(k+1:n,k)/A(k,k);
A(k+1:n,k+1:n) = A(k+1:n,k+1:n) - L(k+1:n,k)*A(k,k+1:n);
A(k+1:n,k) = zeros(n-k,1);
end
end
clc
home
L=L
U = A
piv = piv

ShowGE2
% Script File: ShowGE2
% Illustrates 2-by-2 gaussian elimination in 3-digit floating point
% arithmetic.

A = [.981 .726; .529 .394];


ge2(A)
disp('Strike any key for next example.')
pause

A = [.981 .726; .529 .384];


ge2(A)
disp('Strike any key for next example.')
pause

A = [.981 .726; .529 .294];


ge2(A)

CondEgs
% Script File: CondEgs
% Examines errors for a family of linear equation problems.

for n = [4 8 12 16]
clc
A = pascal(n);
disp(sprintf('cond(pascal(%2.0f)) = %8.4e',n,cond(A)));
disp('True solution is vector of ones. Computed solution =')
xTrue = ones(n,1);
b = A*xTrue;
format long
[L,U,piv] = GEpiv(A);
y = LTriSol(L,b(piv));
x = UTriSol(U,y)
format short
relerr = norm(x - xTrue)/norm(xTrue);
disp(sprintf('Relative error = %8.4e',relerr))
bound = eps*cond(A);
disp(sprintf('Predicted value = EPS*cond(A) = %8.4e',bound))
input('Strike Any Key to Continue.');
end

LTriSol
function x = LTriSol(L,b)
% x = LTriSol(L,b)
%
% Solves the nonsingular lower triangular system Lx = b
% where L is n-by-n, b is n-by-1, and x is n-by-1.

n = length(b);
x = zeros(n,1);
for j=1:n-1
x(j) = b(j)/L(j,j);
b(j+1:n) = b(j+1:n) - L(j+1:n,j)*x(j);
end
x(n) = b(n)/L(n,n);

UTriSol
function x = UTriSol(U,b)
% x = UTriSol(U,b)
%
% Solves the nonsingular upper triangular system Ux = b.
% where U is n-by-n, b is n-by-1, and X is n-by-1.

n = length(b);
x = zeros(n,1);
for j=n:-1:2
x(j) = b(j)/U(j,j);
b(1:j-1) = b(1:j-1) - x(j)*U(1:j-1,j);
end
x(1) = b(1)/U(1,1);

LTriSolM
function X = LTriSolM(L,B)
% X = LTriSolM(L,B)
%
% Solves the nonsingular lower triangular system LX = B
% where L is n-by-n, B is n-by-r, and X is n-by-r.

[n,r] = size(B);
X = zeros(n,r);
for j=1:n-1
X(j,1:r) = B(j,1:r)/L(j,j);
B(j+1:n,1:r) = B(j+1:n,1:r) - L(j+1:n,j)*X(j,1:r);
end
X(n,1:r) = B(n,1:r)/L(n,n);

TriDiLU
% [l,u] = TriDiLU(d,e,f)
%
% Tridiagonal LU without pivoting. d,e,f are n-vectors that define a tridiagonal matrix
% A = diag(e(2:n),-1) + diag(d) + diag(f(1:n-1),1). Assume that A has an LU factorization.
% l and u are n-vectors with the property that if L = eye + diag(l(2:n),-1)
% and U = diag(u) + diag(f(1:n-1),1), then A = LU.

n = length(d);
l = zeros(n,1);
u = zeros(n,1);
u(1) = d(1);
for i=2:n
l(i) = e(i)/u(i-1);
u(i) = d(i) - l(i)*f(i-1);
end

LBiDiSol
function x = LBiDiSol(l,b)
% x = LBiDiSol(l,b)
%
% Solves the n-by-n unit lower bidiagonal system Lx = b
% where l and b are n-by-1 and L = I + diag(l(2:n),-1).

n = length(b);
x = zeros(n,1);
x(1) = b(1);
for i=2:n
x(i) = b(i) - l(i)*x(i-1);
end

UBiDiSol
function x = UBiDiSol(u,f,b)
% x = UBiDiSol(u,f,b)
%
% Solves the n-by-n nonsingular upper bidiagonal system Ux = b
% where u, f, and b are n-by-1 and U = diag(u) + diag(f(1:n-1),1).

n = length(b);
x = zeros(n,1);
x(n) = b(n)/u(n);
for i=n-1:-1:1
x(i) = (b(i) - f(i)*x(i+1))/u(i);
end

HessLU
function [v,U] = HessLU(A)
% [v,U] = HessLU(A)
%
% Computes the factorization H = LU where H is an n-by-n upper Hessenberg
% and L is an n-by-n lower unit triangular and U is an n-by-n upper triangular
% matrix.
%
% v is a column n-by-1 vector with the property that L = I + diag(v(2:n),-1).

[n,n] = size(A);
v = zeros(n,1);
for k=1:n-1
v(k+1) = A(k+1,k)/A(k,k);
A(k+1,k:n) = A(k+1,k:n) - v(k+1)*A(k,k:n);
end
U = triu(A);

GE
function [L,U] = GE(A)
% [L,U] = GE(A)
%
% The LU factorization without pivoting. If A is n-by-n and has an
% LU factorization, then L is unit lower triangular and U is upper
% triangular so A = LU.

[n,n] = size(A);
for k=1:n-1
A(k+1:n,k) = A(k+1:n,k)/A(k,k);
A(k+1:n,k+1:n) = A(k+1:n,k+1:n) - A(k+1:n,k)*A(k,k+1:n);
end
L = eye(n,n) + tril(A,-1);
U = triu(A);

GEpiv
function [L,U,piv] = GEpiv(A)
% [L,U,piv] = GE(A)
%
% The LU factorization with partial pivoting. If A is n-by-n, then
% piv is a permutation of the vector 1:n and L is unit lower triangular
% and U is upper triangular so A(piv,:) = LU. |L(i,j)|<=1 for all i and j.

[n,n] = size(A);
piv = 1:n;
for k=1:n-1
[maxv,r] = max(abs(A(k:n,k)));
q = r+k-1;
piv([k q]) = piv([q k]);
A([k q],:) = A([q k],:);
if A(k,k) ~= 0
A(k+1:n,k) = A(k+1:n,k)/A(k,k);
A(k+1:n,k+1:n) = A(k+1:n,k+1:n) - A(k+1:n,k)*A(k,k+1:n);
end
end
L = eye(n,n) + tril(A,-1);
U = triu(A);

GE2
function GE2(A)
% GE2(A)
%
% Displays the results of 2-by-2 Gaussian elimination when it is applied to
% the linear system Ax = [A(1,1)-A(1,2);A(2,1)-A(2,2)] using 3-digit arithmetic.

clc
condA = cond(A);
a11 = represent(A(1,1));
a12 = represent(A(1,2));
a21 = represent(A(2,1));
a22 = represent(A(2,2));
b1 = float(a11,a12,'-');
b2 = float(a21,a22,'-');
disp(['Stored A = ' pretty(a11) ' ' pretty(a12) ])
disp([' ' pretty(a21) ' ' pretty(a22) ])
L11 = represent(1);
L12 = represent(0);
L21 = float(a21,a11,'/');
L22 = represent(1);
disp(' ');
disp(['Computed L = ' pretty(L11) ' ' pretty(L12)])
disp([' ' pretty(L21) ' ' pretty(L22)])
U11 = a11;
U12 = a12;
U21 = represent(0);
U22 = float(a22,float(L21,a12,'*'),'-');
disp(' ');
disp(['Computed U = ' pretty(U11) ' ' pretty(U12)])
disp([' ' pretty(U21) ' ' pretty(U22)])
y1 = b1;
y2 = float(b2,float(L21,y1,'*'),'-');
x2 = float(y2,U22,'/');
x1 = float(float(y1,float(U12,x2,'*'),'-'),U11,'/');
xe1 = represent(1);
xe2 = represent(-1);
disp(' ');
disp(['Exact b = ' pretty(b1) ])
disp([' ' pretty(b2) ])
disp(' ');
disp(['Exact Solution = ' pretty(xe1) ])
disp([' ' pretty(xe2) ])
disp(' ');
disp(['Computed Solution = ' pretty(x1)])
disp([' ' pretty(x2)])
disp(sprintf('\ncond(A) = %5.3e',condA))
xtilde = [convert(x1);convert(x2)];
x = [1;-1];
b = A*x;
r = A*xtilde-b;
E = -r*xtilde'/(xtilde'*xtilde);
disp(' ');
disp('Computed solution solves (A+E)*x = b where')
disp(sprintf('\n E = %12.6f %12.6f',E(1,1),E(1,2)));
disp(sprintf(' %12.6f %12.6f',E(2,1),E(2,2)));
disp(' ')
Chapter 7 M-File
M-File Home

Script Files

ShowLSFit Fits a line to the square root function.


ShowLSq Sensitivity of LS solutions.
ShowSparseLS Examines "\" LS solving with sparse arrays.
ShowQR Illustrates QRrot.
ShowBVP Solves a two-point boundary value problem.
CholBench Benchmarks various Cholesky implementations.
CholErr Sensitivity of symmetric positive definite systems.
ShowCholBlock Explores block size selection in CholBlock.

Function Files

Rotate Computes a rotation to zero bottom of 2-vector.


QRrot QR factorization via rotations.
LSq Least squares solution via QR factorization.
CholTrid Cholesky factorization (tridiagonal version).
CholTridSol Solves a factored tridiagonal system.
CholScalar Cholesky factorization (scalar version).
CholDot Cholesky factorization (dot product version).
CholSax Cholesky factorization (saxpy version).
CholGax Cholesky factorization (gaxpy version).
CholRecur Cholesky factorization (recursive version).
CholBlock Cholesky factorization (block version).
MakeScalar Makes a block matrix a scalar matrix.

ShowLSFit
% Script File: ShowLSFit
% Displays two LS fits to the function f(x) = sqrt(x) on [.25,1].

close all
z = linspace(.25,1);
fz = sqrt(z);
for m = [2 100 ]
x = linspace(.25,1,m)';
A = [ones(m,1) x];
b = sqrt(x);
xLS = A\b;
alpha = xLS(1);
beta = xLS(2);
figure
plot(z,fz,z,alpha+beta*z,'--')
title(sprintf('m = %2.0f, alpha = %10.6f, beta = %10.6f',m,alpha,beta))
end

ShowLSq
% Script File: Show LSq
% Illustrates LSq on problems with user-specified
% dimension and condition.
m = input('Enter m: ');
n = input('Enter n: ');
logcond = input('Enter the log of the desired condition number: ');
condA = 10^logcond;
[Q1,R1] = qr(randn(m,m));
[Q2,R2] = qr(randn(n,n));
A = Q1(:,1:n)*diag(linspace(1,1/condA,n))*Q2;
clc
disp(sprintf('m = %2.0f, n = %2.0f, cond(A) = %5.3e',m,n,condA))
b = A*ones(n,1);
[x,res] = LSq(A,b);
disp(' ')
disp('Nonzero residual problem.')
disp(' ')
disp(' Exact Solution Computed Solution')
disp('-----------------------------------------')
for i=1:n
disp(sprintf(' %20.16f %20.16f',1,x(i)))
end
b = b+Q1(:,m);
[x,res] = LSq(A,b);
disp(' ')
disp(' ')
disp(' ')
disp('Zero residual problem.')
disp(' ')
disp(' Exact Solution Computed Solution')
disp('-----------------------------------------')
for i=1:n
disp(sprintf(' %20.16f %20.16f',1,x(i)))
end

ShowSparseLS
% Script File: ShowSparseLS
% Illustrate sparse backslash solving for LS problems

clc
n = 50;
disp(' m n full A flops sparse A flops')
disp('----------------------------------------')
for m = 100:100:1000
A = tril(triu(rand(m,m),-5),5);
p = m/n;
A = A(:,1:p:m);
A_sparse = sparse(A);
b = rand(m,1);
% Solve an m-by-n LS problem where the A matrix has about
% 10 nonzeros per column. In column j these nonzero entries
% are more or less A(j*m/n+k,j), k=-5:5.
flops(0)
x = A\b;
f1 = flops;
flops(0)
x = A_sparse\b;
f2 = flops;
disp(sprintf('%4d %4d %10d %10d ',m,n,f1,f2))
end

ShowQR
% Script File: ShowQR
% Illustrates how the QR factorization is found via Givens Rotations.

m = 5;
n = 3;
disp(' ')
disp('Show upper triangularization of ')
A = rand(m,n);
for j=1:n
for i=m:-1:j+1
input('Strike Any Key to Continue');
clc
%Zero A(i,j)
Before = A
[c,s] = Rotate(A(i-1,j),A(i,j));
A(i-1:i,j:n) = [c s; -s c] *A(i-1:i,j:n);
disp(sprintf('Zero A(%g,%g)',i,j))
After = A
end
end
disp('Upper Triangularization Complete.')

ShowBVP
% Script File: ShowBVP
% Illustrates the numerical solution to
%
% -D^2 u + u(x) = 2xsin(x) - 2cos(x) u(0) = u(pi) = 0.
%
% Exact solution = u(x) = x*sin(x).

close all
n = 100;
x = linspace(0,pi,n)';
hx = pi/(n-1);
d = 2*ones(n-2,1) + hx^2;
e = -ones(n-2,1);
[g,h] = CholTrid(d,e);
b = hx^2*( 2*x(2:n-1).*sin(x(2:n-1)) - 2*cos(x(2:n-1)));
umid = CholTridSol(g,h,b);
u = [0;umid;0];
plot(x,u,x,x.*sin(x))
err = norm(u - x.*sin(x),'inf');
title('Solution to -D^2 u + u = 2xsin(x) - 2cos(x), u(0)=u(pi)=0')
xlabel(sprintf(' n = %3.0f norm(u - xsin(x),''inf'') = %10.6f',n,err))

CholBench
% Script File: CholBench
% Benchmarks five different Cholesky implemntations.

clc
n = input('Enter n: ');
X = rand(2*n,n);
A = X'*X;
disp(' ');
disp(sprintf('n = %2.0f',n))
disp(' ');
disp('Algorithm Relative Time Flops')
disp('-----------------------------------')

flops(0); tic; G = CholScalar(A); t1 = toc; f = flops;


disp(sprintf('CholScalar %6.3f %5.0f',t1/t1,f));

flops(0); tic; G = CholDot(A); t2 = toc; f = flops;


disp(sprintf('CholDot %6.3f %5.0f',t2/t1,f));

flops(0); tic; G = CholSax(A); t3 = toc; f = flops;


disp(sprintf('CholSax %6.3f %5.0f',t3/t1,f));

flops(0); tic; G = CholGax(A); t4 = toc; f = flops;


disp(sprintf('CholGax %6.3f %5.0f',t4/t1,f));

flops(0); tic; G = CholRecur(A); t5 = toc; f = flops;


disp(sprintf('CholRecur %6.3f %5.0f',t5/t1,f));

disp(' ')
disp('Relative time = Time/CholScalar Time')

CholErr
% Script File: CholErr
% Error when solving a Hilbert Matrix System.

clc
disp(' n cond(A) relerr ')
disp('----------------------------------')
for n = 2:12
A = hilb(n);
b = randn(n,1);
G = CholScalar(A);
y = LTriSol(G,b);
x = UTriSol(G',y);
condA = cond(A);
xTrue = invhilb(n)*b;
relerr = norm(x-xTrue)/norm(xTrue);
disp(sprintf(' %2.0f %10.3e %10.3e ',n,condA,relerr))
end

ShowCholBlock
% Script File: ShowCholBlock
% Effect of block size on performance of block Cholesky.

n = 192;
A = randn(n,n);
A = A'*A;
G0 = chol(A)';
clc
disp(sprintf('n = %2.0f',n))
disp(' ')
disp(' Block Size Time / Unblocked Time ')
disp('---------------------------------------')
k =0;
for p=[ 8 12 16 24 32 48 96]
tic;
G = CholBlock(A,p);
k=k+1;
t(k) = toc;
disp(sprintf(' %2.0f %6.3f ',p,t(k)/t(1)))
end

Rotate
function [c,s] = Rotate(x1,x2)
% [c,s] = Rotate(x1,x2)
% x1 and x2 are real scalars and c and s is a cosine-sine pair so
% -s*x1 + c*x2 = 0.

if x2==0
c = 1;
s = 0;
else
if abs(x2)>=abs(x1)
cotangent = x1/x2;
s = 1/sqrt(1+cotangent^2);
c = s*cotangent;
else
tangent = x2/x1;
c = 1/sqrt(1+tangent^2);
s = c*tangent;
end
end

QRrot
function [Q,R] = QRRot(A)
% [Q,R] = QRRot(A)
% The QR factorization of an m-by-n matrix A. (m>=n).
% Q is m-by-m orthogonal and R is m-by-n upper triangular.

[m,n] = size(A);
Q = eye(m,m);
for j=1:n
for i=m:-1:j+1
%Zero A(i,j)
[c,s] = Rotate(A(i-1,j),A(i,j));
A(i-1:i,j:n) = [c s; -s c]*A(i-1:i,j:n);
Q(:,i-1:i) = Q(:,i-1:i)*[c s; -s c]';
end
end
R = triu(A);

LSq
function [xLS,res] = LSq(A,b)
% [xLS,res] = LSq(A,b)
% Solution to the LS problem min norm(Ax-b) where A is a full
% rank m-by-n matrix with m>=n and b is a column m-vector.
% xLS is the n-by-1 vector that minimizes the norm(Ax-b) and
% res = norm(A*xLS-b).

[m,n] = size(A);
for j=1:n
for i=m:-1:j+1
%Zero A(i,j)
[c,s] = Rotate(A(i-1,j),A(i,j));
A(i-1:i,j:n) = [c s; -s c]*A(i-1:i,j:n);
b(i-1:i) = [c s; -s c]*b(i-1:i);
end
end
xLS = UTriSol(A(1:n,1:n),b(1:n));
if m==n
res = 0;
else
res = norm(b(n+1:m));
end

CholTrid
function [g,h] = CholTrid(d,e)
% G = CholTrid(d,e)
% Cholesky factorization of a symmetric, tridiagonal positive definite matrix A.
% d and e are column n-vectors with the property that
% A = diag(d) + diag(e(2:n),-1) + diag(e(2:n),1)
%
% g and h are column n-vectors with the property that the lower bidiagonal
% G = diag(g) + diag(h(2:n),-1) satisfies A = GG^T.

n = length(d);
g = zeros(n,1);
h = zeros(n,1);
g(1) = sqrt(d(1));
for i=2:n
h(i) = e(i)/g(i-1);
g(i) = sqrt(d(i) - h(i)^2);
end

CholTridSol

function x = CholTridSol(g,h,b)
% x = CholTridSol(g,h,b)
% Solves the linear system G*G'x = b where b is a column n-vector and
% G is a nonsingular lower bidiagonal matrix. g and h are column n-vectors
% with G = diag(g) + diag(h(2:n),-1).

n = length(g);
y = zeros(n,1);

% Solve Gy = b
y(1) = b(1)/g(1);
for k=2:n
y(k) = (b(k) - h(k)*y(k-1))/g(k);
end

% Solve G'x = y
x = zeros(n,1);
x(n) = y(n)/g(n);
for k=n-1:-1:1
x(k) = (y(k) - h(k+1)*x(k+1))/g(k);
end

CholScalar
function G = CholScalar(A)
% G = CholScalar(A)
% Cholesky factorization of a symmetric and positive definite matrix A.
% G is lower triangular so A = G*G'.

[n,n] = size(A);
G = zeros(n,n);
for i=1:n
% Compute G(i,1:i)
for j=1:i
s = A(j,i);
for k=1:j-1
s = s - G(j,k)*G(i,k);
end
if j < i
G(i,j) = s/G(j,j);
else
G(i,i) = sqrt(s);
end
end
end

CholDot
function G = CholDot(A)
% G = CholDot(A)
% Cholesky factorization of a symmetric and positive definite matrix A.
% G is lower triangular so A = G*G'.

[n,n] = size(A);
G = zeros(n,n);
for i=1:n
% Compute G(i,1:i)
for j=1:i
if j==1
s = A(j,i);
else
s = A(j,i) - G(j,1:j-1)*G(i,1:j-1)';
end
if j < i
G(i,j) = s/G(j,j);
else
G(i,i) = sqrt(s);
end
end
end

CholSax
function G = CholSax(A)
% G = CholSax(A)
% Cholesky factorization of a symmetric and positive definite matrix A.
% G is lower triangular so A = G*G'.

[n,n] = size(A);
G = zeros(n,n);
s = zeros(n,1);
for j=1:n
s(j:n) = A(j:n,j);
for k=1:j-1
s(j:n) = s(j:n) - G(j:n,k)*G(j,k);
end
G(j:n,j) = s(j:n)/sqrt(s(j));
end

CholGax
function G = CholGax(A)
% G = CholGax(A)
% Cholesky factorization of a symmetric and positive definite matrix A.
% G is lower triangular so A = G*G'.

[n,n] = size(A);
G = zeros(n,n);
s = zeros(n,1);
for j=1:n
if j==1
s(j:n) = A(j:n,j);
else
s(j:n) = A(j:n,j) - G(j:n,1:j-1)*G(j,1:j-1)';
end
G(j:n,j) = s(j:n)/sqrt(s(j));
end

CholRecur
function G = CholRecur(A)
% G = CholRecur(A)
% Cholesky factorization of a symmetric and positive definite matrix A.
% G is lower triangular so A = G*G'.

[n,n] = size(A);
G = zeros(n,n);
if n==1
G = sqrt(A);
else
G(1:n-1,1:n-1) = CholRecur(A(1:n-1,1:n-1));
G(n,1:n-1) = LTriSol(G(1:n-1,1:n-1),A(1:n-1,n))';
G(n,n) = sqrt(A(n,n) - G(n,1:n-1)*G(n,1:n-1)');
end

CholBlock
function G = CholBlock(A,p)
% G = CholBlock(A,p)
% Cholesky factorization of a symmetric and positive definite n-by-n matrix A.
% G is lower triangular so A = G*G'. p is the block size and must divide n.

% Represent A and G as m-by-m block matrices where m = n/p.


[n,n] = size(A);
m = n/p;
A = MakeBlock(A,p);
G = cell(m,m);
for i=1:m
for j=1:i
S = A{i,j};
for k=1:j-1
S = S - G{i,k}*G{j,k}';
end
if j < i
G{i,j} = (G{j,j}\S')';
else
G{i,i} = CholScalar(S);
end
end
end
% Convert G to a scalar matrix.
G = MakeScalar(G);

MakeScalar
function A = MakeScalar(A_block)
% A = MakeScalar(A_block)
% Represents the m-by-m block matrix A_block as an n-by-n matrix of scalrs with
% where each block is p-by-p and n=mp.
[m,m] = size(A_block);
[p,p] = size(A_block{1,1});
for i=1:m
for j=1:m
if ~isempty(A_block{i,j})
A(1+(i-1)*p:i*p,1+(j-1)*p:j*p) = A_block{i,j};
end
end
end
Chapter 8 M-Files
M-File Home

Script Files

ShowMySqrt Plots relative error associated with MySqrt.


ShowBisect Illustrates the method of bisection.
ShowNewton Illustrates the classical Newton iteration.
FindConj Uses fzero to compute Mercury-Earth conjunctions.
FindTet Applies fmin to three different objective functions.
ShowGolden Illustrates Golden section search.
ShowSD Steepest descent test environment.
ShowN Newton test environment.
ShowFDN Finite difference Newton test environment.
ShowMixed Globalized Newton test environment.
ShowGN Gauss-Newton test environment.
ShowFmin Illustrates fmin.
ShowFmins Illustrates fmins.

Function Files

MySqrt Canonical square root finder.


Bisection The method of bisection.
StepIsIn Checks next Newton step.
GlobalNewton Newton method with bisection globalizer.
GraphSearch Interactive graphical search environment.
Golden Golden section search.
SDStep Steepest descent step.
NStep Newton step.
FDNStep Finite difference Newton step.
GNStep Gauss-Newton step.
SineMercEarth The sine of the Mercury-Sun-Earth angle.
DistMercEarth Mercury-Earth distance.
TA Surface area of tetrahedron.
TV Volume of tetrahedron.
TE Total edge length of tetrahedron.
TEDiff Difference between tetrahedron edges.
Orbit Generates and plots orbit points.
Sep Separation between points on two orbits.
gSep Gradient of Sep.
gHSep Gradient and Hessian of sep.
SepV Vector from a point on one orbit to a point on another orbit.
JSepV Jacobian of SepV.
rho Residual of orbit fit.
Jrho Jacobian of rho.

ShowMySqrt
% Script File: ShowMySqrt
% Plots the error in the function MySqrt.

close all
L = input('Enter left endpoint of test interval:');
R = input('Enter right endpoint of test interval:');
Avals = linspace(L,R,300);
s = zeros(1,300);
for k=1:300
s(k) = MySqrt(Avals(k));
end
error = abs(s-sqrt(Avals))./sqrt(Avals);
figure
plot(Avals,error+eps*.01)
Title('Relative Error in the Function MySqrt(A)')
xlabel('A');

ShowBisect
% Script File: ShowBisect
% Illustrates 6 steps of the method of bisection.

close all
fName = input('Enter the name of the function (in quotes):');
a0 = input('Enter the left endpoint of interval of interest:');
b0 = input('Enter the right endpoint of interval of interest:');

% Get initial interval.


x = linspace(a0,b0);
y = feval(fName,x);
figure
plot(x,y,x,0*x);
title('Click in left endpoint a.');
[a,y] = ginput(1);
title('Click in right endpoint b.');
[b,y] = ginput(1);
% Display the initial situation.
x = linspace(a-.1*(b-a),b+.1*(b-a));
y = feval(fName,x);
fa = feval(fName,a);
fb = feval(fName,b);
plot(x,y,x,0*x,[a b], [fa fb],'*')
xlabel(sprintf('At the start: a = %10.6f b = %10.6f',a,b));
for k=1:6
mid = (a+b)/2;
fmid = feval(fName,mid);
title('Strike any key to continue');
pause
if fa*fmid<=0
% There is a root in [a,mid].
b = mid;
fb = fmid;
else
% There is a root in [mid,b].
a = mid;
fa = fmid;
end
x = linspace(a-.1*(b-a),b+.1*(b-a));
y = feval(fName,x);
figure
plot(x,y,x,0*x,[a b], [fa fb],'*')
xlabel(sprintf('After %1.0f steps: a = %10.6f b = %10.6f',k,a,b));
end

ShowNewton
% Script File: ShowNewton
% A pure Newton method test environment.

close all
fName = input('Enter the name of the function (with quotes);');
fpName = input('Enter the name of the derivative function (with quotes);');

a = input('Enter left endpoint of interval of interest:');


b = input('Enter right endpoint of interval of interest:');
xvals = linspace(a,b,100);
fvals = feval(fName,xvals);
figure
plot(xvals,fvals,xvals,0*xvals)
v = axis;
title('Click in Starting Value')
[xc,y] = ginput(1);
fc = feval(fName,xc);
fpc = feval(fpName,xc);
Lvals = fc + fpc*(xvals-xc);
hold on
plot(xvals,Lvals,xc,fc,'*')
axis(v)
xlabel(sprintf(' xc = %10.6f fc = %10.3e',xc,fc))
hold off
for k=1:3
step = -fc/fpc;
xc = xc+step;
a = xc-abs(step);
b = xc+abs(step);
xvals = linspace(a,b,100);
fvals = feval(fName,xvals);
fc = feval(fName,xc);
fpc = feval(fpName,xc);
Lvals = fc + fpc*(xvals-xc);
figure
plot(xvals,fvals,xvals,Lvals,xvals,0*xvals,xc,fc,'*')
xlabel(sprintf(' xc = %10.6f fc = %10.3e',xc,fc))
end

FindConj
% Script File: FindConj
% Estimates spacing between Mercury-Earth Conjunctions

clc
GapEst = input('Spacing Estimate = ');
disp(sprintf('Next ten conjunctions, Spacing Estimate = %8.3f',GapEst))
disp(' ')
t = zeros(11,1);
disp('Conjunction Time Spacing')
disp('-----------------------------------')
for k=1:10;
t(k+1) = fzero('SineMercEarth',k*GapEst);
disp(sprintf(' %2.0f %8.3f %8.3f',k,t(k+1),t(k+1)-t(k)))
end

FindTet
% Script File: FindTet
% Applies fmin to three different objective functions.

tol = 10e-16;

reps = 20;
disp(' ')
disp('Objective Function Time Theta')
disp('----------------------------------------------------')

t0=clock;
for k=1:reps
OptLat1 = fmin('TA',-pi/2,pi/2,[0 tol]);
end
t1 = etime(clock,t0);
disp(sprintf(' Area %10.3f %20.16f',1,OptLat1))
t0 = clock;
for k=1:reps
OptLat2 = fmin('TV',-pi/2,pi/2,[0 tol]);
end
t2 = etime(clock,t0);
disp(sprintf(' Volume %10.3f %20.16f',t2/t1,OptLat2))
t0 = clock;
for k=1:reps
OptLat3 = fmin('TE',-pi/2,pi/2,[0 tol]);
end
t3 = etime(clock,t0);
disp(sprintf(' Edge %10.3f %20.16f',t3/t1,OptLat3))
OptLat0 = fzero('TEdiff',-.3,eps);
disp(sprintf('\n fzero method: %20.16f',OptLat0))
disp(sprintf(' Exact: %20.16f',asin(-1/3)))

ShowGolden
% Script File: ShowGolden
% Illustrates Golden Section Search

close all
tvals = linspace(900,950);
plot(tvals,DistMercEarth(tvals));
axis([900 950 40 150])
axis('off')
hold on
fname = 'DistMercEarth';
a = 900;
b = 950;
r = (3 - sqrt(5))/2;
c = a + r*(b-a); fc = feval(fname,c);
d = a + (1-r)*(b-a); fd = feval(fname,d);
y = 78;
step = 0;
plot([900 950],[y y],[a c d b],[y y y y],'o')
text(952,y,num2str(step))
text(950.5,y+8,'Step')
title('Golden Section Search: (a c d b) Trace')
while step < 5
if fc >= fd
z = c + (1-r)*(b-c);
% [a c d b ] <--- [c d z b]
a = c;
c = d; fc = fd;
d = z; fd = feval(fname,z);
else
z = a + r*(d-a);
% [a c d b ] <--- [a z c d]
b = d;
d = c; fd = fc;
c = z; fc = feval(fname,z);
end
y = y - 7;
step = step+1;
plot([900 950],[y y],[a c d b],[y y y y],'o')
text(952,y,num2str(step))

end
tmin = (c+d)/2;
hold off

ShowSD
% Script File: ShowSD
% Minimizes the sep function using steepest descent.

planet1 = struct('A',10,'P',2,'phi', pi/8);


planet2 = struct('A', 4,'P',1,'phi',-pi/7);

close all
Lmax = 1; % Max line step
nSteps = 25; % Total number of steps.
manualSteps = 3; % Number of manual steps with point-and-click line search.
con_size = 20; % Size of the contour plot

% Plot the two orbits.


figure
t=linspace(0,2*pi);
Orbit(t,planet1,'-');
hold on
Orbit(t,planet2,'--');
hold off

% Display contours of the sep function.


tRange = linspace(0,2*pi,con_size);
Z = zeros(con_size,con_size);
for i=1:con_size
for j=1:con_size
t = [tRange(i);tRange(j)];
Z(i,j) = Sep(t,planet1,planet2);
end
end
figure
C = contour(tRange,tRange,Z',10);
title('Enter Contour Labels (Optional). To quit, strike .')
xlabel('t1')
ylabel('t2')
Clabel(C,'manual')
% Get ready for the iteration.
title('Enter Starting Point for Steepest Descent')
tc = zeros(2,1);
[tc(1),tc(2)] = ginput(1);
fc = Sep(tc,planet1,planet2);
gc = gSep(tc,planet1,planet2);
tvals = tc
fvals = fc;
gvals = norm(gc);
title('')
% Steepest Descent with line search:

clc
disp('Step sep t(1) t(2) norm(grad)')
disp('----------------------------------------------------------------')
disp(sprintf('%2.0f %10.6f %10.6f %10.6f %8.3e ',0,fc,tc,norm(gc)))
figure(3)

for step = 1:nSteps


if step<=manualSteps
% Manual Line Search
[tnew,fnew,gnew] = SDStep(tc,fc,gc,planet1,planet2,Lmax,0);
else
% Automated Line Search
[tnew,fnew,gnew] = SDStep(tc,fc,gc,planet1,planet2,Lmax,1);
end
tvals = [tvals tnew]; tc = tnew;
fvals = [fvals fnew]; fc = fnew;
gvals = [gvals norm(gnew)]; gc = gnew;
disp(sprintf('%2.0f %10.6f %10.6f %10.6f %8.3e ',step,fc,tc,norm(gc)))
end

% Show solution on orbit plot:

figure(1)
hold on
last = nSteps+1;
pt1 = Orbit(tvals(1,last),planet1,'*');
pt2 = Orbit(tvals(2,last),planet2,'*');
plot([pt1.x pt2.x],[pt1.y pt2.y])
title(sprintf('min Sep = %8.4f',fvals(last)))
hold off

% Show the descent path on the contour plot:

figure(2)
hold on
plot(tvals(1,:),tvals(2,:),tvals(1,1),tvals(2,1),'o')
hold off
title(sprintf('tmin = (%8.3f,%8.3f) norm(gmin)= %8.4e',tvals(1,last),tvals(2,last),gvals(last)))

% Plot the descent of the sep and its gradient:

figure(3)
subplot(2,1,1)
plot(fvals)
title('Value of Sep')
xlabel('Iteration')
subplot(2,1,2)
semilogy(gvals)
title('Value of norm(gSep).')
xlabel('Iteration')

ShowN
% Script File: ShowN
% Newton method test environment.

close all
planet1 = struct('A',15,'P',2,'phi', pi/10);
planet2 = struct('A',20,'P',3,'phi',-pi/8);

itmax = 10;
tol = 1e-14;

% Plot Orbits

figure
axis equal off
t = linspace(0,2*pi);
Orbit(t,planet1,'-');
hold on
Orbit(t,planet2,'--');

% Enter Starting Points


tc = rand(2,1)*2*pi;
Orbit(tc(1),planet1,'*');
Orbit(tc(2),planet2,'*');
title('Initial Guess')
hold off

% Initializations
Fc = SepV(tc,planet1,planet2); r = norm(Fc);
Jc = JSepV(tc,planet1,planet2); c = cond(Jc);
clc
disp('Iteration tc(1) tc(2) norm(Fc) cond(Jc)')
disp('------------------------------------------------------------------------------')
disp(sprintf(' %2.0f %20.16f %20.16f %8.3e %8.3e',0,tc(1),tc(2),r,c))

% The Newton Iteration

k=0;
while (ktol)
% Take a step and display
[tc,Fc,Jc] = NStep(tc,Fc,Jc,planet1,planet2);

k=k+1;
figure
Orbit(t,planet1,'-');
hold on
Orbit(t,planet2,'--');
Orbit(tc(1),planet1,'*');
Orbit(tc(2),planet2,'*');
hold off
r = norm(Fc);
c = cond(Jc);
title(sprintf(' Iteration = %2.0f norm(Fc) = %8.3e',k,r))
disp(sprintf(' %2.0f %20.16f %20.16f %8.3e %8.3e',k,tc(1),tc(2),r,c))
end
sol = Orbit(tc(1),planet1);
disp(sprintf('\n Intersection = ( %17.16f , %17.16f )',sol.x,sol.y))

ShowFDN
% Script File: ShowFDN
% Finite Difference Newton method test environment.

close all
planet1 = struct('A',15,'P',2,'phi', pi/10);
planet2 = struct('A',20,'P',3,'phi',-pi/8);

itmax = 10;
tol = 1e-14;

% Plot Orbits
figure
axis equal off
t = linspace(0,2*pi);
Orbit(t,planet1,'-');
hold on
Orbit(t,planet2,'--');

% Enter Starting Points


tc = rand(2,1)*2*pi;
Orbit(tc(1),planet1,'*');
Orbit(tc(2),planet2,'*');
title('Initial Guess')
hold off

% Initializations

Fc = SepV(tc,planet1,planet2); r = norm(Fc);
clc
disp('Iteration tc(1) tc(2) norm(Fc)')
disp('--------------------------------------------------------------------')
disp(sprintf(' %2.0f %20.16f %20.16f %8.3e',0,tc(1),tc(2)))

% The FDNewton Iteration

k=0;
while (ktol)
% Take a step and display
[tc,Fc] = FDNStep(tc,Fc,planet1,planet2);

k=k+1;
figure
Orbit(t,planet1,'-');
hold on
Orbit(t,planet2,'--');
Orbit(tc(1),planet1,'*');
Orbit(tc(2),planet2,'*');
hold off
r = norm(Fc);
title(sprintf(' Iteration = %2.0f norm(Fc) = %8.3e',k,r))
disp(sprintf(' %2.0f %20.16f %20.16f %8.3e %8.3e',k,tc(1),tc(2),r))
end
sol = Orbit(tc(1),planet1);
disp(sprintf('\n Intersection = ( %17.16f , %17.16f )',sol.x,sol.y))

ShowMixed
% Script File: ShowMixed
% Minimizes the sep function by doing a few steepest descent steps
% (with line search) and then a few full Newton steps.

close all
SDSteps = 2; % The number of steepest descent steps.
NSteps = 10; % The number of Newton steps.
planet1 = struct('A',10,'P',2,'phi', pi/8);
planet2 = struct('A', 4,'P',1,'phi',-pi/7);

Lmax = 1; % Max line step


con_size = 20; % Size of the contour plot
% Plot the two orbits.
figure
t=linspace(0,2*pi);
Orbit(t,planet1,'-');
hold on
Orbit(t,planet2,'--');
hold off
% Display contours of the sep function.
tRange = linspace(0,2*pi,con_size);
Z = zeros(con_size,con_size);
for i=1:con_size
for j=1:con_size
t = [tRange(j);tRange(i)];
Z(i,j) = Sep(t,planet1,planet2);
end
end
figure
C = contour(tRange,tRange,Z,10);
title('Enter Contour Labels (Optional). To quit, strike .')
xlabel('t1')
ylabel('t2')
Clabel(C,'manual')
% Get ready for the iteration.
title('Enter Starting Point for Steepest Descent')
tc = zeros(2,1);
[tc(1),tc(2)] = ginput(1);
fc = Sep(tc,planet1,planet2);
gc = gSep(tc,planet1,planet2);
tvals = tc;
fvals = fc;
gvals = norm(gc);
title('')
% Steepest Descent with line search:
clc
disp('Step sep t(1) t(2) norm(grad)')
disp('------------------------------------------------------------------------')
disp(sprintf('%2.0f %10.6f %18.15f %18.15f %5.1e ',0,fc,tc,norm(gc)))
figure(3)
for step = 1:SDSteps
[tnew,fnew,gnew] = SDStep(tc,fc,gc,planet1,planet2,Lmax,0);
tvals = [tvals tnew]; tc = tnew;
fvals = [fvals fnew]; fc = fnew;
gvals = [gvals norm(gnew)]; gc = gnew;
disp(sprintf('%2.0f %10.6f %18.15f %18.15f %5.1e ',step,fc,tc,norm(gc)))
end
disp('------------------------------------------------------------------------')
[gc,Hc] = gHSep(tc,planet1,planet2);
for step=SDSteps+1:SDSteps+NSteps;
tc = tc - Hc\gc;
[gc,Hc] = gHSep(tc,planet1,planet2);
fc = Sep(tc,planet1,planet2);
tvals = [tvals tc];
fvals = [fvals fc];
gvals = [gvals norm(gc)];
disp(sprintf('%2.0f %10.6f %18.15f %18.15f %5.1e ',step,fc,tc,norm(gc)))
end
% Show solution on orbit plot:
figure(1)
hold on
last = length(gvals);
pt1 = Orbit(tvals(1,last),planet1,'*');
pt2 = Orbit(tvals(2,last),planet2,'*');
plot([pt1.x pt2.x],[pt1.y pt2.y])
title(sprintf('min Sep = %8.4f',fvals(last)))
hold off
% Show the descent path on the contour plot:

figure(2)
hold on
plot(tvals(1,:),tvals(2,:),tvals(1,1),tvals(2,1),'o')
hold off
title(sprintf('tmin = (%8.3f,%8.3f) norm(gmin)= %8.4e',tvals(1,last),tvals(2,last),gvals(last)))
% Plot the descent of the sep and its gradient:
figure(4)
subplot(2,1,1)
plot(fvals)
title('Value of Sep')
xlabel('Iteration')
subplot(2,1,2)
semilogy(gvals)
title('Value of norm(gSep).')
xlabel('Iteration')

ShowGN
% Script File: ShowGN
% Illustrates Gauss-Newton with line search applied to the problem of fitting a
% two-parameter ellipse to some noisy, nearly-elliptical data.

A0 = 5; P0 = 1; % Parameters of the "true" ellipse.


del = .1; % Noise factor.
m = 50; % Number of points.

con_size = 20; % Size of contour plot


kmax = 25 ; % Maximum number of GN steps.
Lmax = 8; % Maximum step length factor
tol = .000001; % Size of gradient for termination.

% Generate a noisy orbit


theta = linspace(0,2*pi,m)';
c = cos(linspace(0,2*pi,m)');
r = ((2*A0*P0)./(P0*(1-c) + A0*(1+c))).*(1 + del*randn(m,1));
plist = [r c];

% Plot the true orbit and the radius vector samples


close all
figure
axis equal
planet = struct('A',A0,'P',P0,'phi',0);

%Orbit(linspace(0,2*pi,200),planet,'-');
hold on
x = r.*c;
y = r.*sin(theta);
plot(x,y,'o')
hold off

% Generate Contour Plot


range = linspace(.9*min(r),1.5*max(r),con_size);
Z = zeros(con_size,con_size);
for i=1:con_size
for j=1:con_size
f = rho([range(j);range(i)],plist);
Z(i,j) = (f'*f)/2;
end
end
figure
C = contour(range,range,Z,20);
title('Enter Contour Labels (Optional). To quit, strike .')
xlabel('A'); ylabel('P'); Clabel(C,'manual')

% Initialize for iteration


title('Enter Starting Point for Gauss-Newton')
zc = zeros(2,1);
[zc(1),zc(2)] = ginput(1);
Jc = Jrho(zc,plist);
Fc = rho(zc,plist);
wc = (Fc'*Fc)/2;
k=0;
gnorm = norm(Jc'*Fc);

clc
disp(sprintf('"True A" = %5.3f, "True P" = %5.3f',A0,P0))
disp(sprintf('Relative Noise = %5.3f, Samples = %3.0f\n',del,m))
disp('Iteration wc A P norm(grad)')
disp('----------------------------------------------------------')
disp(sprintf(' %2.0f %10.4f %10.6f %10.6f %5.3e ',k,wc,zc(1),zc(2),gnorm))
figure(3)

% Gauss-Newton with linesearch


while (gnorm > tol ) & (k < kmax)
if k<=2
[zc,Fc,wc,Jc] = GNStep('rho','Jrho',zc,Fc,wc,Jc,plist,Lmax,0);
else
[zc,Fc,wc,Jc] = GNStep('rho','Jrho',zc,Fc,wc,Jc,plist,Lmax,1);
end
k=k+1;
gnorm = norm(Jc'*Fc);
disp(sprintf(' %2.0f %10.4f %10.6f %10.6f %5.3e ',k,wc,zc(1),zc(2),gnorm))
end
figure(1)
planetModel = struct('A',zc(1),'P',zc(2),'phi',0);
hold on
Orbit(linspace(0,2*pi),planetModel,'-');
hold off

ShowFmin
% Script File: ShowFmin
% Illustrates the 1-d minimizer fmin.

clc
tol = .000001;
disp('Local minima of the Mercury-Earth separation function.')
disp(sprintf('\ntol = %8.3e\n\n',tol))
disp(' Initial Interval tmin f(tmin) f evals')
disp('----------------------------------------------------')
options = zeros(18,1);
for k=1:8
L = 100+(k-1)*112;
R = L+112;
options(2) = tol;
[tmin options] = fmin('DistMercEarth',L,R,options);
minfeval = options(8);
nfevals = options(10);
disp(sprintf(' [%3.0f,%3.0f] %10.5f %10.5f, %6.0f',L,R,tmin,minfeval,nfevals))
end

ShowFmins
% Script Files: ShowFmins
% Illustrates the multidimensional minimizer fmins.

close all
planet1 = struct('A',10,'P',2,'phi', pi/8);
planet2 = struct('A', 4,'P',1,'phi',-pi/7);
% Plot the orbots
tVals = linspace(0,2*pi);
Orbit(tVals,planet1,'-');
hold on
Orbit(tVals,planet2,'--');

% Call fmins
trace = 0;
steptol = .000001;
ftol = .000001;
options = [trace steptol ftol];
t0=[5;2];
[t,options] = Fmins('Sep',t0,options,[],planet1,planet2);

% Show Solution
pt1 = Orbit(t(1),planet1,'o');
pt2 = Orbit(t(2),planet2,'o');
plot([pt1.x pt2.x],[pt1.y pt2.y])
hold off
its = options(10);
gval = norm(gSep(t,planet1,planet2));
title(sprintf('t_{*} = ( %8.6f , %8.6f ), Steps = %3.0f, norm(gradient) = %5.3e',t,its,gval))
axis off equal

MySqrt
function x = MySqrt(A)
% x = MySqrt(A)
% A is a non-negative real and x is its square root.

if A==0
x = 0;
else
TwoPower = 1;
m = A;
while m>=1
m = m/4;
TwoPower = 2*TwoPower;
end
while m < .25
m = m*4;
TwoPower = TwoPower/2;
end
% sqrt(A) = sqrt(m)*TwoPower
x = (1+2*m)/3;
for k=1:4
x = (x + (m/x))/2;
end
x = x*TwoPower;
end

Bisection

function root = Bisection(fname,a,b,delta)


% root = Bisection(fname,a,b,delta)
% Method of bisection.
%
% fname is a string that names a continuous function f(x) of a single variable.
% a and b define an interval [a,b] and f(a)f(b) < 0
% delta non-negative real.
%
% root is the midpoint of an interval [alpha,beta]
% with the property that f(alpha)f(beta)<=0 and
% |
% |beta-alpha| <= delta+eps*max(|alpha|,|beta|)

fa = feval(fname,a);
fb = feval(fname,b);
if fa*fb > 0
disp('Initial interval is not bracketing.')
return
end
if nargin==3
delta = 0;
end
while abs(a-b) > delta+eps*max(abs(a),abs(b))
mid = (a+b)/2;
fmid = feval(fname,mid);
if fa*fmid<=0
% There is a root in [a,mid].
b = mid;
fb = fmid;
else
% There is a root in [mid,b].
a = mid;
fa = fmid;
end
end
root = (a+b)/2;

StepIsIn
function ok = StepIsIn(x,fx,fpx,a,b)
% ok = StepIsIn(x,fx,fpx,a,b)
% Yields 1 if the next Newton iterate is in [a,b] and 0 otherwise.
% x is the current iterate, fx is the value of f at x, and fpx is
% the value of f' at x.

if fpx > 0
ok = ((a-x)*fpx <= -fx) & (-fx <= (b-x)*fpx);
elseif fpx < 0
ok = ((a-x)*fpx >= -fx) & (-fx >= (b-x)*fpx);
else
ok = 0;
end

GlobalNewton
function [x,fx,nEvals,aF,bF] = GlobalNewton(fName,fpName,a,b,tolx,tolf,nEvalsMax)
% [ x,fx,nEvals,aF,bF] = GlobalNewton(fName,fpName,a,b,tolx,tolf,nEvalsMax)
% fName string that names a function f(x).
% fpName string that names the derivative function f'(x).
% a,b A root of f(x) is sought in the interval [a,b]
% and f(a)*f(b)<=0.
% tolx,tolf Nonnegative termination criteria.
% nEvalsMax Maximum number of derivative evaluations.
%
% x An approximate zero of f.
% fx The value of f at x.
% nEvals The number of derivative evaluations required.
% aF,bF The final bracketing interval is [aF,bF].
%
% The iteration terminates as soon as x is within tolx of a true zero or
% if |f(x)|<= tolf or after nEvalMax f-evaluations.

fa = feval(fName,a);
fb = feval(fName,b);
if fa*fb>0
disp('Initial interval not bracketing.')
return
end
x = a;
fx = feval(fName,x);
fpx = feval(fpName,x);
disp(sprintf('%20.15f %20.15f %20.15f',a,x,b))

nEvals = 1;
while (abs(a-b) > tolx ) & (abs(fx) > tolf) & ((nEvals < nEvalsMax) | (nEvals==1))
%[a,b] brackets a root and x = a or x = b.
if StepIsIn(x,fx,fpx,a,b)
%Take Newton Step
disp('Newton')
x = x-fx/fpx;
else
%Take a Bisection Step:
disp('Bisection')
x = (a+b)/2;
end
fx = feval(fName,x);
fpx = feval(fpName,x);
nEvals = nEvals+1;
if fa*fx<=0
% There is a root in [a,x]. Bring in right endpoint.
b = x;
fb = fx;
else
% There is a root in [x,b]. Bring in left endpoint.
a = x;
fa = fx;
end
disp(sprintf('%20.15f %20.15f %20.15f',a,x,b))
end
aF = a;
bF = b;

GraphSearch
function [L,R] = GraphSearch(fname,a,b,Save,nfevals)
% [L,R] = GraphSearch(fname,a,b,Save,nfevals)
%
% Graphical search. Produces sequence of plots of the function f(x). The user
% specifies the x-ranges by mouseclicks.
%
% name is a string that names a function f(x) that is defined
% on the interval [a,b].
% nfevals>=2
%
% Save is used to determine how the plots are saved. If Save is
% nonzero, then each plot is saved in a separate figure window.
% If Save is zero or if GraphSearch is called with just three
% arguments, then only the final plot is saved.
%
% [L,R] is the x-range of the final plot. The plots are based on nfevals
% function evaluations. If GraphSearch is called with less
% than five arguments, then nfevals is set to 100.

close all
if nargin==3
Save=0;
end
if nargin<5
nfevals=100;
end
AnotherPlot = 1;
L = a;
R = b;
while AnotherPlot
x = linspace(L,R,nfevals);
y = feval(fname,x);
if Save
figure
end
ymin = min(y);
plot(x,y,[L R],[ymin ymin])
title(['The Function ' fname])
v = axis;
v(1) = L;
v(2) = R;
v(3) = v(3)-(v(4)-v(3))/10;
axis(v)
xlabel('Enter New Left Endpoint. (Click off x-range to terminate.)')
text(R,ymin,[' ' num2str(ymin)])
[x1,y1] = ginput(1);
if (x1 < L) | (R < x1)
AnotherPlot=0;
else
xlabel('Enter New Right Endpoint. (Click off x-range to terminate.)')
[x2,y2] = ginput(1);
if (x2 < L) | (R < x2)
AnotherPlot=0;
else
L = x1;
R = x2;
end
end
xlabel(' ')
end

Golden
function tmin = Golden(fname,a,b)
% tmin = Golden(fname,a,b)
% Golden Section Search
%
% fname string that names function f(t) of a single variable.
% a,b define an interval [a,b] upon which f is unimodal.
%
% tmin approximate global minimizer of f on [a,b].

r = (3 - sqrt(5))/2;
c = a + r*(b-a); fc = feval(fname,c);
d = a + (1-r)*(b-a); fd = feval(fname,d);
while (d-c) > sqrt(eps)*max(abs(c),abs(d))
if fc >= fd
z = c + (1-r)*(b-c);
% [a c d b ] <--- [c d z b]
a = c;
c = d; fc = fd;
d = z; fd = feval(fname,z);
else
z = a + r*(d-a);
% [a c d b ] <--- [a z c d]
b = d;
d = c; fd = fc;
c = z; fc = feval(fname,z);
end
end
tmin = (c+d)/2;

SDStep
function [tnew,fnew,gnew] = SDStep(tc,fc,gc,planet1,planet2,Lmax,auto)
% [tnew,fnew,gnew] = SDStep(tc,fc,gc,planet1,planet2,Lmax,auto)
% Generates a steepest descent step.
%
% fName the name of a function f(x,plist) that accepts column
% n-vectors and returns a scalar.
% gName the name of a function g(x,plist) that returns the
% gradient of f at x.
% xc a column-vector, an approximate minimizer
% fc f(xc,plist)
% gc g(xc,plist)
% plist orbit parameters for f and g
% Lmax max step length
% auto if auto is nonzero, then automated line search
%
% xnew a column n-vector, an improved minimizer
% fnew f(xnew,plist)
% gnew g(new,plist)
%

nplotvals = 20;

% Get the Steepest Descent Search Direction


sc = -gc;
% Line Search
% Try to get L<=Lmax so xc+L*sc is at least as good as xc.
L = Lmax;
Halvings = 0;
fL = Sep(tc+L*sc,planet1,planet2);
while (fL>=fc) & Halvings<=10
L = L/2;
Halvings = Halvings+1;
fL = Sep(tc+L*sc,planet1,planet2);
end
% Sample across [0.L]
lambdavals = linspace(0,L,nplotvals);
fvals = zeros(1,nplotvals);
for k=1:nplotvals
fvals(k) = Sep(tc+lambdavals(k)*sc,planet1,planet2);
end
if auto==0
% Manual line search.
plot(lambdavals,fvals);
xlabel('lambda');
ylabel('Sep(tc+lambda*sc)');
title('Click the Best lambda value.');
[lambda,y] = ginput(1);
tnew = tc+lambda*sc;
fnew = Sep(tnew,planet1,planet2);
gnew = gSep(tnew,planet1,planet2);
else
% Automated line search.
[fnew,i] = min(fvals(1:nplotvals));
tnew = tc + lambdavals(i(1))*sc;
gnew = gSep(tnew,planet1,planet2);
end

NStep
function [tnew,Fnew,Jnew] = NStep(tc,Fc,Jc,planet1,planet2)
% [tnew,Fnew,Jnew] = NStep(tc,Fc,Jc,planet1,planet2)
% Newton Step
%
% tc is a column 2-vector, Fc is the value of SepV(t,planet1,planet2) at t=tc
% and Jc is the value of JSepV(t,planet1,planet2) at t=tc. Does one Newton step
% applied to SepV rendering a new approximate root tnew. Fnew and Jnew are the
% values of SepV(tnew,planet1,planet2) and jSepV(tnew,planet1,planet2) respectively.

sc = -(Jc\Fc);
tnew = tc + sc;
Fnew = SepV(tnew,planet1,planet2);
Jnew = JSepV(tnew,planet1,planet2);

FDNStep
function [tnew,Fnew] = FDNStep(tc,Fc,planet1,planet2)
% [tnew,Fnew] = FDNStep(tc,Fc,planet1,planet2)
% Finite difference Newton step.
%
% tc is a column 2-vector, Fc is the value of SepV(t,planet1,planet2) at t=tc
% Does one finite difference Newton step applied to SepV rendering a new
% approximate root tnew. Fnew is the values of SepV(tnew,planet1,planet2).

% Set up the FD Jacobian.


Jc = zeros(2,2);
delta = sqrt(eps);
for k=1:2
tk = tc;
tk(k) = tc(k) + delta;
Jc(:,k) = (SepV(tk,planet1,planet2) - Fc)/delta;
end

% The Finite Difference Newton Step.


sc = -(Jc\Fc);
tnew = tc + sc;
Fnew = SepV(tnew,planet1,planet2);

GNStep
function [xnew,Fnew,wnew,Jnew] = GNStep(FName,JName,xc,Fc,wc,Jc,plist,Lmax,auto)
% [xnew,Fnew,wnew,Jnew] = GNStep(FName,JName,xc,Fc,wc,Jc,plist,Lmax,auto)
% Generates a Gauss-Newton Step
%
% FName the name of a function F(x,plist) that accepts a column
% n-vectors and column m-vector
% JName the name of a function JF(x,plist) that returns the
% Jacobian of F at x.
% xc a column n-vector, an approximate minimizer.
% Fc F(xc,plist)
% wc Fc'*Fc/2
% Jc JF(xc,plist)
% plist parameter list
% auto 0 for manual line search and nonzero otherwise.
%
% xnew a column n-vector, an improved minimizer.
% Fnew F(xnew,plist)
% wnew Fnew'*Fnew/2
% Jnew JF(xnew,plist)

nplotvals = 20; % Number of F evaluations per line search.

% Get the Gauss-Newton Search Direction

sc = -Jc\Fc;

% Line Search

% Try to get L<=Lmax so xc+L*sc is at least as good as xc.

L = Lmax;
Halvings = 0;
FL = feval(FName,xc+L*sc,plist);
while ((FL'*FL/2)>=wc) & Halvings<=10
L = L/2;
Halvings = Halvings+1;
FL = feval(FName,xc+L*sc,plist);
end
lambdavals = linspace(0,L,nplotvals);

wvals = zeros(nplotvals,1);
for k=1:nplotvals
F = feval(FName,xc+lambdavals(k)*sc,plist);
wvals(k) = (F'*F)/2;
end
if auto==0
% Manual line search
plot(lambdavals,wvals);
xlabel('lambda');
ylabel('w(xc+lambda*sc)');
title('Enter the Best lambda value.');
[lambda,y] = ginput(1);
xnew = xc+lambda*sc;
else
% Automated line search

[fnew,i] = min(wvals(1:nplotvals));
xnew = xc + lambdavals(i(1))*sc;
end
Fnew = feval(FName,xnew,plist);
wnew = .5*(Fnew'*Fnew);
Jnew = feval(JName,xnew,plist);
SineMercEarth
function s = SineMercEarth(t)
% s = SineMercEarth(t)
% The sine of the Mercury-Sun-Earth angle at time t

% Mercury location:
xm = -11.9084 + 57.9117*cos(2*pi*t/87.97);
ym = 56.6741*sin(2*pi*t/87.97);

% Earth location:
xe = -2.4987 + 149.6041*cos(2*pi*t/365.25);
ye = 149.5832*sin(2*pi*t/365.25);

s = (xm.*ye - xe.*ym)./(sqrt(xm.^2 + ym.^2).*sqrt(xe.^2 + ye.^2));

DistMercEarth
function s = DistMercEarth(t)
% s = DistMercEarth(t)
% The distance between Mercury and Earth at time t.

% Mercury location:
xm = -11.9084 + 57.9117*cos(2*pi*t/87.97);
ym = 56.6741*sin(2*pi*t/87.97);

% Earth location:
xe = -2.4987 + 149.6041*cos(2*pi*t/365.25);
ye = 149.5832*sin(2*pi*t/365.25);

s = sqrt((xe-xm).^2 + (ye-ym).^2);

TA
function A = TA(theta)
% A = TA(theta)
%
% theta is a real number in the interval [-pi/2,pi/2].
% A is a negative multiple of the surface area of a tetrahedron
% that is inscribed in the unit sphere. One vertex is at the north
% pole (0,0,1) and the other three form an equilateral triangle in the plane
% z = sin(theta).

c = cos(theta);
s = sin(theta);

A = -c.*(sqrt((3*s-5).*(s-1)) + c);

TV
function V = TV(theta)
% V = TV(theta)
%
% theta real in interval [-pi/2,pi/2]
% V is a negative multiple of the volume of a tetrahedron
% that is inscribed in the unit sphere. One vertex is at the north
% pole (0,0,1) and the other three form an equilateral triangle in the plane
% z = sin(theta).

s = sin(theta);
V = (1-s.^2).*(s-1);

TE
function E = TE(theta)
% E = TE(theta)
%
% theta is a real number in the interval [-pi/2,pi/2]
% E is a negative multiple of the total edge length of a tetrahedron
% that is inscribed in the unit sphere. One vertex is at the north
% pole (0,0,1) and the other three form an equilateral triangle in the plane
% z = sin(theta).

E = -(sqrt((1-sin(theta))) + sqrt(3/2)*cos(theta));

TEDiff
function E = TEDiff(theta)
% E = TEDiff(theta)
%
% theta is a real number in the interval [-pi/2,pi/2]
%
% Let T be the tetrahedron with one vertex at the north pole (0.0,1) and the other
% three forming an equilateral triangle in the plane z = sin(theta). E is difference
% between a pole to base edge length and the length of a base edge.

E = sqrt((1-sin(theta))) - sqrt(3/2)*cos(theta);

Orbit
function pLoc = Orbit(t,planet,lineStyle)
% pLoc = Orbit(t,planet,lineStyle)
%
% t is a row vector and for k=1:length(t), pLoc.x(k) = x(t(k)) and
% pLoc.y(k) = y(t(k)) where
%
% x(tau) cos(phi) sin(phi) (planet.A-planet.P)/2 + ((planet.A+planet.P)/2)cos(tau)
% = *
% y(tau) -sin(phi) cos(phi) sqrt(planet.A*planet.P)sin(tau)
%
% If nargin==3 then the points are plotted with line style defined by the
% string lineStyle.

c = cos(t); s = sin(t);
x0 = ((planet.P-planet.A)/2) + ((planet.P+planet.A)/2)*c;
y0 = sqrt(planet.A*planet.P)*s;
cphi = cos(planet.phi); sphi = sin(planet.phi);
pLoc = struct('x',cphi*x0 + sphi*y0,'y',-sphi*x0 + cphi*y0);
if nargin==3
plot(pLoc.x,pLoc.y,lineStyle)
end

Sep
function d = Sep(t,planet1,planet2)
% d = Sep(t,planet1,planet2)
%
% t is a 2-vector and planet1 and planet2 are orbit structures.
% t(1) defines a planet1 orbit point, t(2) defines a planet2 orbit point,
% and d is the distance between them.

pLoc1 = Orbit(t(1),planet1);
pLoc2 = Orbit(t(2),planet2);
d = ((pLoc1.x-pLoc2.x)^2 + (pLoc1.y-pLoc2.y)^2)/2;

gSep
function g = gSep(t,planet1,planet2)
% g = gSep(t,planet1,planet2)
% The gradient of sep(t,planet1,planet2) with respect to 2-vector t.

A1 = planet1.A; P1 = planet1.P; phi1 = planet1.phi;


A2 = planet2.A; P2 = planet2.P; phi2 = planet2.phi;

alfa1 = (P1-A1)/2; beta1 = (P1+A1)/2; gamma1 = sqrt(P1*A1);


alfa2 = (P2-A2)/2; beta2 = (P2+A2)/2; gamma2 = sqrt(P2*A2);
s1 = sin(t(1)); c1 = cos(t(1));
s2 = sin(t(2)); c2 = cos(t(2));
cphi1 = cos(phi1); sphi1 = sin(phi1);
cphi2 = cos(phi2); sphi2 = sin(phi2);
Rot1 = [cphi1 sphi1; -sphi1 cphi1];
Rot2 = [cphi2 sphi2; -sphi2 cphi2];
P1 = Rot1*[alfa1+beta1*c1;gamma1*s1];
P2 = Rot2*[alfa2+beta2*c2;gamma2*s2];
dP1 = Rot1*[-beta1*s1;gamma1*c1];
dP2 = Rot2*[-beta2*s2;gamma2*c2];
g = [-dP1';dP2']*(P2-P1);

gHSep
function [g,H] = gHSep(t,planet1,planet2)
% [g,H] = gHSep(t,planet1,planet2)
%
% t is a 2-vector and planet1 and planet2 are orbits.
% g is the gradient of Sep(t,planet1,planet2) and H is the Hessian.
A1 = planet1.A; P1 = planet1.P; phi1 = planet1.phi;
A2 = planet2.A; P2 = planet2.P; phi2 = planet2.phi;

alfa1 = (P1-A1)/2; beta1 = (P1+A1)/2; gamma1 = sqrt(P1*A1);


alfa2 = (P2-A2)/2; beta2 = (P2+A2)/2; gamma2 = sqrt(P2*A2);
s1 = sin(t(1)); c1 = cos(t(1));
s2 = sin(t(2)); c2 = cos(t(2));
cphi1 = cos(phi1); sphi1 = sin(phi1);
cphi2 = cos(phi2); sphi2 = sin(phi2);
Rot1 = [cphi1 sphi1; -sphi1 cphi1];
Rot2 = [cphi2 sphi2; -sphi2 cphi2];
P1 = Rot1*[alfa1+beta1*c1;gamma1*s1];
P2 = Rot2*[alfa2+beta2*c2;gamma2*s2];
dP1 = Rot1*[-beta1*s1;gamma1*c1];
dP2 = Rot2*[-beta2*s2;gamma2*c2];
g = [-dP1';dP2']*(P2-P1);
ddP1 = Rot1*[-beta1*c1;-gamma1*s1];
ddP2 = Rot2*[-beta2*c2;-gamma2*s2];
H = zeros(2,2);
H(1,1) = (P1(1)-P2(1))*ddP1(1) + (P1(2)-P2(2))*ddP1(2) + ...
dP1(1)^2 + dP1(2)^2;
H(2,2) = (P2(1)-P1(1))*ddP2(1) + (P2(2)-P1(2))*ddP2(2) + ...
dP2(1)^2 + dP2(2)^2;
H(1,2) = -dP1(1)*dP2(1) - dP1(2)*dP2(2);
H(2,1) = H(1,2);

SepV
function z = SepV(t,planet1,planet2)
% z = SepV(t,planet1,planet2)
% The vector from the t(1) point on the planet1 orbit
% to the t(2) point on the planet2 orbit.

pt1 = Orbit(t(1),planet1);
pt2 = Orbit(t(2),planet2);
z = [pt2.x-pt1.x;pt2.y-pt1.y];

JSepV
function J = JSepV(t,planet1,planet2)
% J = JSepV(t,planet1,planet2)
% J is the Jacobian of sepV(t,planet1,planet2).

A1 = planet1.A; P1 = planet1.P; phi1 = planet1.phi;


A2 = planet2.A; P2 = planet2.P; phi2 = planet2.phi;

s1 = sin(t(1)); c1 = cos(t(1));
s2 = sin(t(2)); c2 = cos(t(2));
beta1 = (P1+A1)/2; gamma1 = sqrt(P1*A1);
beta2 = (P2+A2)/2; gamma2 = sqrt(P2*A2);
cphi1 = cos(phi1); sphi1 = sin(phi1);
cphi2 = cos(phi2); sphi2 = sin(phi2);
Rot1 = [cphi1 sphi1; - sphi1 cphi1];
Rot2 = [cphi2 sphi2; - sphi2 cphi2];
J = [ Rot1*[beta1*s1;-gamma1*c1] Rot2*[-beta2*s2;gamma2*c2]];

rho
function f = rho(z,plist)
% f = rho(z,plist)
% z is a 2-vector whose components are the orbit parameters A and P
% respectively. plist is a 2-column matrix. plist(i,1) is the i-th
% observed radius vector length and plist(i,2) is the cosine of the
% corresponding observed polar angle.

A = z(1);
P = z(2);
r = plist(:,1);
c = plist(:,2);
f = r - (2*A*P)./(P*(1-c) + A*(1+c));

Jrho
function J = Jrho(z,plist)
% J = Jrho(z,plist)
% J is the Jacobian of the function rho at z.

A = z(1);
P = z(2);
c = plist(:,2);
denom = (P*(1-c) + A*(1+c)).^2;
J = -[ (2*P^2)*(1-c)./denom (2*A^2)*(1+c)./denom];
Chapter 9 M-Files
M-File Home

Script Files

ShowTraj Shows family of solutions.


ShowEuler Illustrates Euler method.
ShowFixedEuler Plots error in fixed step Euler for y'=y, y(0)=1.
ShowTrunc Shows effect of truncation error.
EulerRoundoff Illustrates Euler in three-digit floating point.
ShowAB Illustrates FixedAB.
ShowPC Illustrates FixedPC.
ShowRK Illustrates FixedRK.
ShowKepler Illustrates ode23 and ode45 on a system.

Function Files

FixedEuler Fixed step Euler method.


ABStart Gets starting values for Adams methods.
ABStep Adams-Bashforth step (order <=5).
FixedAB Fixed step size Adams-Bashforth.
AMStep Adams-Moulton step (order <=5).
PCStep AB-AM predictor-corrector Step (order <=5$).
FixedPC Fixed stepsize AB-AM predictor-corrector.
RKStep Runge-Kutta step (order <=5).
FixedRK Fixed step size Runge-Kutta.
Kepler For solving two-body IVP.
f1 The f function for the model problem y'=y.

ShowTraj
% Script File: ShowTraj
% Plots a family of solutions to y'(t) = -5y(t)

close all
t= linspace(-.1,.4);
y = exp(-5*t);
plot(t,y);
axis([-.1 .4 0 2])
hold on
for c=linspace(0,4,21)
plot(t,c*y)
end
plot(0,1,'o')
plot(t,y,'LineWidth',2)
hold off
title('Solutions to y''(t) = -5 y(t)')

ShowEuler
% Script File: ShowEuler
% Plots a family of solutions to y'(t) = -5y(t)

close all
t= linspace(-.1,.4);
y = exp(-5*t);
plot(t,y);
axis([-.1 .4 0 2])
hold on
tc = 0;
yc = 1;
plot(tc,yc,'*')

hold on
for k=0:4
title(sprintf('k=%1.0f, Click t(%1.0f)',k,k+1))
[tnew,z] = ginput(1);
hc = tnew-tc;
fc = -5*yc;
ynew = yc + hc*fc;
plot([tc tnew],[yc ynew],'--',tnew,ynew,'o')
tc = tnew;
yc = ynew;
end
title('Five Steps of Euler Method (y''=-5y, y(0)=1) ')
hold off

ShowFixedEuler
% Script File: ShowFixedEuler
% Plots the error in a fixed h euler solution to
% y' = -y, y(0) = 1, on [0,5]

close all
tol=.01;
[tvals,yvals] = FixedEuler('f1',1,0,5,1,tol);
plot(tvals,exp(-tvals)-yvals)
title('Fixed h Euler Error for y''=-y, y(0) = 1')
xlabel(sprintf('tol = %5.3f, n = %4.0f',tol,length(tvals)))

ShowTrunc

% Script File: ShowTrunc


% Illustrates local truncation error
close all
a=-5;
h=.1;
y0=1;
t = linspace(-h,4*h,100);
clf
y = exp(a*t);
plot(t,y)
text(.2,1.18,sprintf('y'' = %4.1fy, y(0) = 1',a))
text(.2,1.1,'* = exact solution')
text(.2,1.02,'o = computed solution')
hold on
plot(0,y0,'*')
y1 = (1 + a*h)*y0;
y = (y1/exp(a*h))*exp(a*t);
plot(t,y)
plot(h,exp(a*h),'*')
plot(h,(y1/exp(a*h))*exp(a*h),'o')
pause(1)
y2 = (1 + a*h)*y1;
y = (y2/exp(2*a*h))*exp(a*t);
plot(t,y)
plot(2*h,exp(a*2*h),'*')
plot(2*h,(y2/exp(2*a*h))*exp(a*2*h),'o')
y3 = (1 + a*h)*y2;
y = (y3/exp(3*a*h))*exp(a*t);
plot(t,y)
plot(3*h,exp(a*3*h),'*')
plot(3*h,(y3/exp(3*a*h))*exp(a*3*h),'o')
hold off

%
title('Euler Solution (h=0.1) of y''=-5y, y(0) = 1')

EulerRoundoff

% Script File: EulerRoundOff


% Global error for the fixed step Euler method in 3-digit floating point arithmetic
% applied to y' = -y across [0,1].

close all
t = linspace(0,1);
e = exp(-t);
for n = 140:20:180
tvals = linspace(0,1,n+1);
yvals = 0*tvals;
one = represent(1);
yc = one;
factor = float(one,float(one,represent(n),'/'),'-');
yvals(1) = 1;
for k=1:n
yc = float(factor,yc,'*');
yvals(k+1) = convert(yc);
end
plot(tvals,abs(exp(-tvals)-yvals))
hold on
axis([0 1 0 .05])
end
hold off
title('Euler in 3-Digit Arithmetic')
text(.76,.005,'h = 1/140')
text(.62,.012,'h = 1/160')
text(.42,.02,'h = 1/180')

ShowAB

% Script File: ShowAB


% Plots absolute error for fixed step size Adams-Bashforth
% solution to y' = y, y(0) = 1 across [0,5].

close all
E = zeros(5,5);
for i=1:5
n = 16*2^i;
Exact = exp(-linspace(0,5,n+1)');
for k=1:5
[tvals,yvals] = FixedAB('f1',0,1,5/n,k,n);
E(i,k) = max(abs(yvals-Exact));
end
end
semilogy([32 64 128 256 512]',E)
title('Adams-Bashforth on y''(t) = -y(t), y(0) = 1, 0<=t<=5, h = 5/n')
xlabel('n (Number of Steps)')
ylabel('Maximum absolute error')
text(530,E(5,1),'k=1')
text(530,E(5,2),'k=2')
text(530,E(5,3),'k=3')
text(530,E(5,4),'k=4')
text(530,E(5,5),'k=5')
axis([0 600 1e-12 1e-1])

ShowPC

% Script File: ShowPC


% Plots absolute error for fixed step size Adams PC
% solution to y' = y, y(0) = 1 across [0,5].

close all
E = zeros(5,5);
for i=1:5
n = 16*2^i;
Exact = exp(-linspace(0,5,n+1)');
for k=1:5
[tvals,yvals] = FixedPC('f1',0,1,5/n,k,n);
E(i,k) = max(abs(yvals-Exact));
end
end
semilogy([32 64 128 256 512]',E)
title('Adams PC on y''(t) = -y(t), y(0) = 1, 0<=t<=5, h = 5/n')
xlabel('n (Number of Steps)')
ylabel('Maximum absolute error')
text(530,E(5,1),'k=1')
text(530,E(5,2),'k=2')
text(530,E(5,3),'k=3')
text(530,E(5,4),'k=4')
text(530,E(5,5),'k=5')
axis([0 600 1e-14 1e-1])

ShowRK

% Script File: ShowRK


% Plots absolute error for fixed step size Runge-Kutta
% solution to y' = y, y(0) = 1 across [0,5].

close all
E = zeros(5,5);
for i=1:5
n = 16*2^i;
Exact = exp(-linspace(0,5,n+1)');
for k=1:5
[tvals,yvals] = FixedRK('f1',0,1,5/n,k,n);
E(i,k) = max(abs(yvals-Exact));
end
end
semilogy([32 64 128 256 512]',E)
title('Runge-Kutta on y''(t) = -y(t), y(0) = 1, 0<=t<=5, h = 5/n')
xlabel('n (Number of Steps)')
ylabel('Maximum absolute error')
text(530,E(5,1)+.003,'k=1')
text(530,E(5,2),'k=2')
text(530,E(5,3),'k=3')
text(530,E(5,4),'k=4')
text(530,E(5,5),'k=5')

ShowKepler

% Script File: ShowKepler


% Applies ODE23 and ODE45 to a system of differential equations
% that define an elliptical orbit.

close all
clc

% A simple call to ode23.

tInitial = 0;
tFinal = 2*pi;
uInitial = [ .4; 0 ; 0 ; 2];
tSpan = [tInitial tFinal];
[t, u] = ode23('Kepler', tSpan, uInitial);
nSteps = length(t)-1;
plot(u(:,1),u(:,3))
axis('equal')
title('Kepler Problem: ode23 with Default Tolerances')
xlabel(sprintf('Number of Steps = %5d',nSteps))
figure
plot(t(2:length(t)),diff(t))
title('Kepler Problem: ode23 with Default Tolerances')
ylabel('Step Length')
xlabel('t')
figure
subplot(2,2,1), plot(t,u(:,1)), title('x(t)')
subplot(2,2,2), plot(t,u(:,3)), title('y(t)')
subplot(2,2,3), plot(t,u(:,3)), title('x''(t)')
subplot(2,2,4), plot(t,u(:,4)), title('y''(t)')

% A call with specified output times.

tSpan = linspace(tInitial,tFinal,20);
[t, u] = ode23('Kepler', tSpan, uInitial);
xvals = spline(t,u(:,1),linspace(0,2*pi));
yvals = spline(t,u(:,3),linspace(0,2*pi));
figure
plot(xvals,yvals,u(:,1),u(:,3),'o')
axis('equal')
title('Kepler Problem: ode23 with Specified Output Times')
legend('Spline Fit','ode23 Output',0)

% A call with a more stringent tolerances

tSpan = [tInitial tFinal];


options = odeset('AbsTol',.00000001,'RelTol',.000001,'stats','on');
disp(sprintf('\n Stats for ODE23 Call:\n'))
[t, u] = ode23('Kepler', tSpan, uInitial,options);
nSteps = length(t)-1;
figure
plot(u(:,1),u(:,3))
axis('equal')
title('Kepler Problem: ode23 with RelTol = 10^{-6} and AbsTol = 10^{-8}')
xlabel(sprintf('Number of Steps = %5d',nSteps))
figure
plot(t(2:length(t)),diff(t))
title('Kepler Problem: ode23 with RelTol = 10^{-6} and AbsTol = 10^{-8}')
ylabel('Step Length')
xlabel('t')

% Use ODE45 on the same problem.

tSpan = [tInitial tFinal];


options = odeset('AbsTol',.00000001,'RelTol',.000001,'stats','on');
disp(sprintf('\n Stats for ode45 Call:\n'))
[t, u] = ode45('Kepler', tSpan, uInitial,options);
nSteps = length(t)-1;
figure
plot(u(:,1),u(:,3))
axis('equal')
title('Kepler Problem: ode45 with RelTol = 10^{-6} and AbsTol = 10^{-8}')
xlabel(sprintf('Number of Steps = %5d',nSteps))
figure
plot(t(2:length(t)),diff(t))
title('Kepler Problem: ode45 with RelTol = 10^{-6} and AbsTol = 10^{-8}')
ylabel('Step Length')
xlabel('t')
FixedEuler

function [tvals,yvals] = FixedEuler(fname,y0,t0,tmax,M2,tol)


% [tvals,yvals] = FixedEuler(fname,y0,t0,tmax,M2,tol)
% Fixed step Euler method.
%
% fname is a string that names a function of the form f(t,y).
% M2 a bound on the second derivative of the solution to
% y' = f(t,y), y(t0) = y0
% on the interval [t0,tmax].
%
% Determine positive n so that if tvals = linspace(t0,tmax,n), then
% y(i) is within tol of the true solution y(tvals(i)) for i=1:n.

n = ceil(((tmax-t0)^2*M2)/(2*tol))+1;
h = (tmax-t0)/(n-1);
yvals = zeros(n,1);
tvals = linspace(t0,tmax,n)';
yvals(1) = y0;
for n=1:n-1
fval = feval(fname,tvals(n),yvals(n));
yvals(n+1) = yvals(n)+h*fval;
end

ABStart

function [tvals,yvals,fvals] = ABStart(fname,t0,y0,h,k)


% [tvals,yvals,fvals] = ABStart(fname,t0,y0,h,k)
% Generates enough starting values for the kth order Adams-Bashforth method.
% Uses k-th order Runge-Kutta to generate approximate solutions to
%
% y'(t) = f(t,y(t)) y(t0) = y0
%
% at t = t0, t0+h, ... , t0 + (k-1)h.
%
% fname is a string that names the function f.
% t0 is the initial time.
% y0 is the initial value.
% h is the step size.
% k is the order of the RK method used.
%
% tvals is a column vector with tvals(j) = t0 + (j-1)h, j=1:k
% yvals is a matrix with yvals(j,:) = approximate solution at tvals(j), j=1:k
% For j =1:k, fvals(:,j) = f(tvals(j),yvals(j,:)).

tc = t0;
yc = y0;
fc = feval(fname,tc,yc);
tvals = tc;
yvals = yc';
fvals = fc;
for j=1:k-1
[tc,yc,fc] = RKstep(fname,tc,yc,fc,h,k);
tvals = [tvals; tc];
yvals = [yvals; yc'];
fvals = [fc fvals];
end

ABStep

function [tnew,ynew,fnew] = ABStep(fname,tc,yc,fvals,h,k)


% [tnew,ynew,fnew] = ABStep(fname,tc,yc,fvals,h,k)
% Single step of the kth order Adams-Bashforth method.
%
% fname is a string that names a function of the form f(t,y)
% where t is a scalar and y is a column d-vector.
%
% yc is an approximate solution to y'(t) = f(t,y(t)) at t=tc.
%
% fvals is an d-by-k matrix where fvals(:,i) is an approximation
% to f(t,y) at t = tc +(1-i)h, i=1:k
%
% h is the time step.
%
% k is the order of the AB method used, 1<=k<=5.
%
% tnew=tc+h.
% ynew is an approximate solution at t=tnew.
% fnew = f(tnew,ynew).

if k==1
ynew = yc + h*fvals;
elseif k==2
ynew = yc + (h/2)*(fvals*[3;-1]);
elseif k==3
ynew = yc + (h/12)*(fvals*[23;-16;5]);
elseif k==4
ynew = yc + (h/24)*(fvals*[55;-59;37;-9]);
elseif k==5
ynew = yc + (h/720)*(fvals*[1901;-2774;2616;-1274;251]);
end
tnew = tc+h;
fnew = feval(fname,tnew,ynew);

FixedAB

function [tvals,yvals] = FixedAB(fname,t0,y0,h,k,n)


% [tvals,yvals] = FixedAB(fname,t0,y0,h,k,n)
% Produces an approximate solution to the initial value problem
%
% y'(t) = f(t,y(t)) y(t0) = y0
%
% using a strategy that is based upon a k-th order
% Adams-Bashforth method. Stepsize is fixed.
%
% fname = string that names the function f.
% t0 = initial time.
% y0 = initial condition vector.
% h = stepsize.
% k = order of method. (1<=k<=5).
% n = number of steps to be taken,
%
% tvals is a column vector with tvals(j) = t0 + (j-1)h, j=1:n+1
% yvals is a matrix with yvals(j,:) = approximate solution at tvals(j), j=1:n+1

[tvals,yvals,fvals] = ABStart(fname,t0,y0,h,k);
tc = tvals(k);
yc = yvals(k,:)';
fc = fvals(:,k);

for j=k:n
% Take a step and then update.
[tc,yc,fc] = ABstep(fname,tc,yc,fvals,h,k);
tvals = [tvals; tc];
yvals = [yvals; yc'];
fvals = [fc fvals(:,1:k-1)];
end

AMStep

function [tnew,ynew,fnew] = AMstep(fname,tc,yc,fvals,h,k)


% [tnew,ynew,fnew] = AMstep(fname,tc,yc,fvals,h,k)
% Single step of the kth order Adams-Moulton method.
%
% fname is a string that names a function of the form f(t,y)
% where t is a scalar and y is a column d-vector.
%
% yc is an approximate solution to y'(t) = f(t,y(t)) at t=tc.
%
% fvals is an d-by-k matrix where fvals(:,i) is an approximation
% to f(t,y) at t = tc +(2-i)h, i=1:k
%
% h is the time step.
%
% k is the order of the AM method used, 1<=k<=5.
%
% tnew=tc+h
% ynew is an approximate solution at t=tnew
% fnew = f(tnew,ynew).

if k==1
ynew = yc + h*fvals;
elseif k==2
ynew = yc + (h/2)*(fvals*[1;1]);
elseif k==3
ynew = yc + (h/12)*(fvals*[5;8;-1]);
elseif k==4
ynew = yc + (h/24)*(fvals*[9;19;-5;1]);
elseif k==5
ynew = yc + (h/720)*(fvals*[251;646;-264;106;-19]);
end
tnew = tc+h;
fnew = feval(fname,tnew,ynew);

PCStep
function [tnew,yPred,fPred,yCorr,fCorr] = PCStep(fname,tc,yc,fvals,h,k)
% [tnew,yPred,fPred,yCorr,fCorr] = PCStep(fname,tc,yc,fvals,h,k)
% Single step using the kth-order Adams predictor-corrector framework.
%
% fname is a string that names a function of the form f(t,y)
% where t is a scalar and y is a column d-vector.
%
% yc is an approximate solution to y'(t) = f(t,y(t)) at t=tc.
%
% fvals is an d-by-k matrix where fvals(:,i) is an approximation
% to f(t,y) at t = tc +(1-i)h, i=1:k
%
% h is the time step.
%
% k is the order of the Adams methods used, 1<=k<=5.
%
% tnew=tc+h,
% yPred is the predicted solution at t=tnew
% fPred = f(tnew,yPred)
% yCorr is the corrected solution at t=tnew
% fCorr = f(tnew,yCorr).

[tnew,yPred,fPred] = ABstep(fname,tc,yc,fvals,h,k);
[tnew,yCorr,fCorr] = AMstep(fname,tc,yc,[fPred fvals(:,1:k-1)],h,k);

FixedPC

function [tvals,yvals] = FixedPC(fname,t0,y0,h,k,n)


% [tvals,yvals] = FixedPC(fname,t0,y0,h,k,n)
% Produces an approximate solution to the initial value problem
%
% y'(t) = f(t,y(t)) y(t0) = y0
%
% using a strategy that is based upon a k-th order
% Adams Predictor-Corrector framework. Stepsize is fixed.
%
% fname = string that names the function f.
% t0 = initial time.
% y0 = initial condition vector.
% h = stepsize.
% k = order of method. (1<=k<=5).
% n = number of steps to be taken,
%
% tvals is a column vector with tvals(j) = t0 + (j-1)h, j=1:n+1
% yvals is a matrix with yvals(j,:) = approximate solution at tvals(j), j=1:n+1.

[tvals,yvals,fvals] = ABStart(fname,t0,y0,h,k);
tc = tvals(k);
yc = yvals(k,:)';
fc = fvals(:,k);

for j=k:n
% Take a step and then update.
[tc,yPred,fPred,yc,fc] = PCstep(fname,tc,yc,fvals,h,k);
tvals = [tvals; tc];
yvals = [yvals; yc'];
fvals = [fc fvals(:,1:k-1)];
end
RKStep

function [tnew,ynew,fnew] = RKStep(fname,tc,yc,fc,h,k)


% [tnew,ynew,fnew] = RKStep(fname,tc,yc,fc,h,k)
% Single step of the kth order Runge-Kutta method.
%
% fname is a string that names a function of the form f(t,y)
% where t is a scalar and y is a column d-vector.
%
% yc is an approximate solution to y'(t) = f(t,y(t)) at t=tc.
%
% fc = f(tc,yc).
%
% h is the time step.
%
% k is the order of the Runge-Kutta method used, 1<=k<=5.
%
% tnew=tc+h, ynew is an approximate solution at t=tnew, and
% fnew = f(tnew,ynew).

if k==1
k1 = h*fc;
ynew = yc + k1;
elseif k==2
k1 = h*fc;
k2 = h*feval(fname,tc+h,yc+k1);
ynew = yc + (k1 + k2)/2;
elseif k==3
k1 = h*fc;
k2 = h*feval(fname,tc+(h/2),yc+(k1/2));
k3 = h*feval(fname,tc+h,yc-k1+2*k2);
ynew = yc + (k1 + 4*k2 + k3)/6;
elseif k==4
k1 = h*fc;
k2 = h*feval(fname,tc+(h/2),yc+(k1/2));
k3 = h*feval(fname,tc+(h/2),yc+(k2/2));
k4 = h*feval(fname,tc+h,yc+k3);
ynew = yc + (k1 + 2*k2 + 2*k3 + k4)/6;
elseif k==5
k1 = h*fc;
k2 = h*feval(fname,tc+(h/4),yc+(k1/4));
k3 = h*feval(fname,tc+(3*h/8),yc+(3/32)*k1+(9/32)*k2);
k4 = h*feval(fname,tc+(12/13)*h,yc+(1932/2197)*k1-(7200/2197)*k2+(7296/2197)*k3);
k5 = h*feval(fname,tc+h,yc+(439/216)*k1 - 8*k2 + (3680/513)*k3 -(845/4104)*k4);
k6 = h*feval(fname,tc+(1/2)*h,yc-(8/27)*k1 + 2*k2 -(3544/2565)*k3 + (1859/4104)*k4 - (11/40)*k5);
ynew = yc + (16/135)*k1 + (6656/12825)*k3 + (28561/56430)*k4 - (9/50)*k5 + (2/55)*k6;
end
tnew = tc+h;
fnew = feval(fname,tnew,ynew);

FixedRK

function [tvals,yvals] = FixedRK(fname,t0,y0,h,k,n)


% [tvals,yvals] = FixedRK(fname,t0,y0,h,k,n)
% Produces approximate solution to the initial value problem
%
% y'(t) = f(t,y(t)) y(t0) = y0
%
% using a strategy that is based upon a k-th order
% Runge-Kutta method. Stepsize is fixed.
%
% fname = string that names the function f.
% t0 = initial time.
% y0 = initial condition vector.
% h = stepsize.
% k = order of method. (1<=k<=5).
% n = number of steps to be taken,
%
% tvals is a column vector with tvals(j) = t0 + (j-1)h, j=1:n+1
% yvals is a matrix with yvals(j,:) = approximate solution at tvals(j), j=1:n+1

tc = t0;
yc = y0;
tvals = tc;
yvals = yc';
fc = feval(fname,tc,yc);
for j=1:n
[tc,yc,fc] = RKstep(fname,tc,yc,fc,h,k);
yvals = [yvals; yc' ];
tvals = [tvals; tc];
end

Kepler

function up = Kepler(t,u)
% up = Kepler(t,u)
% t (time) is a scalar and u is a 4-vector whose components satisfy
%
% u(1) = x(t) u(2) = (d/dt)x(t)
% u(3) = y(t) u(4) = (d/dt)y(t)
%
% where (x(t),y(t)) are the equations of motion in the 2-body problem.
%
% up is a 4-vector that is the derivative of u at time t.

r3 = (u(1)^2 + u(3)^2)^1.5;
up = [ u(2) ;...
-u(1)/r3 ;...
u(4) ;...
-u(3)/r3] ;

f1

function yp = f1(t,y)
yp = -y;
Chapter 1: Problem Solutions
Problems Home

P1.1.1 P1.2.1 P1.3.1 P1.4.1 P1.5.1 P1.6.1 P1.7.1


P1.1.2 P1.2.2 P1.3.2 P1.4.2 P1.5.2 P1.6.2
P1.2.3 P1.3.3 P1.4.3 P1.5.3 P1.6.3
P1.2.4 P1.3.4 P1.4.4 P1.5.4
P1.2.5 P1.4.5 P1.5.5
P1.2.6 P1.4.6 P1.5.6
P1.2.7 P1.4.7 P1.5.7
P1.2.8 P1.4.8
P1.2.9 P1.4.9
P1.2.10 P1.4.10

P1.1.1

% Problem P1_1_1
%
% Plots various built-in functions

close all

x = linspace(-1,1);
plot(x,abs(x))
title('The Absolute Value Function')
pause(2)

figure
x = linspace(0,10);
plot(x,sqrt(x))
title('The Square Root Function')
pause(2)

figure
x = linspace(-2,2);
plot(x,exp(x))
title('The Exponential')
pause(2)

figure
x = linspace(1,100);
plot(x,log(x))
title('The Natural Log')
pause(2)

figure
x = linspace(0,4*pi);
plot(x,sin(x))
title('The Sine Function')
pause(2)

figure
x = linspace(0,4*pi);
plot(x,cos(x))
title('The Cosine Function')
pause(2)

figure
x = linspace(-1,1);
plot(x,asin(x))
title('The Inverse Sine Function')
pause(2)

figure
x = linspace(-1,1);
plot(x,acos(x))
title('The Inverse Cosine Function')
pause(2)

figure
x = linspace(-5,5);
plot(x,atan(x))
title('The Inverse Tangent Function')
pause(2)

P1.1.2

% Problem P1_1_2
%
% Plotting a periodic function.

close all

z1 = linspace(0,2,51);
y2 = sqrt(1 -(z1-1).^2);

% Note, plot(z1,y2) displays the first of the four semicircles.


z = [z1 z1(2:51)+2 z1(2:51)+4 z1(2:51)+6];
y = [y2 y2(2:51) y2(2:51) y2(2:51)];
plot(z,y)
axis equal

P1.2.1

% Problem P1_2_1
%
% Subvector illustration

clc
z = [10 40 20 80 30 70 60 90]
disp(' ')
disp('z(1:2:7) = ')
disp(' ')
disp(z(1:2:7))
disp(' ')
disp('z(7:-2:1) = ')
disp(' ')
disp(z(7:-2:1))

disp(' ')
disp('z([3 1 4 8 1]) = ')
disp(' ')
disp(z([3 1 4 8 1]))

P1.2.2
% Problem P1_2_2
%
% Subvector assignment illustration

clc
disp(' ')
disp('If z =')
z = [10 40 20 80 30 70 60 90];
disp(' ')
disp(z)
disp(' ')
disp('then after z(1:2:7) = zeros(1,4) it looks like')
disp(' ')
z(1:2:7) = zeros(1,4);
disp(z)

disp(' ')
disp('If z =')
z = [10 40 20 80 30 70 60 90];
disp(' ')
disp(z)
disp(' ')
disp('then after z(7:-2:1) = zeros(1,4) it looks like')
disp(' ')
z(7:-2:1) = zeros(1,4);
disp(z)

disp(' ')
disp('If z =')
z = [10 40 20 80 30 70 60 90];
disp(' ')
disp(z)
disp(' ')
disp('then after z([3 4 8 1]) = zeros(1,4) it looks like')
disp(' ')
z([3 4 8 1]) = zeros(1,4);
disp(z)

P1.2.3

% Problem P1_2_3
%
% Efficient circle plotting

close all
x = linspace(0,1,200);
y = sqrt(1 - x.^2);
plot(x,y)
axis square equal
hold on
plot(x,-y)
plot(-x,y)
plot(-x,-y)
hold off

P1.2.4

% Problem P1_2_4
%
% Superpositioning

close all

% Method 1:
figure
x = linspace(0,2*pi);
plot(x,sin(x),x,sin(2*x),x,sin(3*x),x,sin(4*x),x,sin(5*x))
title('Method 1')

% Method 2:
figure
plot(x,sin(x));
hold on
for k=2:5
plot(x,sin(k*x),'-')
end
hold off
title('Method 2')

P1.2.5

% Problem P1_2_5
%
% Plotting x, x^2, ... ,x^m where m is a solicited integer.

close all
m = input('Enter positive integer m: ');
x = linspace(0,1); xk = x;
plot(x,x);
hold on
for k=2:m
xk = xk.*x;
plot(x,xk)
end
hold off
title(sprintf(' Plots of x^k across [0,1] for k=1:%1.0f',m))

P1.2.6

% Problem P1_2_6
%
% Partial sum of the exponential power series.

close all

x = linspace(0,1); % or any other vector


m = input('Enter m: ');

y = ones(size(x));
term = x;
for k=1:m
term = x.*term/k;
y = y+term;
end

plot(x,y)
title(sprintf('Partial sum of exp(x) using %2.0f terms',m))

P1.2.7

% Problem P1_2_7
%
% Ellipse plots.

close all

x = linspace(0,2*pi);
c = cos(x);
s = sin(x);
plot(3*6*c,-2+9*s,7+2*c,8+6*s)
axis square equal

P1.2.8

% Problem P1_2_8
%
% Sine superpositioning

close all

x = linspace(0,2*pi);
y = sin(x);
plot(x/2,y)
hold on
for k=1:3
plot((k*pi)+x/2,y)
end
hold off
title('sin(x) across [0,4*pi]')

P1.2.9

% Problem P1_2_9
%
% Plotting exp(-x)*sin(x)
close all

x = linspace(0,2*pi);
y = sin(x);
z = exp(-x);

plot(x,y.*z)
hold on
plot(x+2*pi,z(100)*(y.*z))
hold off
title('e^{-x}sin(x) across [0,4\pi]')

P1.2.10

% Problem P1_2_10
%
% Plot a function and its derivative

close all

subplot(2,1,1)
x = linspace(-10,10,200)';
A = [sin(x) sin(2*x) sin(3*x) sin(4*x)];
y = A*[2;3;7;5];
plot(x,y)
title('f(x) = 2sin(x) + 3sin(2x) + 7sin(3x) + 5sin(4x)')

subplot(2,1,2)
Ap = [cos(x) 2*cos(2*x) 3*cos(3*x) 4*sin(4*x)];
yp = Ap*[2;3;7;5];
plot(x,yp)
title('f''(x) = 2cos(x) + 6cos(2x) + 21cos(3x) + 20sin(4x)

P1.3.1

% Problem P1_3_1
%
% Explore the Updown Sequence

n = 200;
g = zeros(n,1);
for m=1:n
% How long does it take for the UpDown sequence to converge
% with starting value = m?
x = m;
k = 1;
while (x > 1)
if rem(x,2) == 0;
x = x/2;
else
x = 3*x+1;
end
k = k+1;
end
g(m) = k;
end
plot(g)
title('g(m) = is the index of the first iterate that equals 1')
xlabel('m = the starting value')
ylabel('g(m)')

P1.3.2

% Problem P1_3_2
%
% Roots of random quadratics.

clc
disp(' Prob Complex Roots Prob Complex Roots')
disp(' n (Uniform Coeff) (Normal Coeff) ')
disp('--------------------------------------------------')
for n=100:100:800

% Uniformly distributed coefficients:


a = rand(n,3);
% The ith quadratic is a(i,1)x^2 + a(i,2)x + a(i,3). It has complex
% roots if the discriminant a(i,2)^2 - 4*a(i,1)*a(i,3)<0
pcmplx_U = sum(a(:,2).^2 < 4*a(:,1).*a(:,3))/n;

% Normally distributed coefficients:


a = randn(n,3);
% The ith quadratic is a(i,1)x^2 + a(i,2)x + a(i,3). It has complex
% roots if the discriminant a(i,2)^2 - 4*a(i,1)*a(i,3)<0
pcmplx_N = sum(a(:,2).^2 < 4*a(:,1).*a(:,3))/n;
disp(sprintf('%5d %6.4f %6.4f ',n,pcmplx_U,pcmplx_N))
end

P1.3.3

% Problem P1_3_3
%
% Volume of a 4-dimensional sphere

n = input('Enter number of trials: ');

% Throw n darts at the 4-cube with center (0,0,0,0) and edge length 2.
% This cube has volume 16.

A = -1 + 2*rand(n,4);

% The location of the ith dart is (A(i,1),A(i,2), A(i,3), A(i,4)).


% If the sum of squares of the coordinates is less than 1, it is inside
% the unit sphere.

hits = sum(A(:,1).^2 + A(:,2).^2 + A(:,3).^2 + A(:,4).^2 <= 1);


volume = (hits/n)*16;
clc
disp(sprintf('Volume of unit sphere in 4-space = %6.4f (based on %5d trials)',volume,n))

P1.3.4

% Problem P1_3_4
%
% Monte Carlo area estimate.

x0 = .2;
y0 = .4;

n = input('Enter number of trials: ');

x = -1+2*rand(n,1);
y = -1+2*rand(n,1);

% The points (x(i),y(i)) are uniformly distributed in S.

d = sqrt((x-x0).^2+(y-y0).^2); % Distances to (x0,y0).


d_edge = min(min(abs(x+1),abs(x-1)),min(abs(y+1),abs(y-1))); % Distances to nearest edge.

inside_S0 = sum(d<d_edge);
area_S0 = (inside_S0/n)*4;

clc
disp(sprintf('Area of S0 = %6.4f (based on %5d trials)',area_S0,n))

P1.4.1

% Problem P1_4_1
%
% Prints a table showing error in Stirling estimation of binomial coefficients.

close all
clc

disp( ' ')


disp(' Stirling Absolute Relative')
disp(' k 52-choose-k Approximation Error Error')
disp('----------------------------------------------------------------')
e=exp(1);
n = 52;
sn = sqrt(2*pi*n)*((n/e)^n);
true = n;
for k=2:13
sk = sqrt(2*pi*k)*((k/e)^k);
snmk = sqrt(2*pi*(n-k))*(((n-k)/e)^(n-k));
bnk = (sn/sk)/snmk;
true = true*(n-k+1)/k;
abserror = abs(true-bnk);
relerror = abserror/true;
s1 = sprintf(' %2.0f %14.0f %16.2f',k,true,bnk);
s2 = sprintf(' %13.2e %5.2e',abserror,relerror);
disp([s1 s2])
end

P1.4.2

% Problem P1_4_2
%
% Plots, as a function of n, the relative error in the
% Taylor sin(x) approximation
%
% x + x^2/2! +...+ x^n/n!
close all
nTerms = 50;
for x=[1 5 10 20 40]
figure
error = sin(x)*ones(nTerms,1);
s = x;
term = x;
x2 = x^2;
for k=1:nTerms
term = -x2*term/(2*k*(2*k+1));
s = s+ term;
error(k) = abs(error(k) - s);
end;
relerr = error/abs(sin(x));
semilogy(1:nTerms,relerr)
ylabel('Relative Error in Partial Sum.')
xlabel('Order of Partial Sum.')
title(sprintf('x = %5.2f',x))
pause(3)
end

P1.4.3

% Problem P1_4_3
%
% Taylor Approximation to sin(x)

close all
n = input('Enter number of terms:');
x = linspace(0,2*pi);
s = x;
term = x;
s2 = x.^2;
for k=1:n
term = -(term.*s2)/(2*k*(2*k+1));
s = s + term;
end
plot(x,sin(x),x,s)
title(sprintf('Approximation of sin(x) with %2.0f-term Taylor Sum',n))

P1.4.4

% Problem P1_4_4
%
% Exact representation of n!
% It is safe to assume that mantissa length will be the
% limiting factor because a factorial in the neighborhood
% of 2^100 will involve far more than 24 significant bits
% Compute MaxInt = the largest integer that can be stored in 24 bits.

clc
b = 24;
MaxInt = 2^b - 1;
n = 0;
nfact = 1;
s = MaxInt-1;
disp(' n n! x n!/x MaxInt-(n!/x)')
disp('------------------------------------------------------------------')
while(s>=0)
n = n+1;
nfact = nfact*n;
% Compute x = largest power of 2 that divides n!.

TwoPower = 1;
while floor((nfact/TwoPower))*TwoPower == nfact
TwoPower = 2*TwoPower;
end
x = TwoPower/2;

% n! is exactly representable if n!/x <= MaxInt

s = MaxInt-(nfact/x);
disp(sprintf('%2.0f %15.0f %8.0f %15.0f %15.0f',n,nfact,x,nfact/x,s))
end
disp(' ')
disp(sprintf('The largest n so n! is exactly representable is n = %1d.',n-1))

P1.4.5

% Problem P1_4_5
%
% Spacing of the floating point numbers.

clc
L = 4;
disp(' ')
disp(' Interval Spacing of F.P. numbers')
disp('----------------------------------------')
for spacing=-12:-6
R = 2*L;
disp(sprintf(' [%3.0f,%3.0f) 2^(%3.0f)',L,R,spacing))
L = R;
end
disp(' ')
disp('So the next f.p. number after 70 is 70 + 2^-8')

P1.4.6

% Problem P1_4_6
%
% Spacing of the floating point numbers.

clc
L = 1;
disp(' ')
disp(' Interval Spacing of F.P. numbers')
disp('----------------------------------------')
for k=1:6
R = 2*L;
disp(sprintf(' [%2.0f,%2.0f) 2^(-t+%1.0f)',L,R,k))
L = R;
end
disp(' ')
disp('So the minimum y-x = 2^(-t+4) + 2^(-t+3)')
P1.4.7

P1.4.8

% Problem 1_4_8
%
% Nearest floating point number neighbor to a positive integer.

x = input('Enter a positive integer:');


t = input('Enter t, the length of the base-2 mantissa:');

% Compute m (.5<=m<1) and e so x = m * 2^e

m=x;e=0;
while (m>=1)
m=m/2; e=e+1;
end
while (m<1/2)
m=2*m; e=e-1;
end

clc
disp(sprintf('x = %1d',x))
disp(sprintf('t = %1d',t))
if (m==1/2)
disp(sprintf('The previous floating point number is %1d - 2^%1d',x,-t+e-1))
else
disp(sprintf('The previous floating point number is %1d - 2^%1d',x,-t+e))
end
disp(sprintf( 'The next floating point number is %1d + 2^%1d',x,-t+e))

P1.4.9

Not Available.

P1.4.10

Not Available

P1.5.1

Not Available

P1.5.2

Not Available

P1.5.3
Not Available

P1.5.4

Not Available

P1.5.5

Not Available

P1.5.6

% Problem P1_5_6
%
% Vectorized sine-cosine computation.

% Suppose you have a sine and cosine table with entries for 0,1,...,90 degrees.
% How could you "expand" these tables so that they reported the sines and
% cosines for 0, 0.5, 1.0, 1.5, 2.0,..., 89.0, 89.5, and 90.0 degrees?
% This problem is about that.

clc
a = input('Enter a:');
b = input('Enter b with b>a:');
n = input('Enter integer n with n>=1:');

c = cos(linspace(a,b,n+1));
s = sin(linspace(a,b,n+1));

[cnew,snew] = F(c,s,a,b);

err_cnew = sum(abs(cnew - cos(linspace(a,b,2*n+1)')))


err_snew = sum(abs(snew - sin(linspace(a,b,2*n+1)')))

function [cnew,snew] = F(c,s,a,b)


% a and b are scalars with a<b. c and s are row (n+1)-vectors with the
% property that c = cos(linspace(a,b,n+1)) and s = sin(linspace(a,b,n+1)).
%
% cnew and snew are column (2n+1)-vectors with the property that
% c = cos(linspace(a,b,2n+1)) and s = sin(linspace(a,b,2n+1)).

% Establish cnew and snew:

n = length(c)-1;
cnew = zeros(2*n+1,1);
snew = zeros(2*n+1,1);

% If theta = linspace(a,b,2*n+1), then


%
% c = cos(theta(1:2:2*n+1))
% s = sin(theta(1:2:2*n+1)).
%
% Thus, the odd-indexed components of cnew and snew are "free":
cnew(1:2:2*n+1) = c';
snew(1:2:2*n+1) = s';

% If delta = (b-a)/(2*n), the spacing between the theta(k), then using the given
% trig identities we have
%
% cos(theta(2*k)) = cos(theta(2*k-1))cos(delta) - sin(theta(2*k-1))*sin(delta)
% sin(theta(2*k)) = sin(theta(2*k-1))cos(delta) + cos(theta(2*k-1))*sin(delta)
%
% for k=1:n.

delta = (b-a)/(2*n);
c_delta = cos(delta);
s_delta = sin(delta);

cnew(2:2:2*n) = cnew(1:2:2*n-1)*c_delta - snew(1:2:2*n-1)*s_delta;


snew(2:2:2*n) = snew(1:2:2*n-1)*c_delta + cnew(1:2:2*n-1)*s_delta;

P1.5.7

Not Available

P1.6.1

Not Available

P1.6.2

Not Available

P1.6.3

Not Available

P1.7.1

Not Available
Chapter 2: Problem Solutions
Problems Home

P2.1.1 P2.2.1 P2.3.1 P2.4.1


P2.1.2 P2.2.2 P2.3.2 P2.4.2
P2.1.3 P2.2.3 P2.3.3 P2.4.3
P2.1.4 P2.2.4 P2.4.4
P2.1.5 P2.2.5 P2.4.5
P2.4.6
P2.4.7

P2.1.1
% Problem P2_1_1
%
% Modified Vandermonde interpolation of census data
% Census Data from 1820 through 1980.

x = (1820:10:1980)';
y = [ 9638453; ...
12866020; ...
17068953; ...
23191876; ...
31443321; ...
38558371; ...
50189209; ...

62979766; ...
75994575; ...
91972266; ...
105710620; ...
122775046; ...
131669275; ...
150697361; ...
179323175; ...
203235298; ...
226547082];
clc

disp('Predict 1980 population based on census data')


disp('for years k:10:1970 where k is the starting year.')
disp(' ')
yr = input('Enter starting year: ');
z = linspace(yr,1980)';
k = 16 - (1970-yr)/10;
close all
format long
figure
a = MyInterpV(x(k:16),y(k:16));
pval = HornerV(a,z);
plot(z,pval,x(k:17),y(k:17),'o')
title('Prediction of 1980 population using basis x^j')

figure
u = yr;
a = MyInterpV(x(k:16),y(k:16),u);
pval = HornerV(a,z-u);
plot(z,pval,x(k:17),y(k:17),'o')
title(sprintf('Prediction of 1980 population using basis (x-%4.0f)^j',u))

figure
u = (yr+1970)/2;
a = MyInterpV(x(k:16),y(k:16),u);
pval = HornerV(a,z-u);
plot(z,pval,x(k:17),y(k:17),'o')
title(sprintf('Prediction of 1980 population using basis (x-%4.0f)^j',u))
figure
u = (yr+1970)/2;
v = (1970-yr)/2;
a = MyInterpV(x(k:16),y(k:16),u,v);
pval = HornerV(a,(z-u)/v);
plot(z,pval,x(k:17),y(k:17),'o')
title(sprintf('Prediction of 1980 population using basis ((x-%4.0f)/%2.0f)^j',u,v))

function a = MyInterpV(x,y,u,v)
%
% x is a column n-vector with distinct components.
% y is a column n-vector.
% u is a scalar (optional).
% v is a scalar (optional).
%
% a is a column n-vector with the property that
% if p(x) = a(1) + a(2)xbar + ... a(n)xbar^(n-1) with
% xbar = (x-u)/v then p(x(i)) = y(i), i=1:n.
%
% A call of the form MyInterpV(x,y,u) assumes v=1.
% A reference of the form MyInterpV(x,y) assumes u=0 and v=1.

n = length(x);
V1 = ones(n,n);
if nargin <= 3
v = 1;
end

if nargin == 2
u = 0;
end

xbar = (x-u)/v;
for j=2:n
% Set up column j.
V1(:,j) = xbar.*V1(:,j-1);
end
a = V1\y;

P2.1.2
% Problem P2_1_2
%
% Polynomial evaluation for odd and even polynomials.

close all

z = linspace(-pi,pi)';
c = [1 1 2 6 24 120 720 5040 40320 322880 3228800];
a = [1 -1/6 1/120 -1/5040 1/322880];
y = HornerVgen(a,z,'odd');
subplot(2,1,1)
plot(z,y)
title('First 5 terms of Taylor series for sin(x)')

a = [1 -1/2 1/24 -1/720 1/40320 ]';


y = HornerVgen(a,z,'even');
subplot(2,1,2)
plot(z,y)
title('First 5 terms of Taylor series for cos(x)')

function pval = HornerVgen(a,z,s)


%
% a is a column n-vector.
% z is a column m-vector.
% s is a string equal to 'odd' or 'even'.
%
% pval is a column m-vector with the property that if
% pval(i) = p(z(i)) for i=1:m where
%
% p(x) = a(1)x + a(2)x^3 + a(3)x^5 +...+ a(n)x^(2n-1) if s = 'odd'
% p(x) = a(1) + a(2)x^2 + a(3)x^4 +...+ a(n)x^(2n-2) if s = 'even'

n = length(a);
m = length(z);

z2 = z.*z;

pval = a(n)*ones(m,1);
for k=n-1:-1:1
pval = z2.*pval + a(k);
end
if (length(s) == 3)
if s == 'odd'
pval = z.*pval;
end
end

P2.1.3

% Problem P2_1_3
%
% Odd and even polynomials.

clc
z = linspace(0,1,10)';
a = rand(9,1);

% Part A.
%
% Write p(x) = (pe(x) + po(x))/2 where pe(x) = (p(x)+p(-x)) and
% po(x) = (p(x)-p(-x)). Note that pe and po are even and odd.
% See P2.1.2. It follows that
% p(x)/p(-x) = (pe(x)+po(x))/(pe(x)-po(x))
% The cost of evaluating pe AND po is roughly equal to the cost
% of evaluating p. Thus, we can evaluate the given rational with
% little more work than required to evaluate p.

z2 = z.^2;
n = length(a);
pe = HornerV(a(1:2:n),z2);
po = z.*HornerV(a(2:2:n),z2);
partA = (pe + po)./(pe - po);
CheckA = (HornerV(a,z)./HornerV(a,-z)) - partA

% Part B.
%
% Compute the even polynomial p(x) + p(-x). See above.

partB = 2*pe;
CheckB = (HornerV(a,z) + HornerV(a,-z)) - partB

% Part C.
%
% Check the derivative calculation via divided differences:
partC = HornerV(a(2:n).*(1:(n-1))',z);
CheckC = ((HornerV(a,z+10^(-8))-HornerV(a,z))/10^(-8)) - partC

partD = HornerV([0 ; a./(1:n)'],1)


partE = HornerV([0 ; a./(1:n)'],z) - HornerV([0 ; a./(1:n)'],-z)

P2.1.4

% Problem P2_2_1
%
% Conversion from the Newton to the 1,x,x^2,.. repesentation

clc

z = linspace(0,2*pi)';
n = 7;
x = linspace(0,2*pi,n)';
y = cos(x);
c = InterpN(x,y);
a1 = N2V(c,x(1:n-1));
a2 = InterpV(x,y);
home
disp(sprintf('Coefficients for interpolant of cos(z) at z = linspace(0,2pi,%1.0f)',n))
disp(' ')
disp(' via N2V via InterpV')
disp('------------------------------------------------------')
for i=1:n
disp(sprintf(' %20.16f %20.16f',a1(i),a2(i)))
end

function a = N2V(c,x)
%
% c is a column n-vector
% x is a column (n-1)-vector
%
% a is a column n-vector so that if
% p(z) = c(1) + c(2)(z-x(1)) + ... + c(n)(z-x(1))...(z-x(n-1))
%

z = linspace(min(x),max(x),length(c))';

% Evaluate p(z) at the n points z(1),...,z(n) and construct the


% Vandermonde interpolant of (z(i),p(z(i)), i=1:n>

a = InterpV(z,HornerN(c,x,z));

P2.1.5

P2.2.1

% Problem P2_2_1
%
% Conversion from the Newton to the 1,x,x^2,.. repesentation

clc

z = linspace(0,2*pi)';
n = 7;
x = linspace(0,2*pi,n)';
y = cos(x);
c = InterpN(x,y);
a1 = N2V(c,x(1:n-1));
a2 = InterpV(x,y);
home
disp(sprintf('Coefficients for interpolant of cos(z) at z = linspace(0,2pi,%1.0f)',n))
disp(' ')
disp(' via N2V via InterpV')
disp('------------------------------------------------------')
for i=1:n
disp(sprintf(' %20.16f %20.16f',a1(i),a2(i)))
end

function a = N2V(c,x)
%
% c is a column n-vector
% x is a column (n-1)-vector
%
% a is a column n-vector so that if
% p(z) = c(1) + c(2)(z-x(1)) + ... + c(n)(z-x(1))...(z-x(n-1))
%

z = linspace(min(x),max(x),length(c))';

% Evaluate p(z) at the n points z(1),...,z(n) and construct the


% Vandermonde interpolant of (z(i),p(z(i)), i=1:n>

a = InterpV(z,HornerN(c,x,z));

P2.2.2

% Problem P2_2_2
%
% Illustrate recursive evaluation of interpolating polynomial.

close all
z = linspace(0,2*pi)';
x = linspace(0,2*pi,11)';
y = sin(x);
pval = RecurEval(x,y,z);
plot(z,pval,x,y,'o')
title('Interpolant of sin(x) at x = linspace(0,2pi,11)')

function pval = RecurEval(x,y,z)


% x is a column n-vector with distinct entries
% y is a column n-vector
% z is a column m-vector
%
% pval is a column m-vector with the property that pval(i) = p(x(i))
% where p(x) is the degree n-1 interpolant of (x(i),y(i)), i=1:n.

n = length(x);
if n==1
pval(1) = y(1);
else
pvalL = RecurEval(x(1:n-1),y(1:n-1),z);
pvalR = RecurEval(x(2:n),y(2:n),z);
pval = ((z-x(n)).*pvalL - (z-x(1)).*pvalR)/(x(1)-x(n));
end

P2.2.3

% Problem P2_2_3
%
% Interactive plot of a function and a chosen interpolant.

clc
s = input('Enter the name of a function in quotes: ');
n = input('Enter the number of interpolation points: ');
L = input('Enter the left endpoint of the interval: ');
R = input('Enter the right endpoint of the interval: ');
x = linspace(L,R,n)';
y = feval(s,x);
z = linspace(L,R)';
c = InterpN(x,y);
pval = HornerN(c,x,z);
fval = feval(s,z);
plot(z,fval,z,pval,x,y,'o')
t = ['Plot of ' s ' and a ' sprintf('%1.0f',n) '-point interpolant'];
title(t)

P2.2.4

P2.2.5

P2.3.1

% Problem P2_3_1
%
% Flop comparison of HornerN and HornerV

F = zeros(9,4);
a = linspace(0,1,10)';
c = linspace(0,1,10)';
x = linspace(0,1,10)';
z = linspace(0,1,200)';
i=1;
for n=2:10
j=1;
for m=50:50:200
flops(0)
pval = HornerV(a(1:n),z(1:m));
fV = flops;
flops(0)
pval = HornerN(c(1:n),x(1:n),z(1:m));
fN = flops;
F(i,j) = fN/fV;
j=j+1;
end
i=i+1;
end

clc
disp('(Flops HornerN)/(Flops HornerV)')
disp(' ')
disp(' n m=50 m=100 m=150 m=200')
disp('--------------------------------------------')
for i=1:9
s1 =sprintf(' %5.3f ',F(i,:));
disp([sprintf('%3.0f ',i+1) s1])
end

P2.3.2
% Problem 2_3_2
%
% Explore the function e(s)
close all
for n=2:10
z = linspace(0,n-1)';
e = z;
for k=1:n-1
e = e.*(z-k)/(k+1);
end
plot(z,e)
title(sprintf('n=%2.0f max|e(s)| = %4.2f',n,max(abs(e)) ))
pause(1)
end

P2.3.3

% Problem P2_3_3
%
% Using the interpolation polynomial error bound.
% f(x) = exp(ax). For the n-point interpolant, the n-th
% derivative is important and it equals a^n exp(ax).
% It follows that it is no bigger than
% derBound(n) = (abs(a)^n)*max([exp(a*L) exp(a*R)]);
%
% From (2.2) it follows that the error in the interpolant is bounded by
%
% e(n) = (derBound(n)/4n)( (R-L)/(n-1) )^n

clc
disp(' R a : delta=10^-3 delta=10^-4 delta=10^-5 delta=10^-6')
disp('--------------------------------------------------------------------')
for a=[-5 -1 1 5]
L = 0;
for R = [1 2 4 8];
n1 = nbest(L,R,a,.001);
n2 = nbest(L,R,a,.0001);
n3 = nbest(L,R,a,.00001);
n4 = nbest(L,R,a,.00001);
disp(sprintf('%4.0f %4.0f : %4.0f %4.0f %4.0f %4.0f',a,R,n1,n2,n3,n4))
end
end

function n = nbest(L,R,a,delta)
% Smallest n so that the n-point equal spacing interpolant of
% exp(ax) on [L,R} has error less than delta.

n = 2;
while ((abs(a)^n)*max([exp(a*L) exp(a*R)])/(4*n))*((R-L)/(n-1))^n > delta
n=n+1;
end

P2.4.1

% Problem P2_4_1
%
% Compare InterpN and InterpN2

n=21;
format long
x = linspace(-1,1,n)';
y = 1./(1+25*x.^2);
c1 = InterpN(x,y);
c2 = InterpN2(x,y);
clc
home
disp('abs(InterpN(x,y) - InterpN2(x,y)) = ')
disp(' ')
disp(abs(c1 - c2))
P2.4.2

% Problem P2_4_2
%
% Compare InterpN and InterpNEqual

clc

n = 10;
L = 0;
R = 2*pi;
x = linspace(L,R,n)';
y = sin(x);

c = InterpN(x,y);
[cE xE yE] = InterpNEqual('sin',L,R,n);

disp('Compare InterpNEqual and InterpN.')


disp(' ')
disp('abs(c-cE) = ')
disp(' ')
disp(abs(c-cE))

function [c,x,y] = InterpNEqual(fname,L,R,n)


% fname is a string that names a function of the form f(x)
% L < R and n is a positive integer.
%
% x = linspace(L,R,n)'
% y = f(x)
% c = the coefficients of the Newton interpolant of f at x.

x = linspace(L,R,n)';
h = (R-L)/(n-1);
y = feval(fname,x);
c = y;
for k=2:n
c(k:n) = diff(c(k-1:n))/(h*(k-1));
end

P2.4.3

% Problem P2_4_3
%
% Inverse Quadratic Interpolation. Find zero of sin function
% in the vicinity of PI.

clc
x = [3 ; 3.5 ; 4];
y = sin(x);
root = InverseQ(x,y);
disp(sprintf('Root = %8.6f', root))

function root = InverseQ(x,y)


% x and y are column 3-vectors with x(1) < x(2) < x(3) and y(1)y(3)<0
% root = q(0) where q is the quadratic interpolant
% of (y(1),x(1)), (y(2),x(2)), (y(3),x(3))

a = InterpV(y,x);
root = HornerV(a,0);

P2.4.4
% Script P2_4_4
%
% Illustrates SetUp and CubicInterp2D.

a = 0; b = 5; n = 16;
c = 0; d = 3; m = 10;
fA = SetUp('Humps2D',a,b,n,c,d,m);
E = zeros(n-1,m-1);
hx = (b-a)/(n-1);
hy = (d-c)/(m-1);
clc

disp('E(i,j) is the error in 2D cubic interpolant of humps2D(xc,yc)')


disp('at the center of the rectangle {(u,v)|x(i)<=u<=x(i+1), y(j)<=v<=y(j+1)}')
disp('where x = linspace(0,5,16) and y = linspace(0,3,10).')
disp(' ')

disp('E = ')
disp(' ')
for i=1:n-1
for j=1:m-1
xc = a+(i-.5)*hx;
yc = c+(j-.5)*hy;
exact = Humps2D(xc,yc);
approx = CubicInterp2D(xc,yc,a,b,n,c,d,m,fA);
E(i,j) = abs(approx-exact);
end

disp(sprintf('%6.4f ',E(i,:)))
end

function z = CubicInterp2D(x,y,a,b,n,c,d,m,fA)
%
% n,m: integers >=2
% x,a,b: scalars that satisfy a<=x<\=b
% y,c,d: scalars that satisfy c<=y<=d
% fA: an n-by-m matrix with the property that
% fA(i,j) = f(a+(i-1)(b-a)/(n-1),c+(j-1)(d-c)/(m-1))
% for some function f(.,.).
% Post:
% z: cubically interpolated value for f(x,y).
%
% Determine where in the grid the point is located.

hx = (b-a)/(n-1);
i = max([1 ceil((x-a)/hx)]);
hy = (d-c)/(m-1);
j = max([1 ceil((y-c)/hy)]);
% Adjust for edge effects
if (1 < i)&(i < n-1)
% not on an edge
ivals = (i-1):(i+2);
elseif 1==i
% on left edge
ivals = 1:4;
else
% on right edge
ivals = n-3:n;
end
if (1 < j)&(j < m-1)
% not on an edge
jvals = (j-1):(j+2);
elseif 1==j
% on bottom edge
jvals = 1:4;
else
% on top edge
jvals = m-3:m;
end
% Determine coordinates of the grid points.
xvals = c + (ivals-1)*hx;
yvals = c + (jvals-1)*hy;
% call Small2D with the appropriate 4x4 subgrid:

z = Small2D(x,y,xvals',yvals',fA(ivals,jvals));

function z = Small2D(x,y,xvals,yvals,fvals)
%
% x and y are scalars representing the point at which to interpolate f
% xvals and yvals are column 4-vectors with
% xvals(1) < xvals(2) < xvals(3) < xvals(4)
% yvals(1) < yvals(2) < yvals(3) &ly yvals(4)
% fvals is a 4x4 matrix of evaluations of f at the xvals,yvals coords.
%
% Let p1 be the cubic interpolant of (xvals(q),fvals(q,1)), q=1:4.
% Let p2 be the cubic interpolant of (xvals(q),fvals(q,2)), q=1:4.
% Let p3 be the cubic interpolant of (xvals(q),fvals(q,3)), q=1:4.
% Let p4 be the cubic interpolant of (xvals(q),fvals(q,4)), q=1:4.
%
% Let p be the cubic interpolant of the four points
% (yvals(1),p1(x))
% (yvals(2),p2(x))
% (yvals(3),p3(x))
% (yvals(4),p4(x))
%
% z = p(y).

fy = zeros(4,1);
for k=1:4
c = InterpN(xvals,fvals(:,k)); % interpolate horizontally
fy(k) = HornerN(c,xvals,x); % evaluate interpolants at x
end

c = InterpN(yvals,fy); % interpolate vertically


z = HornerN(c,yvals,y); % evaluate interpolant at y

P2.4.5

% Problem P2_4_5
%
% Plotting the derivative of the polynomial interpolant.

n = 6;
x = linspace(0,2*pi,n);
y = sin(x);

z = linspace(0,2*pi);
yp = cos(z);
plot(z,yp,'.')

hold on
PlotDerPoly(x,y)
hold off

function PlotDerPoly(x,y)
% x and y are column n-vectors with x(1) < ... < x(n)
% Plots p'(z) where p is the polynomial interpolant of
% (x(i),y(i)), i=1:n

n = length(x);
a = polyfit(x,y,n-1);

der_a = a(1:n-1).*((n-1):-1:1);
z = linspace(x(1),x(n));
der_pvals = polyval(der_a,z);
plot(z,der_pvals)

P2.4.6
z

P2.4.7

z
Chapter 3: Problem Solutions
Problems Home

P3.1.1 P3.2.1 P3.3.1


P3.1.2 P3.2.2 P3.3.2
P3.1.3 P3.2.3 P3.3.3
P3.1.4 P3.2.4 P3.3.4
P3.1.5 P3.2.5 P3.3.5
P3.1.6 P3.3.6
P3.1.7 P3.3.7
P3.1.8 P3.3.8
P3.1.9 P3.3.9
P3.1.10 P3.3.10
P3.3.11
P3.3.12
P3.3.13
P3.3.14

P3.1.1

% Problem P3_1_1
%
% Compare Locate with MyLocate

n = input('Enter n (the length of the partition): ');


m = input('Enter m (the number of evaluation points): ');
a = rand(n-1,1);
b = rand(n-1,1);
x = linspace(0,1,n)';
zvals = linspace(0,1,m)';
flops(0)
Lvals = pwLEval(a,b,x,zvals);
f1 = flops;
flops(0)
Lvals = MypwLEval(a,b,x,zvals);
f2 = flops;

clc
disp(sprintf('x = linspace(0,1,%2.0f), zvals = linspace(0,1,%2.0f)',n,m))
disp(' ')
disp(sprintf('Flops using Locate: %5.0f ',f1))
disp(sprintf('Flops using MyLocate: %5.0f ',f2))
disp(' ')
disp(' ')
x = sort(rand(n,1));
zvals = sort(rand(m,1));
flops(0)
Lvals = pwLEval(a,b,x,zvals);
f1 = flops;
flops(0)
Lvals = MypwLEval(a,b,x,zvals);
f2 = flops;
disp(sprintf('x = sort(rand(%2.0f,1)), zvals = sort(rand(%2.0f,1))',n,m))
disp(' ')
disp(sprintf('Flops using Locate: %5.0f ',f1))
disp(sprintf('Flops using MyLocate: %5.0f ',f2))

function LVals = MypwLEval(a,b,x,zvals)


% x is a column n-vector with x(1) < ... < x(n).
% zvals is a column m-vector with each component in [x(1),x(n)].
% a,b are column (n-1)-vectors
%
% LVals is a column m-vector with the property that
% LVals(j) = L(zvals(j)) for j=1:m where
% L(z) = a(i) + b(i)(z-x(i)) for x(i)<=z<=x(i+1).

m = length(zvals);
LVals = zeros(m,1);
g = 1;
for j=1:m
i = MyLocate(x,zvals(j),g);
LVals(j) = a(i) + b(i)*(zvals(j)-x(i));
g = i;
end

P3.1.2

% Problem P3_1_2
%
% Location in uniform partitions

x = linspace(-2,2,5);
clc
disp('i x(i) z x(i+1)')
disp('-------------------------')
for z = linspace(-2,2,9)
i = LocateUniform(x(1),x(5),5,z);
disp(sprintf('%1.0f %3.0f %4.1f %3.0f',i,x(i),z,x(i+1)))
end

function i = LocateUniform(alpha,beta,n,z)
% alpha<=z<=beta and n a positive integer >=2
% x(i)<=z<=x(i+1) where x = linspace(alpha,beta,n)
h = (beta-alpha)/(n-1);
i = max(ceil((z-alpha)/h),1);

P3.1.3

% Problem P3_1_3
%
% Applying pwLAdapt on sin(x) on [0,2pi].

[x,y] = pwLAdapt('sin',0,0,2*pi,0,.01,.01);
z = linspace(0,2*pi);
plot(z,sin(z),x,y,x,y,'o')

P3.1.4

% Problem P3.1.4
%
% Looks at pwLAdapt on [0,1] using the function
%
% humps(x) = 1/((x-.3)^2 + .01) + 1/((x-.9)^2+.04)
%
% with delta = 0.

close all
delta = 0;
for d = [128 129];
hmin = 1/d;
figure
[x,y] = pwlAdapt('humps',0,humps(0),1,humps(1),delta,hmin);
plot(x,y,'.');
title(sprintf('hmin = 1/%3.0f Adapt n = %2.0f',d,length(x)))
end

P3.1.5
% Problem P3_1_5
%
% Examine the effect of second derivative on pwLAdapt
% f(x) = exp(-x)sin(kx)
% f'(x) = -exp(-x)sin(kx) + 10exp(-x)cos(kx)
% f''(x) = -2exp(-x)cos(x)

clc
disp('f(x) = exp(-.3x)sin(6.1x)')
disp('Second derivative bound on [a,b] is 40exp(-.3a) assuming a>=0.')
delta = .0001;
disp(' ')
disp(' [x,y] = pwLAdapt(''MyF315'',a,fa,b,fb,.0001,0)')
disp(' ')
disp(' a b bound min(diff(x)) length(x)')
disp('------------------------------------------------------------')
for k=0:8
a = k*pi; fa = feval('MyF315',a);
b = a+pi; fb = feval('MyF315',b);
[x,y] = pwlAdapt('MyF315',a,fa,b,fb,delta,0);
n = length(x);
bound = 40*exp(-.3*a);
xdiffmin = min(diff(x));
disp(sprintf(' %6.2f % 6.2f %9.6f %8.4f %5.0f',a,b,bound,xdiffmin,n))
end

function y = MyF315(x)
y = exp(-.3*x).*sin(6.1*x);

P3.1.6

% Problem P3.1.6
%
% Compares pwLStatic and pwLAdapt1 on [0,1] using the function
%
% humps(x) = 1/((x-.3)^2 + .01) + 1/((x-.9)^2+.04)
%
close all
% Second derivative estimate based on divided differences
z = linspace(0,1,101);
humpvals = humps(z);
M2 = max(abs(diff(humpvals,2)/(.01)^2));
for delta = [1 .5 .1 .05 .01]
figure
[x,y] = pwlStatic('humps',M2,0,1,delta);
subplot(1,2,1)
plot(x,y,'.');
title(sprintf('delta = %8.4f Static n= %2.0f',delta,length(x)))
[x,y] = pwlAdapt1('humps',0,humps(0),1/3,humps(1/3),2/3,humps(2/3),1,humps(1),delta,.001);
subplot(1,2,2)
plot(x,y,'.');
title(sprintf('delta = %8.4f Adapt n= %2.0f',delta,length(x)))
end

function [x,y] = pwLAdapt1(fname,xL,fL,xL1,fL1,xR1,fR1,xR,fR,delta,hmin)


% fname is a string that names an available function of the form y = f(u).
% xL,fL are scalars where fL = f(xL)
% xL1,fL1 are scalars where fL1 = f(xL1)
% xR,fR are scalars where fR = f(xR)
% xR1,fR1 are scalars where fR1 = f(xR1)
%
% xL1 = (2L + R)/3 and xR1 = L + 2R)/3
% delta is a positive real
% hmin is a positive real
%
% x is a column n-vector with the property that
% xL = x(1) < ... < x(n) = xR.
% Each subinterval [L,R] is either <= hmin in length or has the property
% that |f(m) - L(m)| <= delta for m = (2L+R)/3 or (L+2R)/3 and where L(x)
% is the line that connects (xL,fL) and (xR,fR).
%
% y is a column n-vector with the property that y(i) = f(x(i)).

if (xR-xL) <= hmin


% Subinterval is acceptable
x = [xL;xR];
y = [fL;fR];
else
if abs((2*fL+fR)/3 - fL1) <= delta & abs((fL+2*fR)/3 - fR1) <= delta
% Subinterval accepted.
x = [xL;xR];
y = [fL;fR];
else
% Produce left and right partitions, then synthesize.
mid = (xL+xR)/2;
fmid = feval(fname,mid);
zL = (5*xL+xR)/6;
fzL = feval(fname,zL);
zR = (xL+5*xR)/6;
fzR = feval(fname,zR);
[xLeft,yLeft] = pwLAdapt1(fname,xL,fL,zL,fzL,xL1,fL1,mid,fmid,delta,hmin);
[xRight,yRight] = pwLAdapt1(fname,mid,fmid,xR1,fR1,zR,fzR,xR,fR,delta,hmin);
x = [ xLeft;xRight(2:length(xRight))];
y = [ yLeft;yRight(2:length(yRight))];
end
end

P3.1.7

% Problem P3_1_7
%
% Applying pwLAdapt to sqrt

close all
[x,y] = pwLAdapt('sqrt',0,0,1,1,.001,.01);
z = linspace(0,1);
plot(z,sqrt(z),x,y,x,y,'o')
axis([-.1 1.1 -.2 1.2])
hold on
plot(x,zeros(size(x)),'.')
hold off

P3.1.8

% Problem P3_1_8
%
% Examine pwLAdapt with feval max on [0,1] using the function
%
% humps(x) = 1/((x-.3)^2 + .01) + 1/((x-.9)^2+.04)
%
close all

eMax = 50;

for delta = [1 .5 .1 .05 .01]


[x,y,eTaken] = pwlAdapt2('humps',0,humps(0),1,humps(1),delta,.001,eMax);
figure
plot(x,y,'.');
axis([0 1 0 100])
title(sprintf('delta = %8.4f x(%1.0f) = %5.3f',delta,length(x),x(length(x))))
pause
end

function [x,y,eTaken] = pwLAdapt2(fname,xL,fL,xR,fR,delta,hmin,eMax)


% fname is a string that names an available function of the form y = f(u).
% xL is real and fL = f(xL)
% xR is real and fR = f(xR)
% delta is a positive real
% hmin is a positive real
% eMax is a positive integer representing the maximum number of fevals allowed.
%
% x is a column n-vector with the property that
% xL = x(1) < ... < x(n) = xR. Each subinterval
% is either <= hmin in length or has the property
% that at its midpoint m, |f(m) - L(m)| <= delta
% where L(x) is the line that connects (xR,fR).
%
% y is a column n-vector with the property that y(i) = f(x(i)).
% eTaken is the number of f-evaluations required.

if (xR-xL) <= hmin


% Subinterval is acceptable
x = [xL;xR];
y = [fL;fR];
eTaken = 0
else
mid = (xL+xR)/2;
fmid = feval(fname,mid);
eTaken = 1;
eMax = eMax-1;
if (abs(((fL+fR)/2) - fmid) <= delta )
% Subinterval accepted.
x = [xL;xR];
y = [fL;fR];
else
% Produce left and right partitions, then synthesize.
if eMax ==0
x = xL;
y = fL;
return
end
[xLeft,yLeft,eTakenL] = pwLAdapt2(fname,xL,fL,mid,fmid,delta,hmin,eMax);
eTaken = eTaken + eTakenL;
eMax = eMax - eTakenL;
if eMax == 0
x = xLeft;
y = yLeft;
return
end

[xRight,yRight,eTakenR] = pwLAdapt2(fname,mid,fmid,xR,fR,delta,hmin,eMax);
eTaken = eTaken + eTakenR;
x = [ xLeft;xRight(2:length(xRight))];
y = [ yLeft;yRight(2:length(yRight))];
end
end

P3.1.9

% Problem P3_1_9
%
% Examine pwLAdapt with saved f-evals on [0,1] using the function
%
% humps(x) = 1/((x-.3)^2 + .01) + 1/((x-.9)^2+.04)
%

close all
[x,y,xunused,yunused] = pwlAdapt3('humps',0,humps(0),1,humps(1),1,.001,[],[]);
plot(x,y,'.',xunused,yunused,'o');
title('o = the saved f-evaluations')

P3.1.10

P3.2.1
% Problem P3_2_1
%
% Apply pwcStatic to exp(-2x)sin(10pi*x) on [0,1].

close all
alpha = 0;
beta = 1;
delta = .001;
z = linspace(0,1)';
fz = feval('MyF321',z);
for M4 = [100 1000 10000 100000 1000000]
[a,b,c,d,x] = pwCStatic('MyF321','dMyF321',M4,alpha,beta,delta);
Cvals = pwCEval(a,b,c,d,x,z);
err = max(abs(Cvals-fz));
figure
plot(z,fz,z,Cvals,x,feval('MyF321',x),'o')
title(sprintf('M4 = %3.0e n = %2.0f delta = %2.0e error = %2.0e',M4,length(x),delta,err))
end

function [a,b,c,d,x] = pwCStatic(fname,fpname,M4,alpha,beta,delta)


% fname is a string that names an available function f(x).
% Assume that f can take vector arguments.
% M4 is an upper bound for|f''''(x)| on [alpha,beta].
% alpha and beta are scalars with alpha<beta
% delta is a positive scalar
%
% x is a column n-vector that defines the partition.
% a,b,c,d is a column (n-1)-vectors with the property that C(x(i)) = f(x(i)),
% i=1:n. The piecewise cubic Hermite interpolant C(x) of this
% data satisfies |C(x) - f(x)| <= delta, where
% x = linspace(alpha,beta,n).

n = max(2,ceil(1 + ((beta-alpha)*M4/(384*delta))^.25) );
x = linspace(alpha,beta,n)';
y = feval(fname,x);
s = feval(fpname,x);
a = y(1:n-1);
b = s(1:n-1);
Dx = (beta-alpha)/(n-1);
Dy = diff(y);
yp = Dy/Dx;
c = (yp - s(1:n-1))/Dx;
d = (s(1:n-1) - 2*yp + s(2:n))/Dx^2;

function y = dMyF321(x)
x2 = 10*pi*x;
y = exp(-2*x).*( -2*sin(x2) + (10*pi)*cos(x2));

P3.2.2
% Problem P3_2_2
%
% Compare pwLAdapt and pwCAdapt on f(x) = sqrt(x) on [.001,9]

hmin = .001;
L = .001; fL = sqrt(L); sL = .5/sqrt(L);
R = 9.000; fR = sqrt(R); sR = .5/sqrt(R);
clc
disp(' pwL pwC')
disp(' delta length(x) length(x)')
disp('--------------------------------------')
for delta = [.1 .01 .001 .0001 .00001]

[x,y] = pwLAdapt('sqrt',L,fL,R,fR,delta,hmin);
nL = length(x);
[x,y,s] = pwCAdapt('sqrt','dMyF322',L,fL,sL,R,fR,sR,delta,hmin);
nC = length(x);
disp(sprintf(' %7.5f %3.0f %3.0f',delta,nL,nC))
end
function [x,y,s] = pwCAdapt(fname,fpname,xL,fL,sL,xR,fR,sR,delta,hmin)
% fname is a string that names an available function of the form y = f(u).
% fpname is a string that names a function that is the derivative of f.
% xL is real and fL = f(xL) and sL = f'(xL).
% xR is real and fR = f(xR) and sR = f'(xR).
% delta is a positive real
% hmin is a positive real
%
% x is a column n-vector with the property that
% xL = x(1) < ... < x(n) = xR. Each subinterval
% is either <= hmin in length or has the property
% that at its midpoint m, |f(m) - q(m)| <= delta
% where q(x) is the cubic hermite interpolant of

% (xL,fL,sL) and (xR,fR,sR).


%
% y is a column n-vector with the property that
% y(i) = f(x(i)).
%
% sc is a column n-vector with the property that
% s(i) = f'(x(i)).

if (xR-xL) <= hmin


% Subinterval is acceptable
x = [xL;xR];
y = [fL;fR];
s = [sL;sR];
else
mid = (xL+xR)/2;
fmid = feval(fname,mid);

% Compute the cubic hermite interpolant and evaluate at the midpoint:


[alfa,beta,gamma,eta] = HCubic(xL,fL,sL,xR,fR,sR);
qeval = pwCEval(alfa,beta,gamma,eta,[xL;xR],mid);
if abs(qeval - fmid) <= delta
% Subinterval accepted.
x = [xL;xR];
y = [fL;fR];
s = [sL;sR];
else
% Produce left and right partitions and synthesize.

smid = feval(fpname,mid);

[xLeft,yLeft,sLeft] = pwCAdapt(fname,fpname,xL,fL,sL,mid,fmid,smid,delta,hmin);
[xRight,yRight,sRight] = pwCAdapt(fname,fpname,mid,fmid,smid,xR,fR,sR,delta,hmin);
x = [ xLeft;xRight(2:length(xRight))];
y = [ yLeft;yRight(2:length(yRight))];
s = [ sLeft;sRight(2:length(sRight))];
end
end

function y = dMyF322(x)
y = .5./sqrt(x);

P3.2.3
% Problem P3_2_3
%
% Variable subinterval piecewise cubic hermite interpolation.

close all
[x,y] = pwCHExp(0,5,.0001);
[a,b,c,d] = pwC(x,y,-y);
z = linspace(0,5)';
Cvals = pwCEval(a,b,c,d,x,z);
plot(z,exp(-z),z,Cvals,x,y,'o')
title('Piecewise cubic hermite interpolant of exp(-x)')
function [x,y] = pwCHExp(a,b,tol)
% a,b are real scalars with a < b.
% tol is a positive real scalar.
%
% x,y is a column n-vectors where a = x(1) < x(2) < ... < x(n) = b
% and y(i) = exp(-x(i)), i=1:n.
% The partition is chosen so that if C(z) is the piecewise cubic
% hermite interpolant of exp(-z) on this partition,
% then |C(z) - exp(-z)| <= tol for all z in [a,b].

x = a;
y = exp(-a);
n = 1;
while x(n) < b
[R,fR] = stretch(x(n),y(n),tol);
if R < b
x = [x;R];
y = [y;fR];
else
x = [x;b];
y = [y;exp(-b)];
end
n = n+1;
end

function [R,fR] = stretch(L,fL,tol);


% L,fL are scalars that satisfy fL = exp(-L)
% tol is a positive real.
%
% R,fR are scalars that satisfy fR = exp(-R) with the
% property that if q(z) is the cubic hermite interpolant
% of exp(-z) at z=L and z=R, then |q(z) - exp(-z)| <= tol on [L,R].

h = sqrt(sqrt((384*tol/fL)));
R = L + h;
fR = exp(-R);

P3.2.4

P3.2.5

P3.3.1

% Problem P3_3_1
%
% Not-a-knot spline interpolation of f(x) = x^3.

x = sort(rand(4,1));
y = x.^3;
[a,b,c,d] = CubicSpline(x,y);
z = linspace(x(1),x(4));
Cvals = pwCEval(a,b,c,d,x,z);
z0 = linspace(x(1),x(4),20);
plot(z,Cvals,z0,z0.^3,'o')
title('Not-a-knot spline interpolant of x^3')

P3.3.2

% Problem P3_3_2
%
% 4-point not-a-knot spline interpolant
close all
x = sort(rand(4,1));
y = rand(4,1);
[a,b,c,d] = CubicSpline(x,y);
z = linspace(x(1),x(4))';
Cvals = pwCEval(a,b,c,d,x,z);
a = InterpV(x,y);
z0 = linspace(x(1),x(4),20)';
pvals = HornerV(a,z0);
plot(z,Cvals,x,y,'*',z0,pvals,'o')
title('n=4 not-a-knot is just cubic interpolation.')

P3.3.3

% Problem P3_3_3
%
% Natural spline interpolant of x^3.

close all
x = [-3;-1;1;3];
y = x.^3;
[a,b,c,d] = CubicSpline(x,y,2,0,0);
S0 = pwCEval(a,b,c,d,x,0);
z = linspace(-3,3);
Cvals = pwCEval(a,b,c,d,x,z);
plot(z,Cvals,x,y,'o')
title(sprintf('S(0) = %8.4e',S0))

P3.3.4

% Problem P3_3_4
%
% Illustrate spline-under-tension idea. As sigma increases,
% the approximant looks more and more like a piecewise linear
% function.

close all
n = 10;

x = linspace(0,1,n)';
y = exp(-2*x).*sin(10*pi*x);
s = -2*exp(-2*x).*sin(10*pi*x) + 10*pi*exp(-2*x).*cos(10*pi*x);
z = linspace(0,1)';
fz = exp(-2*z).*sin(10*pi*z);
plot(z,fz,x,y,'o')
sigma = 100;
[a,b,c,d] = Fit(x,y,s,sigma);
hold on
for i=1:n-1
xvals = linspace(x(i),x(i+1),100)';
u = exp(sigma*(xvals-x(i)));
yvals = a(i) + b(i)*(xvals-x(i)) + c(i)*u + d(i)./u;
plot(xvals,yvals,'.')
end
hold off
title('n=10, f(x) = exp(-2x)sin(10*pi*x), sigma = 100')
for sigma = [ 10 100 250];
[a,b,c,d] = Fit(x,y,s,sigma);
figure
hold on
for i=1:n-1
xvals = linspace(x(i),x(i+1),100)';
u = exp(sigma*(xvals-x(i)));
yvals = a(i) + b(i)*(xvals-x(i)) + c(i)*u + d(i)./u;
plot(xvals,yvals,x,y,x,y,'o')
end
title(sprintf('PWL fit and the tension spline fit with sigma = %6.1f',sigma))
hold off
end
function [a,b,c,d] = Fit(x,y,s,sigma)
% x,y,s are column n-vectors
% sigma is a positive scalar
%
% a,b,c,d are column (n-1)-vectors so that if
%
% g(z) = a(i) + b(i)(z-x(i)) + c(i)exp(sigma(z-x(i))) + d(i)exp(-sigma(z-x(i)))
%
% then g(x(i))= y(i), g'(x(i)) = s(i), g(x(i+1)) = y(i+1), and g'(x(i+1)) = s(i+1)
% for i=1:n.

n = length(x);
a = zeros(n-1,1);
b = zeros(n-1,1);
c = zeros(n-1,1);
d = zeros(n-1,1);

for i=1:n-1
del = x(i+1)-x(i);
f = exp(sigma*del);
M = [1 0 1 1; ...
1 del f 1/f; ...
0 1 sigma -sigma; ...
0 1 sigma*f -sigma/f];
rhs = [y(i);y(i+1);s(i);s(i+1)];
u = M\rhs;
a(i) = u(1);
b(i) = u(2);
c(i) = u(3);
d(i) = u(4);
end

P3.3.5

% Problem P3_3_5
%
% Illustrates CubicSpline1 with various end conditions,
% some not so good.

close all
xvals = linspace(0,4*pi,100);
yvals = sin(xvals);
for n = 4:3:16
x = linspace(0,4*pi,n)';
y = sin(x);
[a,b,c,d] = CubicSpline1(x,y,1);
svals = pwCEval(a,b,c,d,x,xvals);
plot(xvals,yvals,xvals,svals)
title(sprintf('Spline interpolant of sin(x) using qL'' and qR''. (%2.0f subintervals)',n-1))
pause
end
for n = 4:3:16
x = linspace(0,4*pi,n)';
y = sin(x);
[a,b,c,d] = CubicSpline1(x,y,2);
svals = pwCEval(a,b,c,d,x,xvals);
plot(xvals,yvals,xvals,svals)
title(sprintf('Spline interpolant of sin(x) using qL'''' and qR''''. (%2.0f subintervals)',n-1))
pause
end

for n = 4:3:16
x = linspace(0,4*pi,n)';
y = sin(x);
[a,b,c,d] = CubicSpline1(x,y,1);
svals = pwCEval(a,b,c,d,x,xvals);
plot(xvals,yvals,xvals,svals)
title(sprintf('Spline interp. of sin(x) using derivatives of end interpolants. (%2.0f subintervals)',n-1))
pause
end
for n = 4:3:16
x = linspace(0,4*pi,n)';
y = sin(x);
[a,b,c,d] = CubicSpline1(x,y,1,1,1);
svals = pwCEval(a,b,c,d,x,xvals);
plot(xvals,yvals,xvals,svals)
title(sprintf('''Good'' Complete spline interpolant of sin(x). (%2.0f subintervals)',n-1))
pause
end
for n = 4:3:16
x = linspace(0,4*pi,n)';
y = sin(x);
[a,b,c,d] = CubicSpline1(x,y,1,-1,-1);
svals = pwCEval(a,b,c,d,x,xvals);
plot(xvals,yvals,xvals,svals)
title(sprintf('''Bad'' Complete spline interpolant of sin(x). (%2.0f subintervals)',n-1))
pause
end
for n = 4:3:16
x = linspace(0,4*pi,n)';
y = sin(x);
[a,b,c,d] = CubicSpline1(x,y,2,0,0);
svals = pwCEval(a,b,c,d,x,xvals);
plot(xvals,yvals,xvals,svals)
title(sprintf('Natural spline interpolant of sin(x). (%2.0f subintervals)',n-1))
pause
end
for n = 4:3:16
x = linspace(0,4*pi,n)';
y = sin(x);
[a,b,c,d] = CubicSpline1(x,y);
svals = pwCEval(a,b,c,d,x,xvals);
plot(xvals,yvals,xvals,svals)
title(sprintf('Not-a-Knot spline interpolant of sin(x). (%2.0f subintervals)',n-1))
pause
end

function [a,b,c,d] = CubicSpline1(x,y,derivative,muL,muR)


% x,y are column n-vectors. n >= 4 and x(1) < ... x(n)
% derivative is the order of the spline's derivative that are
% used in the end conditions (= 1 or 2)
% muL,muR are the values of the specified derivative at the
% left and right endpoint.
%
% a,b,c,d are column (n-1)-vectors that define the spline.
% On [x(i),x(i+1)], the spline S(z) is specified by the cubic
%
% a(i) + b(i)(z-x(i)) + c(i)(z-x(i))^2 + d(i)(z-x(i))^2(z-x(i+1).
%
% Usage:
% [a,b,c,d] = CubicSpline1(x,y,1,muL,muR) S'(x(1)) = muL, S'(x(n)) = muR
% [a,b,c,d] = CubicSpline1(x,y,1) S'(x(1)) = qL'(x(1)), S'(x(n)) = qR'(x(n))
% [a,b,c,d] = CubicSpline1(x,y,2,muL,muR) S''(x(1)) = muL, S''(x(n)) = muR
% [a,b,c,d] = CubicSpline1(x,y,2) S''(x(1)) = qL''(x(1)), S''(x(n)) = qR''(x(n))
% [a,b,c,d] = CubicSpline1(x,y) S'''(z) continuous at x(2) and x(n-1)
% Here qL = cubic interpolant of (x(i),y(i)), i=1:4
% qR = cubic interpolant of (x(i),y(i)), i=n-3:n

if nargin == 3
% need qL and qR
n = length(x);
cL = InterpN(x(1:4),y(1:4));
cR = InterpN(x(n:-1:n-3),y(n:-1:n-3));
end
if nargin == 2
[a,b,c,d] = CubicSpline(x,y);
else
if derivative == 1
if nargin == 3
% Differentiate the Newton interpolants and evaluate.
muL = cL(2) + cL(3)*(x(1)-x(2)) + cL(4)*((x(1)-x(2)) *(x(1)-x(3)));
muR = cR(2) + cR(3)*(x(n)-x(n-1)) + cR(4)*((x(n)-x(n-1))*(x(n)-x(n-2)));
end
[a,b,c,d] = CubicSpline(x,y,1,muL,muR);
else
if nargin ==3
% Differentiate the Newton interpolants twice and evaluate.
muL = 2*cL(3) + 2*cL(4)*((x(1)-x(2)) + (x(1)-x(3)));

muR = 2*cR(3) + 2*cR(4)*((x(n)-x(n-1)) + (x(n)-x(n-2)));


end
[a,b,c,d] = CubicSpline(x,y,2,muL,muR);
end
end

P3.3.6

% Problem P3_3_6
%
% Integral of the splines second derivative.

clc
disp(' n Natural Not-a-Knot Bad Complete')
disp('--------------------------------------------------')

for n = [10 20 40 80 160 320 ]


x = linspace(0,1,n)';
y = 1./(1+25*x.^2);
[a,b,c,d] = CubicSpline(x,y,2,0,0);
e1 = Energy(ConvertS(a,b,c,d,x));
[a,b,c,d] = CubicSpline(x,y);
e2 = Energy(ConvertS(a,b,c,d,x));
[a,b,c,d] = CubicSpline(x,y,1,30,-15);
e3 = Energy(ConvertS(a,b,c,d,x));
disp(sprintf(' %3.0f %8.5e % 8.5e %8.5e',n,e1,e2,e3))
end

function e = Energy(S)
% S is the pp representation of a spline
% e is the integral from x(1) to x(n) of the
% square of the second derivative of the spline

[x,rho,L,k] = unmkpp(S);

% The second derivative of the ith cubic is 2*rho(i,2) + 6*rho(i,1)(x-x(i)).


% The integral from x(i) to x(i+1) of [2*rho(i,2) + 6*rho(i,1)(x-x(i))]^2
% is
% 4* rho(i,2)^2 * del + 12*rho(i,2)*rho(i,1)*del^2 + 12*rho(i,1)^2*del^3

del = (x(2:L+1)-x(1:L))';
r1 = 4*rho(:,2).^2;
r2 = 12*rho(:,2).*rho(:,1);
r3 = 12*rho(:,1).^2;
e = sum(((del.*r3 + r2).*del + r1).*del);

P3.3.7

% Problem P3_3_7
%
% Max 3rd derivative continuity.

clc
n = input('Enter the number of points to interpolate: ');
x = linspace(0,pi,n)';
y = exp(-x).*sin(x);

S = spline(x,y);
d3 = MaxJump(S)
function d3 = MaxJump(S);
% S is the pp representation of a cubic spline.
% d3 is the absolute value of the largest 3rd derivative
% jump at any knot.

[x,rho,L,k] = unmkpp(S);
% 3rd derivative of the i-th local cubic is 6*rho(i,1)

if L==1
d3 = 0;
else
d3 = 6*max(abs(rho(2:L,1)-rho(1:L-1,1)));
end

P3.3.8

% Problem P3_3_8
%
% Different representations for piecewise cubics.

x = [0 .1 .3 .5 .6 1]';
y = sin(2*pi*x);

% Generate not-a-knot spline 2 ways:


[a,b,c,d] = CubicSpline(x,y);
S1 = Spline(x,y);
[x1,rho1,L1,k1] = unmkpp(S1);

% Convertthe book spline tothe pp representation and compare


% with S1.
S2 = ConvertS(a,b,c,d,x);
[x2,rho2,L2,k2] = unmkpp(S2);

x_error = x1-x2
rho_error = rho1-rho2
L_error = L1-L2
k_error = k1-k2

function S = ConvertS(a,b,c,d,x)
% x is a column n-vector with x(1) < ... < x(n)
% a,b,c, and d are column n-1 vectors.
%
% S is the pp-representation of the piecewise cubic whose
% ith local cubic is defined by
%
% a(i) + b(i)*(x-x(i)) + c(i)*(x-x(i))^2 + d(i)*(x-x(i))^2*(x-x(i+1))
%
% Note that the i-th local cubic is also prescribed by
% a(i) + b(i)*(x-x(i)) + (c(i)+d(i)(x(i)-x(i+1))*(x-x(i))^2 + d(i)*(x-x(i))^3

rho = [d c-d.*diff(x) b a];


S = mkpp(x,rho);

P3.3.9

% Problem P3_3_9
%
% Illustrate a 1-knot natural spline.

y = [-1 ; 3 ; -2];
[a,b,c,d] = SmallSpline(y);

zL = linspace(-1,0)';
CL = ((d(1)*zL + c(1)).*zL+b(1)).*zL+a(1);
zR = linspace(0,1)';
CR = ((d(2)*zR + c(2)).*zR+b(2)).*zR+a(2);
plot([zL;zR],[CL;CR],[-1;0;1],y,'o')

function [a,b,c,d] = SmallSpline(y)

% y is 3-vector.
%

% a,b,c,d are column 2-vectors with the property that if


%
% S(x) = a(1) + b(1)x + c(1)x^2 + d(1)x^3 on [-1,0]
% and
% S(x) = a(2) + b(2)x + c(2)x^2 + d(2)x^3 on [0,1]
% then
% (a) S(-1) = y(1), S(0) = y(2), S(1) = y(3),
% (b) S''(-1) = S''(1) = 0
% (c) S, S', and S'' are continuous on [-1,1]
%

rhs = zeros(8,1);

% S(-1) = y(1)
M = [ 1 -1 1 -1 0 0 0 0 ];
rhs(1) = y(1);
% S(0) = y(2)
M = [M ; 1 0 0 0 0 0 0 0];
rhs(2) = y(2);
% S(1) = y(3)
M = [M; 0 0 0 0 1 1 1 1];
rhs(3) = y(3);
% S''(-1) = 0
M = [M; 0 0 2 -6 0 0 0 0];
rhs(4) = 0;

% S''(1) = 0
M = [M; 0 0 0 0 0 0 2 6];
rhs(5) = 0;

% S continuous at x = 0
M = [M; 1 0 0 0 -1 0 0 0 ];
rhs(6) = 0;

% S' continuous at x = 0
M = [M; 0 1 0 0 0 -1 0 0 ];
rhs(7) = 0;

% S'' continuous at x = 0
M = [M; 0 0 1 0 0 0 -1 0 ];
rhs(8) = 0;
u = M\rhs;
a = u([1 5]);
b = u([2 6]);
c = u([3 7]);
d = u([4 8]);

P3.3.10

% Problem P3_3_10
%
% Spline interpolation in the plane.

close all
theta = linspace(0,2*pi)';
xvals = 5*cos(theta);
yvals = 3*sin(theta);

for n = 5:4:13
theta = linspace(0,2*pi,n)';
x = 5*cos(theta);
y = 3*sin(theta);
[xi,yi] = SplineInPlane(x,y,100);
figure
plot(xvals,yvals,xi,yi,x,y,'o')
axis('equal')
pause
end

function [xi,yi] = SplineInPlane(x,y,m)


% x and y are columnn-vectors.
% xi and yi are pp-representations of not-a-knot splines with the
% property that they interpolate x(i) and y(i) respectively
% at their knots.

n = length(x);
d = sqrt( (x(2:n)-x(1:n-1)).^2 + (y(2:n)-y(1:n-1)).^2 );
t = [0; cumsum(d)];
ti = linspace(0,t(n),m)';
xi = spline(t,x,ti);
yi = spline(t,y,ti);

P3.3.11

P3.3.12

P3.3.13

P3.3.14
z
Chapter 4: Problem Solutions
Problems Home

P4.1.1 P4.2.1 P4.3.1 P4.4.1 P4.5.1


P4.1.2 P4.2.2 P4.3.2 P4.4.2 P4.5.2
P4.1.3 P4.2.3 P4.3.3 P4.4.3
P4.1.4 P4.2.4 P4.3.4 P4.4.4
P4.1.5 P4.2.5 P4.3.5 P4.4.5
P4.3.6 P4.4.6
P4.3.7
P4.3.8

P4.1.1
% Problem P4_1_1
%
% Error in the Corrected trapezoidal rule.
% Apply to the function f(x) = x^4.

clc
numI = CorrTrap('MyF411','DMyF411',0,1);
exact = 1/5;
err = abs(numI - exact);

% Use the equation


%
% error = c * (b-a)^5 * 24
%
% to solve for c:
c = err/24;

disp(sprintf('Computed constant = %18.15f',c))


disp(' Actual constant = 1/720')

function numI = CorrTrap(fname,fpname,a,b)


% fname is a string that names a function f(x)
% fpname is a string that names the derivative of f.
% a,b are real numbers
% numI is an approximation to the integral of f(x) from a to b.

fa = feval(fname,a);
fb = feval(fname,b);
Dfa = feval(fpname,a);
Dfb = feval(fpname,b);
h = (b-a);
numI = (h/2)*(fa + fb) + (h^2/12)*(Dfa - Dfb);

function y = MyF411(x)
y = x^4;

function y = DMyF411(x)
y = 4*x^3;

P4.1.2
% Problem P4_1_2
%
% Computing the NC weights via a linear system solve

clc
disp(' m norm(NCweights(m)-MyNCweights(m)')
disp('-------------------------------------')
for m=2:11
disp(sprintf('%2.0f %20.16f',m,norm(NCweights(m)-MyNCweights(m))))
end

function w = MyNCweights(m);
% m a positive integer >= 2.
% w a column m-vector of weights associated with the
% m-point closed Newton-Cotes rule.

A = ones(m,m);
A(2,:) = linspace(0,1,m);
for i=3:m
A(i,:) = A(i-1,:).*A(2,:);
end
rhs = 1./(1:m)';
w = A\rhs;

P4.1.3
% Problem P4_1_3
%
% NCweights(m) is exact for polynomials of degree m.

clc
disp(' m Error in NCweights(m) applied to x^m on [0,1]')
disp('----------------------------------------------')
for m=3:2:11
x = (linspace(0,1,m)').^m;
I = 1/(m+1);
numI = NCweights(m)'*x;
disp(sprintf('%2.0f %20.16f',m,abs(I-numI)))
end

P4.1.4
% Problem P4_1_4
%
% Examines the quality of the Newton-Cotes error bound

global AVAL
AVAL = input('Integrand = 1/(1+a*x). Enter a = ');
clc
disp(sprintf('Integral from 0 to 1 of 1/(1+%2.0f*x)',AVAL))
disp(' ')
disp(' m QNC(m) Error Error Bound')
disp(' ')
for m=2:11
numI = QNC('NCTest',0,1,m);
err = abs(numI-log(AVAL+1)/AVAL);
if 2*floor(m/2) == m;
d = m-1;
else
d = m;
end
DerBound=1;
for k=1:d
DerBound = AVAL*k*DerBound;
end

errBound = NCerror(0,1,m,DerBound);
s = sprintf('%20.16f %10.3e %10.3e',numI,err,errBound);
disp([ sprintf(' %2.0f ',m) s])
end

P4.1.5
% Problem P4_1_5
%
% Examines the quality of the Open Newton-Cotes error bound.
clc
disp('Easy case: Integral from 0 to pi/2 of sin(x)')
disp(' ')
disp('Take DerBound = 1.')
disp(' ')
disp(' m QNCOpen(m) Error Error Bound')
disp(' ')
for m=2:7
numI = QNCopen('sin',0,pi/2,m);
err = abs(numI-1);
errBound = NCOpenError(0,pi/2,m,1);
s = sprintf('%20.16f %10.3e %10.3e',numI,err,errBound);
disp([ sprintf(' %2.0f ',m) s])
end

P4.2.1
% Problem P4_2_1
%
% Error bound for composite NC rules applied to the integral
% of sin(x) from 0 to pi/2.

clc

disp(' m n=1 n=10 n=100')


disp('--------------------------------------')
for m=2:11
e1 = CompNCerror(0,pi/2,m,1,1);
e2 = CompNCerror(0,pi/2,m,1,10);
e3 = CompNCerror(0,pi/2,m,1,100);
disp(sprintf('%3.0f %8.3e %8.3e %8.3e',m,e1,e2,e3))
end

function error = CompNCerror(a,b,m,DerBound,n)


% a,b are real scalars
% m are positive integer with 2<=m<=11
% DerBound is a positive real
% n is apositive integer
%
% error is an upper bound for the error when the composite
% NC(m) rule is applied to integral of f(x) from a to b.
% Assumes that the d+1st derivative of f is bounded by DerBound
% where d = m-1 if m is even and d = m if m is odd.

if 2*floor(m/2)==m
d = m-1;
else
d = m;
end

error = NCerror(a,b,m,DerBound)/n^(d+1);

P4.2.2
% Problem P4_2_2
%
% A more vectorized CompQNC

A = zeros(10,3);
for i=1:10
m = i+1;
for n=1:3
numI = CompQNC('sin',0,pi/2,m,n);
numI1 = CompQNC1('sin',0,pi/2,m,n);
A(i,n) = abs(numI-numI1);
end
end

clc
disp('Difference between CompQNC and CompQNC1.')
disp('Tested on integral from 0 to pi/2 of sin(x).')
disp(' ');
disp(' m n=1 n=2 n=3')
disp('-------------------------------------------')
for i=1:10
disp(sprintf('%2.0f %8.3e %8.3e %8.3e',i+1,A(i,:)))
end

function numI = CompQNC1(fname,a,b,m,n)


% fname is a string that names an available function of the
% form f(x) that is defined on [a,b]. f should
% return a column vector if x is a column vector.
% a,b are real scalars
% m is an integer that satisfies 2 <= m <=11
% n is a positive integer
%
% numI is the the composite m-point Newton-Cotes approximation of the
% integral of f(x) from a to b. The rule is applied on
% each of n equal subintervals of [a,b].

w = CompNCweights(m,n);
x = linspace(a,b,n*(m-1)+1)';
f = feval(fname,x);
numI = (b-a)*w'*f;

function w = CompNCweights(m,n)
% m and n are positive integers with 2<=m<=11.
% The weight vector associated with composite NC(m)
% rule with n equal length subintervals.

w = zeros(n*(m-1)+1,1);
for k=1:n
i=(1+(k-1)*(m-1)):(1+k*(m-1));
w(i) = w(i) + NCweights(m);
end
w = w/n;

P4.2.3
% Problem P4_3_3
%
% Illustrates AdaptQNC for a range of tol values and a range of
% underlying NC rules. Uses the humps function for an integrand
% and displays information about the function evaluations.
%
global FunEvals VecFunEvals
clc
home
for tol = [.01 .001 .0001 .00001]
for m=3:2:9
disp(' ')
disp(sprintf('tol = %8.5f m = %1.0f',tol,m ))
FunEvals = 0;
VecFunEvals = 0;
num = AdaptQNC('SpecHumps',0,1,m,tol);
disp(sprintf(' AdaptQNC: %4.0f %4.0f',FunEvals,VecFunEvals))

FunEvals = 0;
VecFunEvals = 0;
num0 = AdaptQNC1('SpecHumps',0,1,m,tol);
disp(sprintf(' AdaptQNC1: %4.0f %4.0f',FunEvals,VecFunEvals))
end
end

function numI = AdaptQNC1(fname,a,b,m,tol,OldEvals)


%
% fname is a string that names an available function of the
% form f(x) that is defined on [a,b]. f should
% return a column vector if x is a column vector.
% a,b are real scalars
% m is an integer that satisfies 2 <= m <=11
% tol is a positive real
% OldEvals is the value of f at linspace(a,b,m) (optional)
%
% numI is the composite m-point Newton-Cotes approximation of the
% integral of f(x) from a to b, with the abscissae chosen adaptively.

% Compute the integral two ways and estimate the error.


xvals = linspace(a,b,(2*m-1))';
if nargin == 5
OldEvals = feval(fname,xvals(1:2:(2*m-1)));
end
NewEvals = feval(fname,xvals(2:2:(2*m-2)));
fEvals = zeros(2*m-1,1);
fEvals(1:2:(2*m-1)) = OldEvals;
fEvals(2:2:(2*m-2)) = NewEvals;
A1 = (b-a)*NCweights(m)'*fEvals(1:2:(2*m-1));
A2 = ((b-a)/2)*NCweights(m)'*(fEvals(1:m) + fEvals(m:(2*m-1)));

d = 2*floor((m-1)/2)+1;
error = (A2-A1)/(2^(d+1)-1);
if abs(error) <= tol
% A2 is acceptable
numI = A2 + error;
else
% Sum suitably accurate left and right integrals
mid = (a+b)/2;
numI = AdaptQNC1(fname,a,mid,m,tol/2,fEvals(1:m)) + ...
AdaptQNC1(fname,mid,b,m,tol/2,fEvals(m:2*m-1));
end

P4.2.4
% Problem P4_2_4
%
% Illustrate the corrected trapezoid rule.

a = 0;
b = pi/2;
clc
disp('Integral of sin(x) from 0 to pi/2')
disp(' ')
disp(' n Trapezoidal Corrected Trapezoidal')
disp(' Rule Rule')
disp('----------------------------------------------')
for n=[1 2 4 8 16 32 64];
numI = CompQNC('sin',a,b,2,n);
numI1 = CompCorrTrap('sin','cos',a,b,n);
disp(sprintf('%3.0f %15.12f %15.12f',n,numI,numI1))
end

function numI = CompCorrTrap(fname,fpname,a,b,n)


% fname,fpname are strings that name a function f(x) and its derivative.
% a,b are reals
% n is a positive integer
%
% numI is an approximation of the integral of f from a to b.

h = (b-a)/n;
corr = (h^2/12)*(feval(fpname,a) - feval(fpname,b));
numI = CompQNC(fname,a,b,2,n) + corr;

P4.2.5
z

P4.3.1
% Problem P4_3_1
%
% Uses quad and Adapt to estimate the integral of the
% humps function from 0 to 1.

clc

disp(' Tol quad Adapt Quad Time / Adapt Time')


disp('-----------------------------------------------------------------')
for tol = logspace(-2,-5,4)

tic;
Q1 = quad('humps',0,1,tol);
t1 = toc;

tic;
Q2 = Adapt('humps',0,1,tol);
t2 = toc;

disp(sprintf('%7.5f %10.8f %10.8f %8.3f',tol,Q1,Q2,t2/t1))


end

P4.3.2
% Problem P4_3_2
%
% Using the linearity of the quadrature problem.

tol = .001;
a = 0;
b = 1;

[numG,countG] = quad('humps',a,b,tol/2);
[numH,countH] = quad('sin',a,b,tol/2);
numDiff = (numG - numH)/2;
numSum = (numG + numH)/2;
[numGmH,countGmH] = quad('GmH',a,b,tol);
[numGpH,countGpH] = quad('GpH',a,b,tol);
count = countGmH + countGpH;
clc
home
disp('Method 1 computes the integrals of g and h separately and then combines.')
disp('Method 2 computes the integrals of (g-h)/2 and (g+h)/2 directly.')
disp(' ')
disp('g(x) = humps(x) is hard, h(x) = sin(x) = easy.')
disp(' ')
disp(' Idiff Isum g-evals h-evals')
disp('---------------------------------------------------')
disp(sprintf('Method 1: %8.4f %8.4f %4.0f %4.0f',numDiff,numSum,countG,countH))

disp(sprintf('Method 2: %8.4f %8.4f %4.0f %4.0f',numGmH,numGpH,count,count))

P4.3.3
% Problem P4_3_3
%
% Illustrates AdaptQNC for a range of tol values and a range of
% underlying NC rules. Uses the humps function for an integrand
% and displays information about the function evaluations.
%
global FunEvals VecFunEvals
clc
home
for tol = [.01 .001 .0001 .00001]
for m=3:2:9
disp(' ')
disp(sprintf('tol = %8.5f m = %1.0f',tol,m ))
FunEvals = 0;
VecFunEvals = 0;
num = AdaptQNC('SpecHumps',0,1,m,tol);
disp(sprintf(' AdaptQNC: %4.0f %4.0f',FunEvals,VecFunEvals))

FunEvals = 0;
VecFunEvals = 0;
num0 = AdaptQNC1('SpecHumps',0,1,m,tol);
disp(sprintf(' AdaptQNC1: %4.0f %4.0f',FunEvals,VecFunEvals))
end
end

function numI = AdaptQNC1(fname,a,b,m,tol,OldEvals)


%
% fname is a string that names an available function of the
% form f(x) that is defined on [a,b]. f should
% return a column vector if x is a column vector.
% a,b are real scalars
% m is an integer that satisfies 2 <= m <=11
% tol is a positive real
% OldEvals is the value of f at linspace(a,b,m) (optional)
%
% numI is the composite m-point Newton-Cotes approximation of the
% integral of f(x) from a to b, with the abscissae chosen adaptively.

% Compute the integral two ways and estimate the error.


xvals = linspace(a,b,(2*m-1))';
if nargin == 5
OldEvals = feval(fname,xvals(1:2:(2*m-1)));
end
NewEvals = feval(fname,xvals(2:2:(2*m-2)));
fEvals = zeros(2*m-1,1);
fEvals(1:2:(2*m-1)) = OldEvals;
fEvals(2:2:(2*m-2)) = NewEvals;
A1 = (b-a)*NCweights(m)'*fEvals(1:2:(2*m-1));
A2 = ((b-a)/2)*NCweights(m)'*(fEvals(1:m) + fEvals(m:(2*m-1)));

d = 2*floor((m-1)/2)+1;
error = (A2-A1)/(2^(d+1)-1);
if abs(error) <= tol
% A2 is acceptable
numI = A2 + error;
else
% Sum suitably accurate left and right integrals
mid = (a+b)/2;
numI = AdaptQNC1(fname,a,mid,m,tol/2,fEvals(1:m)) + ...
AdaptQNC1(fname,mid,b,m,tol/2,fEvals(m:2*m-1));
end

P4.3.4
z

P4.3.5
z

P4.3.6
(a) Solution:

1. I = Q_n+Ch^2; (by assumption)

2. So, I = Q_2n+Ch^2/4, which can be seen as follows.

Error composite rule = error simple rule/(r^(d+1)), where


r = ratio of points adding in, i.e., r=(2*n)/n=2,
d = m-1 (if m is even), m (if m is odd),
m = 2 for the trapezoid rule, so d=1.
So, r^(d+1)=2^(1+1)=4.

3. Subtracting 2 from 1 yields: abs(Q_n-Q_2n) = 3/4*Ch^2.

4. Solving 3 for Ch^2 yields Ch^2 = 4/3*abs(Q_n-Q_2n).


5. Therefore, abs(I-Q_2n) = Ch^2/4 (from 2)
= 4/3*abs(Q_n-Q_2n)*1/4 (from 4)
= 1/3*abs(Q_n-Q_2n) (simplifying).

% This script computes Q_2^(k+1) efficiently, where k is the


% smallest positive integer so that abs(I-Q_(2^(k+1))) < = tol.

% Establish some values for testing purposes.


a = 0; % left endpoint.
b = 2*pi; % right endpoint.
f = 'sin'; % function name.
tol = 10^(-3); % tolerance.

n=1; % number of points.

% Note: This script assumes the existence of a trapezoid function that


% can integrate a function f from a to b using the composite trapezoid
% rule.

% Compute Q_n and Q_2n initially.


Q_n = trapezoid(f,a,b,n);
Q_2n = trapezoid(f,a,b,2*n);

n=2;
while(1/2*abs(Q_n-Q_2n) > tol) % while abs(I-Q_2n) > tol
n = 2*n;
Q_n = Q_2n; % Take advantage of powers of 2 to avoid an extra trapezoid call.
Q_2n = trapezoid(f,a,b,n);
end;

P4.3.7
z

P4.3.8
z

P4.4.1
% Problem P4_4_1
%
% Gauss Quadrature error bound applied to the integral of
% exp(cx) from a to b. (c>0)
% Em = ((b-a)^(2m+1))/(2m+1) * (m!)^4/((2m)!)^3
a = 0;

c = 1;
Mvals = zeros(1,3);

clc
disp(' b tol=10^-3 tol=10^-6 tol=10^-9 ')
disp('-----------------------------------------')
for b = 1:10
for j = 1:3
tol = .001^j;
m = 1;
E = (b-a)^3*b*exp(c*b)/24;
while E > tol
m = m+1;
E = E*(b-a)^2 * ((2*m-1)/(2*m+1)) * m * (m/((2*m-1)*2*m))^3;
end
Mvals(j) = m;
end
disp(sprintf('%2.0f %2.0f %2.0f %2.0f',b,Mvals))
end

P4.4.2
% Problem P4_4_2
%
% Illustrates composite Gauss-Legendre rules on three different
% integrands.
%
% Show QNC(m,n) errors for integral of sin from 0 to pi/2.

close all
figure
for m = 2:6
% m-point rule used.
err = [];
for n = [1 2 4 8 16]
% n = number of subintervals.
err = [err abs(compQGL('sin',0,pi/2,m,n) -1)+eps];
end
semilogy([1 2 4 8 16],err)
axis([0 20 10^(-17) 10^0])
text(16,err(5),sprintf('m = %1.0f',m))
hold on
end
title('Example 1. QGL(m,n) error for integral of sin from 0 to pi/2')
xlabel('n = number of subintervals.')
ylabel('Error in QGL(m,n)')
% Show QGL(m,n) errors for integral of sqrt from 0 to 1.
figure
for m = 2:6
% m-point rule used.
err = [];
for n = [1 2 4 8 16 ]
% n = number of subintervals.
err = [err abs(compQGL('sqrt',0,1,m,n) - (2/3))+eps];
end
semilogy([1 2 4 8 16],err)
axis([0 20 10^(-6) 10^(-2)])
text(16,err(5),sprintf('m = %1.0f',m))
hold on
end
title('Example 2. QNC(m,n) error for integral of sqrt from 0 to 1')
xlabel('n = number of subintervals.')
ylabel('Error in QGL(m,n)')
% Show QGL(m,n) errors for integral of 1/(1+25x^2) from 0 to 1.
figure
for m=2:6
% m-point rule used.
err = [];
for n = [1 2 4 8 16]
% n = number of subintervals.
err = [err abs(compQGL('Runge',0,1,m,n) - (atan(5)/5))+eps;];
end
semilogy([1 2 4 8 16],err)
axis([0 20 10^(-17) 10^0])
text(16,err(5),sprintf('m = %1.0f',m))
hold on
end
title('Example 3. QGL(m,n) error for integral of 1/(1+25x^2) from 0 to 1')
xlabel('n = number of subintervals.')
ylabel('Error in QGL(m,n)')

function numI = CompQGL(fname,a,b,m,n)


% numI = CompQGL(fname,a,b,m,n)
%
% fname is a string that names an available function of the
% form f(x) that is defined on [a,b]. f should
% return a column vector if x is a column vector.
% a,b are real scalars
% m is an integer that satisfies 2 <= m <=6
% n is a positive integer
%
% numI is the composite m-point Gauss-Legendre approximation of the
% integral of f(x) from a to b. The rule is applied on
% each of n equal subintervals of [a,b].

z = linspace(a,b,n+1);

% The easy approach which involves n calls to f

numI=0;
for i=1:n
numI = numI + QGL(fname,z(i),z(i+1),m);
end

% An approach that involves a single call to f. Uses the fact that


%
% w'*f1 + ... + w'*f2 = [w;...;w]'*[f1;f2;...;fn] where w and the fi are m-vectors.

[w,x] = GLweights(m);
wtilde = []; xtilde = [];
mdpt = (z(1:n)+z(2:n+1))/2; % The midpoints of the intervals.
h_half = .5*(b-a)/n; % Half the subinterval length.
for i=1:n
wtilde = [wtilde;w];
xtilde = [xtilde;mdpt(i)+h_half*x];
end
numI = h_half*(wtilde'*feval(fname,xtilde));

P4.4.3
% Problem P4_4_3
%
% Test generalized SplineQ

clc
disp('Test on integral of x^3 - 3x^2 + 4x - 20 from a to b with spline')
disp('based on linspace(0,5,6).')
disp(' ')
disp(' ')
disp(' a b SplineQ1 Exact')
disp('-----------------------------------')
x = linspace(0,5,6)';
y = x.^3 - 3*x.^2 + 4*x - 20;
a = 0;
b = 5;
numI = SplineQ1(x,y,a,b);
exact = (b^4/4 - b^3 + 2*b^2 -20*b ) - (a^4/4 - a^3 + 2*a^2 -20*a );
disp(sprintf('%4.2f %4.2f %10.6f %10.6f',a,b,numI,exact))
a = 1.5;
b = 5;
numI = SplineQ1(x,y,a,b);
exact = (b^4/4 - b^3 + 2*b^2 -20*b ) - (a^4/4 - a^3 + 2*a^2 -20*a );
disp(sprintf('%4.2f %4.2f %10.6f %10.6f',a,b,numI,exact))
a = 1.5;
b = 3.5;
numI = SplineQ1(x,y,a,b);
exact = (b^4/4 - b^3 + 2*b^2 -20*b ) - (a^4/4 - a^3 + 2*a^2 -20*a );
disp(sprintf('%4.2f %4.2f %10.6f %10.6f',a,b,numI,exact))
a = 2.3;
b = 2.6;
numI = SplineQ1(x,y,a,b);
exact = (b^4/4 - b^3 + 2*b^2 -20*b ) - (a^4/4 - a^3 + 2*a^2 -20*a );
disp(sprintf('%4.2f %4.2f %10.6f %10.6f',a,b,numI,exact))
a = 5;
b = 5;
numI = SplineQ1(x,y,a,b);
exact = (b^4/4 - b^3 + 2*b^2 -20*b ) - (a^4/4 - a^3 + 2*a^2 -20*a );
disp(sprintf('%4.2f %4.2f %10.6f %10.6f',a,b,numI,exact))
a = 3;
b = 4;
numI = SplineQ1(x,y,a,b);
exact = (b^4/4 - b^3 + 2*b^2 -20*b ) - (a^4/4 - a^3 + 2*a^2 -20*a );
disp(sprintf('%4.2f %4.2f %10.6f %10.6f',a,b,numI,exact))
a = 0;
b = .3;
numI = SplineQ1(x,y,a,b);
exact = (b^4/4 - b^3 + 2*b^2 -20*b ) - (a^4/4 - a^3 + 2*a^2 -20*a );
disp(sprintf('%4.2f %4.2f %10.6f %10.6f',a,b,numI,exact))
a = 0;
b = 0;
numI = SplineQ1(x,y,a,b);
exact = (b^4/4 - b^3 + 2*b^2 -20*b ) - (a^4/4 - a^3 + 2*a^2 -20*a );
disp(sprintf('%4.2f %4.2f %10.6f %10.6f',a,b,numI,exact))
a = 2;
b = 2;
numI = SplineQ1(x,y,a,b);
exact = (b^4/4 - b^3 + 2*b^2 -20*b ) - (a^4/4 - a^3 + 2*a^2 -20*a );
disp(sprintf('%4.2f %4.2f %10.6f %10.6f',a,b,numI,exact))

function numI = SplineQ1(x,y,a,b)


% x,y are n-vectors with x(1) < ... < x(n)
% a,b are such that x(1) <= a <= b <= x(n)
%
% numI is the integral from x(1) to x(n) of the not-a-knot spline
% interpolant of (x(i),y(i)), i=1:n

S = spline(x,y);
[x,rho,L,k] = unmkpp(S);
if a==x(L+1)
numI = 0;
return
end
ia = locate(x,a); %x(ia) <= a <= x(ia+1)
ib = locate(x,b); %x(ib) <= b <= x(ib+1)
if ia==ib
% same interval
hb = (b-x(ia));
fb = hb*(((rho(ia,1)*hb/4 + rho(ia,2)/3)*hb + rho(ia,3)/2)*hb + rho(ia,4));
ha = (a-x(ia));
fa = ha*(((rho(ia,1)*ha/4 + rho(ia,2)/3)*ha + rho(ia,3)/2)*ha + rho(ia,4));
numI = fb-fa;
return
end
% Start in the subinterval that houses a:
h1 = (x(ia+1)-x(ia));
f1 = h1*(((rho(ia,1)*h1/4 + rho(ia,2)/3)*h1 + rho(ia,3)/2)*h1 + rho(ia,4));
ha = (a-x(ia));
fa = ha*(((rho(ia,1)*ha/4 + rho(ia,2)/3)*ha + rho(ia,3)/2)*ha + rho(ia,4));
sum = f1-fa;
% Add in the contributions of the intervals in between a and b but not
% including either of those intervals.
for i=(ia+1):(ib-1)
% Add in the integral from x(i) to x(i+1).
h = x(i+1)-x(i);
subI = h*(((rho(i,1)*h/4 + rho(i,2)/3)*h + rho(i,3)/2)*h + rho(i,4));
sum = sum + subI;
end
% Add in the contribution of the rightmost interval that houses b.
hb = b-x(ib);
subI = hb*(((rho(ib,1)*hb/4 + rho(ib,2)/3)*hb + rho(ib,3)/2)*hb + rho(ib,4));
numI = sum + subI;

P4.4.4
z

P4.4.5
z

P4.4.6
z

P4.5.1
% Problem P4_5_1
%
% Scaling tol when adding integrals

a = 0;
b = 2;
tol = .0001;
m=5;
exact = quad8('humps',a,b,tol,.0001);
clc
home
disp('Error bounds are additive with AdaptQNC. Thus, if')
disp('an integral is expressed as the sum of n integrals and')
disp('each of those is computed to within tol, then the best we can')
disp('say about the sum is that it is correct to within n*tol.')
disp(' ')
disp('Illustrate with integral of humps from 0 to 2, tol = .0001 and')
disp('using AdaptQNC with m = 5. Total integral broken into n equal-length')
disp('subintegrals.')
disp(' ')
disp(' Subintegrals Error')
disp('------------------------------------')
for n=2:10
numI = 0;
x = linspace(a,b,n);
for i=1:n-1
d = AdaptQNC('humps',x(i),x(i+1),m,tol);
numI = numI +d;
end
disp(sprintf(' %2.0f %15.9e',n-1,abs(numI-exact)))
end

P4.5.2
% Problem P4_5_2
%
% Compare Static Scheduling and Pool-of-task scheduling.

p = 4;
close all

figure
task_vec = [1000; 100*ones(39,1)];
[tr1,tr2] = Xtime(task_vec,p);
subplot(2,1,1)
plot(tr1)
title(sprintf('Static Assignment. Total Time = %5.0f',length(tr1)))
ylabel('Procs Busy')
xlabel('Time Step')
axis([0 length(tr1) 0 p+1])
subplot(2,1,2)
plot(tr2)
title(sprintf('Pool-of-Task Assignment. Total Time = %5.0f',length(tr2)))
ylabel('Procs Busy')
xlabel('Time Step')
axis([0 length(tr1) 0 p+1])
figure
task_vec = [100*ones(39,1);1000];
[tr1,tr2] = Xtime(task_vec,p);
subplot(2,1,1)
plot(tr1)
title(sprintf('Static Assignment. Total Time = %5.0f',length(tr1)))
ylabel('Procs Busy')
xlabel('Time Step')
axis([0 length(tr1) 0 p+1])
subplot(2,1,2)
plot(tr2)
title(sprintf('Pool-of-Task Assignment. Total Time = %5.0f',length(tr2)))
ylabel('Procs Busy')
xlabel('Time Step')
axis([0 length(tr1) 0 p+1])

function [trace_static,trace_pool] = XTime(task_vec,p)


% ask_vec is a column n-vector of positive integers
% p is the number of processors, a positive integer.
%
% trace_static is a column vector where trace_static(k) is the number of
% processors busy during time step k, where
% k = length(trace_static). Based on static
% wrap mapping assignment of tasks.
%
% trace_pool is a column vector where trace_pool(k) is the number of
% processors busy during time step k, where
% k = length(trace_pool). Based on the pool-of-task
% paradigm.

% Static assignment:

n = length(task_vec);
% How much work is each processor assigned?
proc_vec = zeros(p,1);
for k=1:p
proc_vec(k) = sum(task_vec(k:p:n));
end

t = zeros(max(proc_vec),1);
for k=1:p
t(1:proc_vec(k)) = t(1:proc_vec(k)) + ones(proc_vec(k),1);
end
trace_static = t;

% Pool-of-task assignment:

next = 1;
busy = zeros(p,1);
t = [];
while (next<=n) | any(busy>0)
% Make sure all free processors are given a task if possible.
[z,free] = min(busy);
while (z==0) & (next<=n)
% The first free processor is given a task
busy(free) = task_vec(next);
next = next + 1;
[z,free] = min(busy);
end
t = [t;sum(busy>0)];
busy = busy-1;
end
trace_pool = t;
Chapter 5: Problem Solutions
Problems Home

P5.1.1 P5.2.1 P5.3.1 P5.4.1 P5.5.1


P5.1.2 P5.2.2 P5.3.2 P5.4.2 P5.5.2
P5.1.3 P5.2.3 P5.3.3 P5.4.3
P5.1.4 P5.2.4 P5.4.4
P5.1.5 P5.2.5
P5.2.6
P5.2.7
P5.2.8
P5.2.9
P5.2.10
P5.2.11
P5.2.12
P5.2.13
P5.2.14
P5.2.15
P5.2.16

P5.1.1

% Script P5_1_1
%
% Compare two ways of setting up the Pascal matrix

clc
disp(' n PascalTri PascalTri1')
disp(' Flops Flops ')
disp('------------------------------------')
for n=[5 10 20 40 80]
flops(0);
P = PascalTri(n);
f = flops;
flops(0);
P = PascalTri1(n);
f1 = flops;
disp(sprintf(' %3.0f %6.0f %6.0f',n, f,f1))
end

function P = PascalTri1(n)
% n is a positive integer
% T is nxn lower triangular matrix with T(i,j) =
% the binomial coefficient (i-1)-choose-(j-1)

P = tril(ones(n,n));
for i=3:n
for j=2:i-1
P(i,j) = P(i-1,j-1) + P(i-1,j);
end
end

P5.1.2

% Script P5_1_2
%
% Illustrate Circulant matrix set-up via Toeplitz

clc
v = (1:8)';
C = Circulant(v)

function C = Circulant(v)
% v a column vector
% C is a circulant matrix with first row = v.

C = toeplitz([1; v(length(v):-1:2)],v);

P5.1.3
% Script P5_1_3
%
% Embeddig a Toeplitz matrix into a circulant matrix.

clc
row = [10 20 30 40 50];
col = [10; 200; 300; 400; 500];
T = toeplitz(col,row)
C = EmbedToep(col,row)

function C = EmbedToep(col,row)
% col is a column n-vector
% row is a row n-vector with row(1) = col(1)
%
% C is a circulant matrix of size 2n-1 with the property
% that C(1:n,1:n) = T where T is an n-by-n Toeplitz
% matrix with T(:,1) = col and T(1,:) = row.

n = length(col);
m = 2*n-1;
C = zeros(m,m);
C(1,:) = [row col(n:-1:2)'];
for k=2:m
C(k,:) = [C(k-1,m) C(k-1,1:m-1)];
end

P5.1.4
% Script P5_1_4
%
% Illustrate RandBand

clc
disp('p = 1 and q = 1')
A = RandBand(5,5,1,1)
disp('p = 3 and q = 2')
A = RandBand(9,5,3,2)
disp('p = 1 and q = 3')
A = RandBand(5,6,1,3)

function A = RandBand(m,n,p,q)
% m, n, p, and q are positive integers
% random m-by-n matrix with lower bandwidth p and upper bandwidth q.

A = triu(tril(rand(m,n),q),-p);

P5.1.5

Not Available

P5.2.1
% Script P5_2_1
%
% Checks the Hessenberg matrix-vector product.

clc
n = 10;
A = triu(randn(n,n),-1);
z = randn(n,1);
y = HessMatVec(A,z);
err = y - A*z

function y = HessMatVec(A,z)
% A is m-by-n with A(i,j) = 0, i>j+1, z is n-by-1.
% y = A*z
[n,n] = size(A);
y = z(n)*A(:,n);
for k=1:n-1
y(1:k+1) = y(1:k+1) + A(1:k+1,k)*z(k);
end

P5.2.2

% Script P5_2_2
%
% Checks TriMatVec
clc
m = 5;
n = 3;

A = triu(randn(m,n));
x = randn(n,1);
y = TriMatVec(A,x);
err = y - A*x
A = triu(randn(n,m));
x = randn(m,1);
y = TriMatVec(A,x);
err = y - A*x

function y = TriMatVec(A,x)
% A is an m-by-n upper triangular matrix and x n-by-1.
% y = A*x

[m,n] = size(A);
y = zeros(m,1);
for k=1:n
s = min([k m]);
y(1:s) = y(1:s) + A(1:s,k)*x(k);
end

P5.2.3
% Script P5_2_3
%
% Saxpy matrix multiply with one factor triangular.

clc
disp('Example where B has more columns than rows:')
A = rand(4,4)
B = triu(rand(4,6))
C = MatMatSax1(A,B)
Cexact = A*B

disp(' ')
disp('Example where B has more rows than columns:')
A = rand(6,6)
B = triu(rand(6,4))
C = MatMatSax1(A,B)
Cexact = A*B

function C = MatMatSax1(A,B)
% A is an m-by-p matrix.
% B is a p-by-n matrix (upper triangular).
% C A*B (Saxpy Version)

[m,p] = size(A);
[p,n] = size(B);
C = zeros(m,n);
for j=1:n
% Compute j-th column of C.
for k=1:min([j p])
C(:,j) = C(:,j) + A(:,k)*B(k,j);
end
end

P5.2.4

% Script P5_2_4
%
% Saxpy matrix multiply with both factors triangular.

clc
A = triu(rand(5,5))
B = triu(rand(5,5))
C = MatMatSax2(A,B)
Cexact = A*B

function C = MatMatSax2(A,B)
% A is an n-by-n matrix.
% B is a p-by-n matrix (upper triangular)
% C A*B (Saxpy Version)
%
[n,n] = size(A);
C = zeros(n,n);
for j=1:n
% Compute j-th column of C.
for k=1:j
C(1:j,j) = C(1:j,j) + A(1:j,k)*B(k,j);
end
end

P5.2.5

% Script 5_2_5
%
% Illustrate (Lower Triangular)*(Upper Triangular) products

A = tril(rand(6,6))
B = triu(rand(6,6))
C = MatMatDot1(A,B)
Error = C - A*B

function C = MatMatDot1(A,B)
% A and B are n-by-n lower triangular matrices.
% C = A*B (Dot Product Version)

[n,n] = size(A);
C = zeros(n,n);
for j=1:n
% Compute j-th column of C.
for i=1:n
s = 1:min([i j]);
C(i,j) = A(i,s)*B(s,j);
end
end
P5.2.6

% Script P5_2_6
%
% Illustrate A*(Banded B)

p = 2;
A = rand(6,6)
B = triu(tril(rand(6,6),p),-p)
C = BandMatMatSax(A,B,p)
Error = C - A*B

function C = bandMatMatSax(A,B,p)
% A is an n-by-n matrix.
% B is an n-by-n matrix, bandwidth p
% C = A*B (Saxpy Version)

[n,n] = size(A);
C = zeros(n,n);
for j=1:n
% Compute j-th column of C.
for k=max([1 j-p]):min([j+p n])
C(:,j) = C(:,j) + A(:,k)*B(k,j);
end
end

P5.2.7

% Script P5_2_7
%
% Matrix powers via repeated squaring

clc
k = input('Compute A^k where k = ');
A = randn(5,5);
A = A/norm(A,1);
B = MatPower(A,k);
Error = B - A^k

function B = MatPower(A,k)
% A is an n-by-n matrix and k is a positive integer.
% B = A^k
s = '';
x = k;
while x>=1
if rem(x,2)==0
s = ['0' s];
x = x/2;
else
s = ['1' s];
x = (x-1)/2;
end
end
C = A;
First = 0;
for j=length(s):-1:1
if s(j)=='1'
if First==0
B = C;
First =1;
else
B = B*C;
end
end
C = C*C;
end

P5.2.8
% Script P5_2_8
%
% y = (polynomial in matrix A)*(vector)

clc
v = randn(8,1);
A = randn(8,8);
c = randn(4,1);
y = HornerM(c,A,v);
Error = y - (c(1)*v + c(2)*A*v + c(3)*A*A*v + c(4)*A*A*A*v)

The function HornerM is not provided.

P5.2.9

% Script P5_2_9
%
% Illustrate Krylov matrix set-up

clc
A = magic(4);
B = A(:,3:4);
p = 3
C = Krylov(A,B,p)

function C = Krylov(A,B,p)
% A is n-by-n, B is n-by-t, and p is a positive integer.
% C = [ B AB (A^2)B ... (A^p-1))B]

C = B;
C1 = B;
for k=1:p-1
C1 = A*C1;
C = [ C C1];
end

P5.2.10
% Script P5_2_10
%
% Illustrate row scaling

clc
A = magic(5); A = A(:,1:3)
d = 1:5
B = ScaleRows(A,d)

function B = ScaleRows(A,d)
% A is an m-by-n and d an m-vector
% B = diag(d)*A
[m,n] = size(A);
B = zeros(m,n);
for i=1:m
B(i,:) = d(i)*A(i,:);
end

P5.2.11

% Script P5_2_11
%
% Structured Matrix-vector multiplication

clc
close all

n = 20;
k = 30;
P = SetUp(n-1);
v0 = rand(n,1);
v0 = (v0 + v0(n:-1:1))/2;
V = Forward(P,v0,k);
for j=1:k
plot(V(:,j))
title(sprintf('j = %1d',j))
pause(1)
end
hold off

[The function Forward is not provided.]

P5.2.12
z

P5.2.13

P5.2.14
z

P5.2.15

P5.2.16

P5.3.1

% Script P5_3_1
%
% Integral of SampleF2 over [0,2]x[0,2] for various 2D composite
% QNC(7) rule.

clc
m = 7;
disp(' Subintervals Integral Relative Time')
disp('------------------------------------------------')
for n = [4 8 16 32]
tic;
numI2D = MyCompQNC2D('SampleF2',0,2,0,2,m,n,m,n);
t = toc;
if n==4;
base = t;
time = 1;
else
time = t/base;
end
disp(sprintf(' %7.0f %17.15f %11.1f',n,numI2D,time))
end

The function MyCompQNC2D is not provided.

P5.3.2

% Script P5_3_2
%
% Solving integral equations.

close all
a = 0;
b = 5;
clc
disp('m n Set-up Time ')
disp('-----------------------')
for sigma = [.01 .1]
j = 1;
figure
for m = [3 5]
for n = [5 10]
tic;
Kmat = Kernel(a,b,m,n,sigma);
tfinish = toc;
T = etime(tfinish,tstart);
disp(sprintf('%2.0f %2.0f %5.3f',m,n,T))
x = linspace(a,b,n*(m-1)+1)';
rhs = 1./((x-2).^2 + .1) + 1./((x-4).^2 + .2);
fvals = Kmat\rhs;
z = linspace(a,b,200)';
sz = spline(x,fvals,z);
subplot(2,2,j)
plot(z,sz)
title(sprintf('m=%2.0f, n=%2.0f sigma = %5.2f',m,n,sigma))
j= j+1;
end
end
end

function Kmat = Kernel(a,b,m,n,sigma)


%
% Kmat(i,j) =
[omega,x] = wCompNC(a,b,m,n);
N = length(omega);
v = exp(-((x(1)-x).^2)/sigma);
Kmat = Toeplitz(v);
omega = (b-a)*omega;
for j=1:N
Kmat(:,j) = Kmat(:,j)*omega(j);
end

P5.3.3

P5.4.1

% Script P5_4_1
%
% Compares the ordinary DFT with the FFT.

clc
global w
x = randn(1024,1)+sqrt(-1)*randn(1024,1);
disp(' n DFT Flops FFTRecur Flops FFT Flops');
disp('------------------------------------------------------')
for n = [2 4 8 16 32 64 128 256 ]
flops(0);
y = DFT(x(1:n));
dftFlops = flops;

flops(0);
% Precompute the weight vector.
w = exp(-2*pi*sqrt(-1)/n).^(0:((n/2)-1))';
% Note: there are better ways to do this.
y2 = MyFFTRecur(x(1:n));
recurFlops = flops;
flops(0);
y = fft(x(1:n));
fftFlops = flops;
disp(sprintf(' %4.0f %10.0f %10.0f %10.0f', n,dftFlops,recurFlops,fftFlops))
end

function y = MyFFTRecur(x)
% x is a column vector with power-of-two length.
% y is the DFT of x.

global w % Precomputed weight vector


n = length(x);
if n ==1
y = x;
else
m = n/2;
yT = MyFFTRecur(x(1:2:n));
yB = MyFFTRecur(x(2:2:n));

% The required weight vector is a subvector of the precomputed weight vector.


n_orig = 2*length(w);
s = n_orig/n;
d = w(1:s:length(w));

z = d.*yB;
y = [ yT+z ; yT-z ];
end

P5.4.2
z

P5.4.3

% Problem P5_4_3
%
% Test generalized strassen multiply.

clc
n = input('Enter n:');
nmin = input('Enter nmin:');
A = randn(n,n);
B = randn(n,n);
C = MyStrass(A,B,nmin);
err = norm(C-A*B)

function C = MyStrass(A,B,nmin)
% C = MyStrass(A,B)
%
% A,B are n-by-n matrices, n a power of 2.
% nmin is the regukar matrix multiply threshold, a positive integer.
% C is an n-by-n matrix equal to A*B.
[n,n] = size(A);
if n <= nmin
C = A*B;
else
m = floor(n/2); u = 1:m; v = m+1:2*m;
P1 = MyStrass(A(u,u)+A(v,v),B(u,u)+B(v,v),nmin);
P2 = MyStrass(A(v,u)+A(v,v),B(u,u),nmin);
P3 = MyStrass(A(u,u),B(u,v)-B(v,v),nmin);
P4 = MyStrass(A(v,v),B(v,u)-B(u,u),nmin);
P5 = MyStrass(A(u,u)+A(u,v),B(v,v),nmin);
P6 = MyStrass(A(v,u)-A(u,u),B(u,u) + B(u,v),nmin);
P7 = MyStrass(A(u,v)-A(v,v),B(v,u)+B(v,v),nmin);
C = [ P1+P4-P5+P7 P3+P5; P2+P4 P1+P3-P2+P6];

if 2*m < n
% The odd n case.
% Partition A = [A(1:n-1:n-1) u; v alfa]
% Partition B = [B(1:n-1:n-1) w; y alfa]
u = A(1:n-1,n); v = A(n,1:n-1); alfa = A(n,n);
w = B(1:n-1,n); y = B(n,1:n-1); beta = B(n,n);
% Perform the 2-by-2 block multiply using Strassen multiply in the
% (1,1) block.
C = [C+u*y A(1:2*m,1:2*m)*w+beta*u ; v*B(1:2*m,1:2*m)+alfa*y v*w+alfa*beta];
end
end

% Script P5_4_3
%
% Compares compares general-n Strassen method flops with ordinary A*B flops

clc
A0 = rand(128,128);
B0 = rand(128,128);
disp(' n Strass Flops Ordinary Flops')
disp('-------------------------------------')
for n = [15:33 63 64 127 128]
A = A0(1:n,1:n);
B = B0(1:n,1:n);
flops(0);
C = MyStrass(A,B);
f = flops;
disp(sprintf('%3.0f %10.0f %10.0f', n,f,2*n^3))
end

Function MyStrass not provided.

P5.4.4
% Script 5_4_4
%
% Compare Strass and conventional matrix multiply

clc
disp(' q r (Strass Flops)/(Conventional Flops)')
disp('-------------------------------------------------')
for q = 1:20
n = 2^q;
flop_min = StrassCount(n,1);
rmin = 0;
for r=1:q
f = StrassCount(n,2^r);
if f<flop_min
flop_min = f;
rmin = r;
end
end
disp(sprintf(' %2.0f %2.0f %15.2f ',q,rmin,flop_min/(2^(3*q+1))))
end

function f = StrassCount(n,nmin)
% n and nmin are powers of 2 and nmin<=n
% f is the number of flops required to compute an n-by-n matrix multiply
% using Strassen if n>nmin and conventional multiplication otherwise.

if n==nmin
% flops for conventional n-by-n multiply.
f = 2*n^3;
else
m = n/2;
f = 18*m^2 + 7*StrassCount(m,nmin);
end

P5.5.1

Solution not provided.

P5.5.2

% Script P5_5_2
%
% Explores when it pays to do a 2-processor matrix-vector
% multiply.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Time for single processor execution:
% T_seq = (2*n^2)/R;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Let n = 2m and set top = 1:m and bot = (m+1):n.
% Proc(1) sends A(bot,:) to Proc(2)
% T_send_mat = alpha + (n^2/2)*beta;
% Proc(1) computes y(top) = A(top,:)*x and Proc(2) computes
% y(bot) = A(bot,:)*x.
% T_work = (n^2)/R;
% Proc(2) sends y(bot) to Proc(1)
% T_send_vec = alpha + (n/2)*beta;
% Total execution time:
% T_par = 2*alpha + beta*n*(n+1)/2 + (n^2)/R;
% Comparison quotient T_par/T_seq:
% T_ratio = .5*(1+beta*R/2) + (alpha*R/n^2) + (beta*R)/(4*n);
% Thus, if beta*R&lt2 then for sufficiently large n, it pays
% to distribute the computation.

clc
disp('R = 1/beta alpha Minimum n')
disp('-----------------------------------')
for R = [10^5 10^6 10^7 10^8 10^9]
beta = 1/R; %safe by a factor of two
for alpha = [10^(-3) 10^(-4) 10^(-5) 10^(-6)]
%compute the smallest n so that it pays to distribute.
n = ceil((1 + sqrt(1+16*alpha*R))/2);

disp(sprintf(' %6.1e %6.1e %8.0f',R,alpha,n))


end
disp(' ')
end
Chapter 6: Problem Solutions
Problems Home

P6.1.1 P6.2.1 P6.3.1 P6.4.1


P6.1.2 P6.2.2 P6.3.2 P6.4.2
P6.1.3 P6.2.3 P6.3.3 P6.4.3
P6.1.4 P6.2.4 P6.3.4 P6.4.4
P6.1.5 P6.3.5
P6.1.6 P6.3.6
P6.1.7 P6.3.7
P6.3.8
P6.3.9
P6.3.10
P6.3.11

P6.1.1

% Problem P6_1_1
%
% Solving certain singular lower triangular systems.

clc
n = 6;
L = tril(randn(n,n));
L(1,1) = 0
x = LTriSol1(L)
disp('L*x =')
disp(L*x)

function x = LTriSol1(L)
% x = LTriSol1(L)
%
% L is an n-by-n lower triangular matrix. L(2:n,2:n) is
% nonsingular and L(1,1) = 0.
%
% x satisfies Lx = 0 with x(1) = -1.

[n,n] = size(L);
x = zeros(n,1);
x(1) = -1;
x(2:n) = LTriSol(L(2:n,2:n),L(2:n,1));

P6.1.2
% Problem P6_1_2
%
% Solving multiple righthand side upper triangular systems

clc
n = 6;
r = 4;
U = triu(randn(n,n))
B = randn(n,r)
X = UTriSolM(U,B)
disp('U*X - B')
disp(U*X-B)

function X = UTriSolM(U,B)
% X = UTriSolM(U,B)
%
% U is an n-by-n nonsingular upper triangular matrix
% B is am n-by-r matrix.
%
% X satisfies UX = B.

[n,r] = size(B);
X = zeros(n,r);
for j=n:-1:2
X(j,1:r) = B(j,1:r)/U(j,j);
B(1:j-1,1:r) = B(1:j-1,1:r) - U(1:j-1,j)*X(j,1:r);
end
X(1,1:r) = B(1,1:r)/U(1,1);

P6.1.3

% Problem P6_1_3.
%
% Solving the upper triangular Sylvester equation SX-XT=B.

clc
m = 6;
n = 4;
S = triu(randn(m,m))
T = triu(randn(n,n))
B = randn(m,n)
X = Sylvester(S,T,B)
disp(sprintf('norm(SX-XT-B)/norm(X) = %8.3e',norm(S*X-X*T-B)/norm(X)))

function X = Sylvester(S,T,B)
% X = Sylvester(S,T,B)
%
% S is an m-by-m upper triangular
% T is an n-by-n upper triangular with no diagonal entries in
% common with S.
% B is an m-by-n matrix.
%
% X is an m-by-n so SX-XT=B.

[m,n] = size(B);
X = zeros(m,n);
for k=1:n
if k==1
v = B(:,k);
else
v = B(:,k) + X(:,1:k-1)*T(1:k-1,k);
end
X(:,k) = UTriSol(S-T(k,k)*eye(m,m),v);
end

P6.1.4
% Problem P6_1_4
%
% Solving the lower triangular Sylvester equation SX-XT=B.

clc
m = 6;
n = 4;
S = tril(randn(m,m))
T = tril(randn(n,n))
B = randn(m,n)
X = Sylvester1(S,T,B)
disp(sprintf('norm(SX-XT-B)/norm(X) = %8.3e',norm(S*X-X*T-B)/norm(X)))

function X = Sylvester1(S,T,B)
% X = Sylvester1(S,T,B)
%
% S is an m-by-m lower triangular.
% T is an n-by-n lower triangular with no diagonal entries in
% common with S.
% B is an m-by-n matrix.
%
% X is an m-by-n so SX-XT=B.
%

[m,n] = size(B);
X = zeros(m,n);
for k=n:-1:1
if k==n
v = B(:,n);
else
v = B(:,k) + X(:,k+1:n)*T(k+1:n,k);
end
X(:,k) = LTriSol(S-T(k,k)*eye(m,m),v);
end

P6.1.5
% Problem P6_1_5
%
% The inverse of upper triangular matrix.

clc
n = 6;
U = triu(randn(n,n))
X = UTriInv(U)
disp(sprintf('norm(U*X - eye(n,n))/norm(X) = %8.3e',norm(U*X - eye(n,n))/norm(X) ));

function X = UTriInv(U)
% X = UTriInv(U)
%
% U is an n-by-n nonsingular upper triangular matrix.
%
% X is the inverse of U.

[n,n] = size(U);
X = zeros(n,n);
for k=1:n
v = zeros(k,1);
v(k) = 1;
X(1:k,k) = UTriSol(U(1:k,1:k),v);
end

P6.1.6

% Problem P6_1_6
%
% Inverse of Forsythe matrix.

n = input('Enter n: ');
A = tril(-ones(n,n) + 2*eye(n,n))
A_inv = eye(n,n);
for j = 1:n
for i=j+1:n
A_inv(i,j) = 2^(i-j-1);
end
end
A_inv = A_inv

P6.1.7

Not available.

P6.2.1

% Problem P6_2_1
%
% Solving lower Hessenberg systems

n = 6;
A = triu(randn(n,n),-1)
b = A'*ones(n,1)
x = HessTrans(A,b)

function x = HessTrans(A,b)
% x = HessTrans(A,b)
%
% A is an n-by-n upper Hessenberg matrix.
% b iS an n-by-1 vector.
%
% x solves A'*x = b.

n = length(b);
[v,U] = HessLU(A);
% (U'L')x = b
y = LTriSol(U',b);
x = UBidiSol(ones(n,1),v(2:n),y);

P6.2.2

% Problem P6_2_2
%
% Compare CubicSpline and CubicSpline1 where the latter exploits the
% tridiagonal nature of the linear system.

clc
disp('Complete Spline')
disp(' ')
disp(' CubicSpline CubicSpline1')
disp(' n flops time flops time')
disp('-----------------------------------------------')
for n = [20 40 80 160 320]
x = linspace(0,pi/2,n)';
y = sin(x);

flops(0)
tic;
[a,b,c,d] = CubicSpline(x,y,1,1,0);
t1 = toc;
f1 = flops;

flops(0)
tic;
[a1,b1,c1,d1] = CubicSpline1(x,y,1,1,0);
t2 = toc;
f2 = flops;
disp(sprintf('%4.0f %6.0f %8.3f %6.0f %8.3f',n,f1,t1,f2,t2))
end
disp(' ')
disp(' ')
disp('Second Derivative Spline')
disp(' ')
disp(' CubicSpline CubicSpline1')
disp(' n flops time flops time')
disp('-----------------------------------------------')
for n = [20 40 80 160 320]
x = linspace(0,pi/2,n)';
y = sin(x);
flops(0)
tic;
[a,b,c,d] = CubicSpline(x,y,2,0,1);
t1 = toc;
f1 = flops;
flops(0)
tic;
[a1,b1,c1,d1] = CubicSpline1(x,y,2,0,1);
t2 = toc;
f2 = flops;

disp(sprintf('%4.0f %6.0f %8.3f %6.0f %8.3f',n,f1,t1,f2,t2))


end

disp(' ')
disp(' ')
disp('Not-a-Knot Spline')
disp(' ')
disp(' CubicSpline CubicSpline1')
disp(' n flops time flops time')
disp('-----------------------------------------------')
for n = [20 40 80 160 320]
x = linspace(0,pi/2,n)';
y = sin(x);
flops(0)
tic;
[a,b,c,d] = CubicSpline(x,y);
t1 = toc;
f1 = flops;
flops(0)
tic;
[a1,b1,c1,d1] = CubicSpline1(x,y);
t2 = toc;
f2 = flops;
disp(sprintf('%4.0f %6.0f %8.3f %6.0f %8.3f',n,f1,t1,f2,t2))
end

function [a,b,c,d] = CubicSpline(x,y,derivative,muL,muR)


% [a,b,c,d] = CubicSpline(x,y,derivative,muL,muR)
% Cubic spline interpolation with prescribed end conditions.
%
% x,y are column n-vectors. It is assumed that n >= 4 and x(1) < ... x(n).
% derivative is an integer (1 or 2) that specifies the order of the endpoint derivatives.
% muL and muR are the endpoint values of this derivative.
%
% a,b,c, and d are column (n-1)-vectors that define the spline S(z). On [x(i),x(i+1)],
%
% S(z) = a(i) + b(i)(z-x(i)) + c(i)(z-x(i))^2 + d(i)(z-x(i))^2(z-x(i+1).
%
% Usage:
% [a,b,c,d] = CubicSpline(x,y,1,muL,muR) S'(x(1)) = muL, S'(x(n)) = muR
% [a,b,c,d] = CubicSpline(x,y,2,muL,muR) S''(x(1)) = muL, S''(x(n)) = muR
% [a,b,c,d] = CubicSpline(x,y) S'''(z) continuous at x(2) and x(n-1)

% First, set up all but the first and last equations that
% define the vector of interior knot slopes s(2:n-1).

n = length(x);
Dx = diff(x);
yp = diff(y) ./ Dx;
T = zeros(n-2,n-2);
r = zeros(n-2,1);
for i=2:n-3
T(i,i) = 2*(Dx(i) + Dx(i+1));
T(i,i-1) = Dx(i+1);
T(i,i+1) = Dx(i);
r(i) = 3*(Dx(i+1)*yp(i) + Dx(i)*yp(i+1));
end

% For each of the 3 cases, finish setting up the linear system,


% solve the system, and set s(1:n) to be the vector of slopes.

if nargin==5
%Derivative information available.
if derivative==1
% End values for S'(z) specified.
T(1,1) = 2*(Dx(1) + Dx(2));
T(1,2) = Dx(1);
r(1) = 3*(Dx(2)*yp(1)+Dx(1)*yp(2)) - Dx(2)*muL;
T(n-2,n-2) = 2*(Dx(n-2)+Dx(n-1));
T(n-2,n-3) = Dx(n-1);
r(n-2) = 3*(Dx(n-1)*yp(n-2) + Dx(n-2)*yp(n-1)) -Dx(n-2)*muR;
s = [muL; T\r; muR];
end

if derivative==2
% End values for S''(z) specified.
T(1,1) = 2*Dx(1) + 1.5*Dx(2);
T(1,2) = Dx(1);
r(1) = 1.5*Dx(2)*yp(1) + 3*Dx(1)*yp(2) + Dx(1)*Dx(2)*muL/4;
T(n-2,n-2) = 1.5*Dx(n-2)+2*Dx(n-1);
T(n-2,n-3) = Dx(n-1);
r(n-2) = 3*Dx(n-1)*yp(n-2) + 1.5*Dx(n-2)*yp(n-1)-Dx(n-1)*Dx(n-2)*muR/4;
stilde = T\r;
s1 = (3*yp(1) - stilde(1) - muL*Dx(1)/2)/2;
sn = (3*yp(n-1) - stilde(n-2) + muR*Dx(n-1)/2)/2;
s = [s1;stilde;sn];
end;
else
% No derivative information. Compute the not-a-knot spline.
q = Dx(1)*Dx(1)/Dx(2);
T(1,1) = 2*Dx(1) +Dx(2) + q;
T(1,2) = Dx(1) + q;
r(1) = Dx(2)*yp(1) + Dx(1)*yp(2)+2*yp(2)*(q+Dx(1));
q = Dx(n-1)*Dx(n-1)/Dx(n-2);
T(n-2,n-2) = 2*Dx(n-1) + Dx(n-2)+q;
T(n-2,n-3) = Dx(n-1)+q;
r(n-2) = Dx(n-1)*yp(n-2) + Dx(n-2)*yp(n-1) +2*yp(n-2)*(Dx(n-1)+q);

[l,u] = TriDiLU(d,e,f);
stilde = UBiDiSol(u,f,LBiDiSol(l,r));

s1 = -stilde(1)+2*yp(1);
s1 = s1 + ((Dx(1)/Dx(2))^2)*(stilde(1)+stilde(2)-2*yp(2));
sn = -stilde(n-2) +2*yp(n-1);
sn = sn+((Dx(n-1)/Dx(n-2))^2)*(stilde(n-3)+stilde(n-2)-2*yp(n-2));
s = [s1;stilde;sn];
end

% Compute the a,b,c,d vectors.

a = y(1:n-1);
b = s(1:n-1);
c = (yp - s(1:n-1)) ./ Dx;
d = (s(2:n) + s(1:n-1) - 2*yp) ./ (Dx.* Dx);

P6.2.3
% Problem P6_2_3
%
% Solve HX-XT=B where H is upper Hessenberg and T is upper triangular.

clc
m = 6;
n = 4;
H = triu(randn(m,m),-1)
T = triu(randn(n,n))
B = randn(m,n)
X = SylvesterH(H,T,B)
disp(sprintf('norm(H*X-X*T-B)/norm(X) = %8.3e ',norm(H*X-X*T-B)/norm(X)))

function X = SylvesterH(H,T,B)
% X = SylvesterH(H,T,B)
%
% S is an m-by-m upper Hessenberg matrix.
% T is an n-by-n upper triangular matrix with the property that HX-XT=B
% is a nonsingular linear system.
% B is an m-by-n matrix.
%
% X is an m-by-n matrix that satisfies HX-XT=B.
%
[m,n] = size(B);
X = zeros(m,n);
for k=1:n
if k==1
r = B(:,k);
else
r = B(:,k) + X(:,1:k-1)*T(1:k-1,k);
end
[v,U] = HessLU(H-T(k,k)*eye(m,m));
y = LBiDiSol(v,r);
X(:,k) = UTriSol(U,y);
end

P6.2.4

% Clear command window to start.


clc;

% Create W.
d=[1 2 3 4 5 6];
e=[3 5 0 5 0 5];
f=[2 7 2 7 2 8];

% Create b.
b=[7 7 7 7 7 7]';
% Create alpha, beta.
alpha = 2;
beta = 3;

% Solve Tx=b, where T = W+alpha*e_n*e_1'+beta*e_1*e_n';


x = TriCorner(d,e,f,alpha,beta,b);

function x = TriCorner(d,e,f,alpha,beta,b)

% W is a tridiagonal matrix with d encoding the main diagonal,


% f encoding the super diagonal, and e encoding the subdiagonal.
%
% T = tridiagonal + corners.
% T = W+alpha*e_n*e_1'+beta*e_1*e_n',
% where e_1, e_n = first, last cols. of identity matrix.
%
% Goal: Find x such that Tx = b. Do this efficiently.
% Note: x = z-Y*[1+Y(1,1) Y(1,2); Y(n,1) 1+Y(n,2)]^(-1)*[z1; zn]
% where z = W^(-1)*b, Y = W^(-1)*[alpha*e_n beta*e_1].
%
%
% Solution:

% Initialization:
n = length(b);
e_1 = zeros(n,1); e_1(1) = 1;
e_n = zeros(n,1); e_n(n) = 1;

% Compute LU factorization of W:
[l,u] = TriDiLU(d,e,f); % See p. 217 for TriDiLU.m.

% Solve Wz = b by noting LUz = b.


% First step is to solve Ly=b. Second step is to solve Uz=y.
% Note L = lower bidiagonal, U = upper bidiagonal, since W = tridiagonal.
y = LBiDiSol(l,b); % See p.217 for LBiDiSol.m.
z = UBiDiSol(u,f,y); % See p.218 for UBiDiSol.m.

% Solve for y1, y2 (columns of Y). First we're solving Wy1 = alpha*e_n.
% Second we're solving Wy2 = beta*e_1.

% Use the same approach as above. Repeat linear system solves twice.
t = LBiDiSol(l,alpha*e_n);
y1 = UBiDiSol(u,f,t);

t = LBiDiSol(l,beta*e_1);
y2 = UBiDiSol(u,f,t);

% Assemble Y.
Y = [y1 y2];

% Let V = [1+Y(1,1) Y(1,2); Y(n,1) 1+Y(n,2)]^(-1).


% Compute V efficiently.
% Compute determinant.
delta = 1/((1+Y(1,1))*(1+Y(n,2))-(Y(1,2))*(Y(n,1)));
V(1,1) = 1+Y(n,2);
V(1,2) = -Y(1,2);
V(2,1) = -Y(n,1);
V(2,2) = 1+Y(1,1);
V = delta*V;

% Let s = [z(1); z(n)].


s=[z(1); z(n)];

% Final computations.
x=z-Y*V*s;

% Check!
W = diag(e(2:n),-1) + diag(d) + diag(f(1:n-1),1);
T = W+alpha*e_n*e_1'+beta*e_1*e_n';
difference = norm(T*x-b);
disp(['The norm of the difference is ' num2str(difference) '.']);

P6.3.1

% Problem P6_3_1
%
% Multiple righthand side solver.

clc
n = 6;
r = 4;
A = randn(n,n)
B = randn(n,r)
X = MultRhs(A,B)
disp(sprintf('norm(AX-B)/(norm(A)*norm(X)) = %8.3e',norm(A*X-B)/(norm(A)*norm(X))))

function X = MultRhs(A,B)
% X = MultRhs(A,B)
%
% A is an n-by-n nonsingular matrix.
% B is an n-by-r matrix.
%
% X is an n-by-r matrix that satisfies AX = B.
[n,r] = size(B);
X = zeros(n,r);
[L,U,piv] = GEpiv(A);
for k=1:r
y = LTriSol(L,B(piv,k));
X(:,k) = UTriSol(U,y);
end

P6.3.2

% Problem P6_3_2
%
% Illustrates computation of a 5-by-5 LU factorization
% with parameterized pivoting.
clc
format short
disp('Steps through Gaussian elimination of a 5-by-5')
disp('matrix showing pivoting.')
alfa = input('Enter alfa (0 < alfa <= 1): ');
input('Strike Any Key to Continue.');
clc
n = 7;
A = magic(n);
[n,n] = size(A);
L = eye(n,n);
piv = 1:n;
for k=1:n-1
clc
[maxv,r] = max(abs(A(k+1:n,k)));
if abs(A(k,k)) >= alfa*maxv
% No Interchange
q = k;
else
q = r+k;
end
Before = A
disp(sprintf('piv = [ %1.0f %1.0f %1.0f %1.0f %1.0f %1.0f %1.0f %1.0f ]',piv))
disp(' ')
disp(sprintf('Interchange rows k = %1.0f and q = %1.0f',k,q))
piv([k q]) = piv([q k]);
A([k q],:) = A([q k],:);
After = A
disp(sprintf('piv = [ %1.0f %1.0f %1.0f %1.0f %1.0f %1.0f %1.0f %1.0f ]',piv))
disp(' ')
disp(sprintf('Zero A(%1.0f:%1.0f,%1.0f):',k,k+1,k))
input('Strike Any Key to Continue.');
clc
if A(k,k) ~= 0
L(k+1:n,k) = A(k+1:n,k)/A(k,k);
A(k+1:n,k+1:n) = A(k+1:n,k+1:n) - L(k+1:n,k)*A(k,k+1:n);
A(k+1:n,k) = zeros(n-k,1);
end
end
clc
L=L
U = A
piv = piv

P6.3.3
% Problem P6_3_3
%
% Computing a specified entry of A^-1.

clc
n = 6;
A = hilb(n)
X = zeros(n,n);
for i=1:n
for j=1:n
X(i,j) = Ainv1(A,i,j);
end
end
disp('Computed inverse using Ainv:')
disp(' ' )
for i=1:n
disp(sprintf(' %10.0f %10.0f %10.0f %10.0f %10.0f %10.0f',X(i,:)))
end
Xexact = invhilb(n)

function z = Ainv(A,i,j)
% z = Ainv(A,i,j)
%
% A is an n-by-n nonsingular matrix.
% i,j integers with 1<=i<=n and 1<=j<=n.
%
% z is the (i,j) entry of A^-1.

[n,n] = size(A);
[L,U,piv] = GEpiv(A);
b = zeros(n,1);
b(j) = 1;
y = LTriSol(L,b(piv));
% Just need the i-th component of the solution to Ux = y.
xtilde = UTriSol(U(i:n,i:n),y(i:n));
z = xtilde(1);

P6.3.4

% Problem P6_3_4
%
% Block back substitution, the 2-by-2 case.

clc
n = 6;
A = randn(n,n);
B = randn(n,n);
C = randn(n,n);
g = randn(n,1);
h = randn(n,1);
[y,z] = BlockSolve(A,B,C,g,h);
xtilde = [y;z];
x = [A B; zeros(n,n) C]\[g;h];
disp(' BlockSolve Standard Solve')
disp('---------------------------------------------------')
for k=1:2*n
disp(sprintf(' %23.15f %23.15f',xtilde(k),x(k)))
end

function [y,z] = BlockSolve(A,B,C,g,h)


% [y,z] = BlockSolve(A,B,C,g,h)
%
% A,B,C are n-by-n matrices with A and C nonsingular
% g,h are column n-vectors
%
% y,z are column n-vectors so Ay+Bz = g and Cz = h.
z = C\h;
y = A\(g-B*z);

P6.3.5

% Problem P6_3_5
%
% Examines the solution to ABCx = f.

clc
disp('f1 = ProdSolve flops')
disp('f2 = Multiply and Solve flops')
disp(' ')
disp(' ')
disp(' n f2/f1 ')
disp('-----------------')
for n = [10 20 40 80 160]
A = randn(n,n);
B = randn(n,n);
C = randn(n,n);
f = randn(n,1);
flops(0);
x = ProdSolve(A,B,C,f);
f1 = flops;
flops(0)
M = A*B*C;
[LM,UM,pivM] = GEpiv(M);
y = LTriSol(LM,f(pivM));
xtilde = UTriSol(UM,y);
f2 = flops;
disp(sprintf('%4.0f %8.3f',n,f2/f1))
end

function x = ProdSolve(A,B,C,f)
% x = ProdSolve(A,B,C,f)
%
% A,B,C are n-by-n nonsingular matrices
% f is a column n-vector
%
% x is a columnn-vector so ABCx = f
%
[LA,UA,pivA] = GEpiv(A);
yA = Ltrisol(LA,f(pivA));
xA = UtriSol(UA,yA);
[LB,UB,pivB] = GEpiv(B);
yB = Ltrisol(LA,xA(pivB));
xB = UtriSol(UA,yB);
[LC,UC,pivC] = GEpiv(C);
yC = Ltrisol(LA,xB(pivC));
x = UtriSol(UC,yC);

P6.3.6
Not Available
P6.3.7
Not Available

P6.3.8

% Problem P6_3_8
%
% Intersection in 3-space.

P = [1;0;0];
Q = [0;1;1];
R = [1;1;1];
S = [1;0;1];

t = Intersect1(P,Q,R,S)

function t = Intersect1(P,Q,R,S)
% t = Intersect(P,Q,R,S)
%
% P, Q, R, and S are column 3-vectors.
% t is a scalar so that P + t*Q is a linear combination
% or R and S.

% This means P + t*Q = x(1)*R + x(2)*S for some pair of scalars


% x(1) and x(2). In other words,
%
% P = [R S -Q]*[x(1);x(2);t]

A = [R S -Q]
x = A\P;
t = x(3);

P6.3.9
Not Available

P6.3.10
Not Available

P6.3.11

Not Available

P6.4.1
% Problem P6_4_1
%
% Explore relative error in computed x.

clc

del = .0000002;
A = [ 981 726 ; 529 del+726*529/981]
disp(sprintf('cond(A) = %10.3e',cond(A)))
disp(' ')
xexact = [ 1.234567890123456; .0000123456789012];
b = A*xexact;
x = A\b;
disp(sprintf('xexact = %20.16f %20.16f',xexact))
disp(sprintf('x = %20.16f %20.16f',x))

P6.4.2

% Problem P6_4_2
%
% Explore computed relative error.

clc
xexact = [.1234567890123456 ; 1234.567890123456];
for OurEps = logspace(-5,-16,12)
% Simulate a computed x so norm(x - xexact)/norm(xexact) is about OurEps*cond
x = xexact + rand(2,1)*10^4*OurEps*norm(xexact);
disp(' ')
disp(sprintf('OurEps = %8.3e',OurEps))
disp(' ')
disp(sprintf('xexact = %20.16f %20.16f',xexact))
disp(sprintf('x = %20.16f %20.16f',x))
end

P6.4.3

Not Available

P6.4.4

Not Available
Chapter 7: Problem Solutions
Problems Home

P7.1.1 P7.2.1 P7.3.1 P7.4.1


P7.1.2 P7.2.2 P7.3.2 P7.4.2
P7.1.3 P7.2.3 P7.3.3 P7.4.3
P7.1.4 P7.2.4 P7.3.4
P7.1.5 P7.2.5 P7.3.5
P7.2.6 P7.3.6

P7.1.1

% Problem P7_1_1
%
% Examine minimization in different norms.

close all
n = 100;
b = sort(rand(3,1));
b = b(3:-1:1);
A = [1;1;1];
x = linspace(-1,1,n);
v1 = zeros(n,1);
v2 = zeros(n,1);
vinf = zeros(n,1);
for k=1:n
v1(k) = norm(A*x(k)-b,1);
v2(k) = norm(A*x(k)-b,2);
vinf(k) = norm(A*x(k)-b,'inf');
end
subplot(2,2,1)
plot(x,v1,[b(2),b(2)],[0,6])
title(sprintf('1-norm: min at %5.3f',b(2)))
subplot(2,2,2)
plot(x,v2,[sum(b)/3, sum(b)/3],[0,6])
title(sprintf('2-norm: min at %5.3f',sum(b)/3))
subplot(2,2,3)
plot(x,vinf,[(b(1)+b(3))/2,(b(1)+b(3))/2],[0,6])
title(sprintf('inf-norm: min at %5.3f',(b(1)+b(3))/2))

P7.1.2
% Problem P7_1_2
%
% Quadratic LS fits to the sine function.
close all
for m=[3 6 12 24]
[a,b,c] = LSFit(0,pi/3,'tan',m);
x = linspace(0,pi/3)';
y = tan(x);
qvals = (c*x + b).*x + a;
figure
plot(x,y,x,qvals)
title(sprintf('m = %2.0f',m))
end

function [a,b,c] = LSFit(L,R,fname,m)


% [a,b,c] = LSFit(L,R,fname,m)
%
% L,R satisfy L= 3.
%
% a,b,c are scalars so that if q(x) =ax^2 + bx +c, then
% sum from k=1 to m of [q(x(k)) -f(x(k))]^2
% is minimized where x = linspace(L.R,m).

x = linspace(L,R,m)';
z = [ones(m,1) x x.^2]\feval(fname,x);
a = z(1);
b = z(2);
c = z(3);

P7.1.3
Not Available

P7.1.4
Not Available

P7.1.5
% Problem P7_1_5
%
% Assume A (nxn) is given and b, c, and d are all given
% column n-vectors. We want to choose scalars alfa and
% beta so the solution to Ax = b + alfa*c + beta*d is
% as small as possible. Note that the solution is
%
% x = u + alfa*v + beta*w = u + [v w]*[alfa;beta]
%
% where Au = b, Av = c, and Aw = d.

% Solve for u, v, and w...

[L,U,P] = lu(A);
u = U\(L\(P*b));
v = U\(L\(P*c));
w = U\(L\(P*d));

% The act of minimizing norm(u + [v w]*[alfa;beta]) is the


% act of solving the LS problem min norm(Cz - r) where
% C is the n-by-2 matrix [v w], r = -u, and setting
% alfa = z(1) and beta = z(2).

z = -[v w]\u;
alfa = z(1);
beta = z(2);

P7.2.1
% Problem P7_2_1
%
% Solving Hessenberg systems via the QR factorization.

clc
n = 6;
A = triu(randn(n,n),-1)
b = randn(n,1)
x = HessQRSolve(A,b)
disp('A*x-b = ')
disp(A*x - b)

function x = HessQRSolve(H,b)
% x = HessQRSolve(H,b)
%
% H is an n-by-n nonsingular upper Hessenberg.
% b is a column n-vector.
%
% x column n-vector so Hx = b.

[Q,R] = HessQRrot(H); % See P7.2.4

x = UTriSol(R,Q'*b); % Ax = QRx = b means Rx = Q'b.

P7.2.2
% Script P7_2_2
%
% Symmetrizing a 2-by-2 matrix through rotation.
clc
A = randn(2,2)

% To make the (1,2) and (2,1) entries of [c s;-s c]'*A


% the same, we require cA(1,2) - sA(2,2) = sA(1,1)+c(2,1), i.e.,
%
% -s(A(1,1)+A(2,2)) + c(A(1,2)-A(2,1)) = 0
%
% Note that this means that we want the bottom component
% of [c s;-s c][(A(1,1)+A(2,2); A(1,2)-A(2,1)] to be zero.
[c,s] = Rotate(A(1,1)+A(2,2),A(1,2)-A(2,1));
disp('[c s;-s c] =')
disp(' ')
disp([c s;-s c])
disp('[c s; -s c]''*A = ')
disp(' ')
disp([c s;-s c]'*A)

P7.2.3
% Problem P7_2_3
%
% Lower triangularizing a matrix using rotations

clc
n = 5;
A = randn(n,n)
[Q,L] = QLRot(A)
disp('Q''*Q - I = ')
disp(Q'*Q - eye(n,n))
disp('Q''*A - L =')
disp(Q'*A - L)

function [Q,L] = QLrot(A)


% [Q,L] = QLrot(A)
%
% A is an n-by-n matrix.
%
% Q is an n-by-n orthogonal matrix and
% L is an n-by-n lower triangular so A = QL.

[n,n] = size(A);
Q = eye(n,n);
for j=n:-1:2
% Zero A(1:j-1,j)
for i=1:j-1
% Zero A(i,j)
% Note that if [c s;-s c][z;-y] = [r;0], then
% [c s;-s c][y z] = [0;r]. So we can use Rotate to
% zero the top of a 2-vector.

[c,s] = Rotate(A(i+1,j),-A(i,j));
A(i:i+1,1:j) = [c s; -s c]*A(i:i+1,1:j);
Q(:,i:i+1) = Q(:,i:i+1)*[c s; -s c]';
end
end
L = tril(A);

P7.2.4
% Problem P7_2_4
%
% hessenberg QR factorization.

clc
n = 6;
A = triu(randn(n,n),-1)
[Q,R] = HessQRrot(A)
disp('Q''*Q - I = ')
disp(Q'*Q - eye(n,n))
disp('Q''*A - R =')
disp(Q'*A - R)

function [Q,R] = HessQRrot(A)


% [Q,R] = HessQRrot(A)
%
% A is an n-by-n upper Hessenberg matrix.
%
% Q is an n-by-n orthogonal matrix and
% R is an n-by-n upper triangular such that A = QR.

[n,n] = size(A);
Q = eye(n,n);
for j=1:n-1
%Zero A(j+1,j)
[c,s] = Rotate(A(j,j),A(j+1,j));
A(j:j+1,j:n) = [c s; -s c]*A(j:j+1,j:n);
Q(:,j:j+1) = Q(:,j:j+1)*[c s; -s c]';
end
R = triu(A);

P7.2.5
% Problem P7_2_5
%
% Least squares fit of the square root function on [.25,1]

clc
disp(' m a b')
disp('-------------------------------------------------')
for m = [2:10 20 40 80 200]
x = linspace(.25,1,m)';
z = [ones(m,1) x]\sqrt(x);
disp(sprintf('%4.0f %20.16f %20.16f',m,z(1),z(2)))
end

P7.2.6
% Problem P7_2_6
%
% Using a rotation to zero the top component of a 2-vector.

clc
x = randn(2,1)
[c,s] = Rotate1(x(1),x(2));
disp('[c s;-s c]] =')
disp(' ')
disp([c s;-s c])
disp('[c s;-s c]*x =')
disp(' ')
disp([c s;-s c]*x)

function [c,s] = Rotate1(x,y)


% [c,s] = Rotate1(x,y)
%
% x,y are real scalars
%
% c,s satisfy c^2+s^=1 with the property that the top component
% of the vector [c s;-s c][x;y] is zero.

[c,s] = Rotate(-y,x);

P7.3.1
% Problem P7_3_1
%
% Test BVPsolver

a = 0;
b = 1;
close all
for n = [10 100 1000]
uvals = TwoPtBVP(n,a,b,'MyF731p','MyF731q','MyF731r');
figure
plot(linspace(a,b,n)',uvals)
title(sprintf('n = %1.0f',n))
end

function uvals = TwoPtBVP(n,a,b,pname,qname,rname)


% uvals = TwoPtBVP(n,a,b,pname,qname,rname)
%
% a,b are reals with a<b.
% n is an integer >= 3
% pname is a string that names a positive function defined on [a,b].
% qname is a string that names a positive function defined on [a,b].
% rname is a string that names a function defined on [a,b].
%
% uvals is a column n-vector with the property that uvals(i) approximates
% the solution to the 2-point boundary value problem
%
% -D[p(x)Du(x)] + q(x)u(x) = r(x) a<=x<=b
% u(a) = u(b) = 0
%
% at x = a+(i-1)h where h = (b-a)/(n-1)

x = linspace(a,b,n)';
h = (b-a)/(n-1);
p = feval(pname,x(1:n-1)+h/2);
q = feval(qname,x);
r = feval(rname,x);
uvals = zeros(n,1);
e = zeros(n-2,1);
e(2:n-2) = -p(2:n-2);
d = p(1:n-2)+p(2:n-1)+(h^2)*q(2:n-1);
[d,e] = CholTrid(d,e);
rhs = (h^2)*r(2:n-1);
uvals(2:n-1) = CholTridSol(d,e,rhs);

P7.3.2
% Problem P7_3_2
%
% Recursive outer product Cholesky

clc
n = 5;
Gexact = tril(rand(n,n))
A = Gexact*Gexact';
G = CholOuter1(A)

P7.3.3
% Problem P7_3_3
%
% pentadiagonal Cholesky
% Produce plot of timings:
% set up A and b

clc

time = [];
for n = 400:200:2000
% Set up A as instructed:
j = 1:n;
d = 100 + j';
e = 10 + j';
f = j';
% Don't actually form A
% A = diag(d) + diag(e(2:n),1) + diag(e(2:n),-1) + ...
% diag(f(3:n),2) + diag(f(3:n),-2);
% Right-hand side:
b = 100*ones(n,1);
% Record starting time.
tic
% Solve Ax = b
% Factor A = G*G'
% G = diag(g) + diag(h(2:n),-1) + diag(p(3:n),-2);
[g,h,p] = Chol5(d,e,f);
% Solve Gy = b for y
y = LTriSol5(g,h,p,b);
% Solve G'*x = y for x
x = UTriSol5(g,h,p,y);
% Compute elapsed time.
dt = toc;
% Add to table of timings.
time = [time dt];
end
% Make plot of timings.

close all
plot(400:200:2000,time)
xlabel('n')
ylabel('time')
title('Time to solve n-by-n penta-diagonal system')

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Produce table of flops:
clc

disp('n flops');
disp('------------------');
for n = [10 20 40 80 160]
% Set up A as instructed:
j = 1:n;
d = 100 + j';
e = 10 + j';
f = j';
% Set up right-hand side.
b = 100*ones(n,1);
% Reset number of flops.
flops(0)
% Factor A = G * G'
[g,h,p] = Chol5(d,e,f);
% Solve Gy = b for y
y = LTriSol5(g,h,p,b);
% Solve G'*x = y for x
x = UTriSol5(g,h,p,y);
% Print number of flops required.
disp(sprintf('%4.0f %8.0f', n, flops));
end

function x = LTriSol5(g,h,p,b)
% x = LTriSol5(g,h,p,b)
%
% for recitation problem 7.3.3
% Pre:
% g,h,p as in Chol5.m
% b n-vector
%
% Post:
% x such that G*x = b,
% where G = diag(g) + diag(h(2:n),-1) + diag(p(3:n),-2)
n = length(g);
x = zeros(n,1);
% initialize first 2 x values
x(1) = b(1)/g(1);
x(2) = (b(2) - x(1)*h(2)) / g(2);
% get rest of x values (this is similar to 2nd part of
% CholTriDiSol, p.241)
for i = 3:n
x(i) = (b(i) - x(i-2)*p(i) - x(i-1)*h(i)) / g(i);
end

P7.3.4
Not Availabe

P7.3.5
% Problem 7_3_5
%
% We need to solve Ay = b for y and Az = u for z:

G = CholScalar(A);
y1 = LTriSol(G,b); y = UTriSol(G',y1);
z1 = LTriSol(G,u); z = UTriSol(G',z1);

% It follows that

x = y - ((u'*y)/(1+u'*z))*z;

P7.3.6
Not Available.

P7.4.1
% Problem P7_4_1
%
% Illustrate a general block Cholesky

p = [ 2 4 3 1 5];
n = sum(p);
G0 = tril(rand(n,n));
A = G0*G0';
G = MyCholBlock(A,p);
clc
home
disp('The block width vector:')
disp(' ')
s = sprintf('%2.0f ',p);
disp(['[' s ']'])
disp(' ' )
error = norm(G-G0,1)/norm(G0,1)

function G = MyCholBlock(A,p)
% G = MyCholBlock(A,p)
%
% Pre: A is an n-by-n symmetric and positive definite matrix
% and p is a positive integer vector with sum(p) = n
% Post: G is lower triangular and A = G*G'.

[n,n] = size(A);
m = length(p);
finish = cumsum(p);
start = [1 finish(1:m-1)+1];
G = zeros(n,n);
for i=1:m
ivec = start(i):finish(i);
for j=1:i
jvec = start(j):finish(j);
%Find the (i,j) block of G, i.e., G(ivec,jvec).
S = A(ivec,jvec);
for k=1:j-1
kvec = start(k):finish(k);
S = S - G(ivec,kvec)*G(jvec,kvec)'; end
if j<i
for q=1:(finish(i)-start(i)+1)
G(start(i)+q-1,jvec)= (LTriSol(G(jvec,jvec),S(q,:)'))';
end
else
G(ivec,ivec) = CholScalar(S);
end
end
end

P7.4.2
% Problem P7_4_2
%
% Explore load balancing in shared memory Cholesky with
% "wrap mapping" of tasks.

n = 100;
clc
disp(' p max/min flops')
disp('---------------------')
for p = [2 4 8 16 32 64]
f = FlopBalance(n,p);
maxf = max(f); minf = min(f);
disp(sprintf(' %3.0f %7.5f',p,maxf/minf))
end

function f = FlopBalance(n,p)
% f = FlopBalance(n,p)
%
% n,p are positive integers with n>p.
%
% f is a column p-vector where f(mu) is the approximate number
% of flops required by the mu-th processor when parallel
% cholesky is implemented with wrap mapping.
f = zeros(p,1);
for mu=1:p
MyCols = mu:p:n;
for k=1:n
if any(MyCols==k)
% Flops associated with column generation.
f(mu) = f(mu) + (n-k)+3;
end
% Flops associated with saxpy updates
f(mu) = f(mu) + 2*(n-k+1)*ceil((n-k)/p);
end
end

P7.4.3
% Script P7_4_3
%
% Explore load balancing in shared memory Cholesky with
% "contiguous mapping" of tasks.

n = 100;
clc
disp(' p max/min flops')
disp('---------------------')
for p = [2 4 8 16 32 64]
f = FlopBalance1(n,p);
maxf = max(f);
minf = min(f);
disp(sprintf(' %3.0f %7.2f',p,maxf/minf))
end
Chapter 8: Problem Solutions
Problems Home

P8.1.1 P8.2.1 P8.3.1 P8.4.1

P8.1.2 P8.2.2 P8.3.2 P8.4.2

P8.1.3 P8.2.3 P8.3.3 P8.4.3

P8.1.4 P8.2.4 P8.3.4 P8.4.4

P8.1.5 P8.2.5 P8.4.5

P8.1.6 P8.2.6 P8.4.6

P8.1.7 P8.2.7 P8.4.7

P8.1.8 P8.2.8 P8.4.8

P8.1.9 P8.2.9

P8.1.10

P8.1.11

P8.1.12

P8.1.13

P8.1.14

P8.1.15

P8.1.16

P8.1.1
% Problem P8_1_1
%
% Plots the error in the function Mysqrt with min max starting
% approximation.

close all

s = 17/48;
t = 2/3;
x = linspace(.25,1)';
y = sqrt(x);
z = s+t*x;
plot(x,y,x,z)
title('sqrt(x) and (17+32x)/48')
Avals = linspace(.25,1,300);
s = zeros(1,300);
for k=1:300
s(k) = MySqrt1(Avals(k));
end
error = abs(s-sqrt(Avals))./sqrt(Avals);
figure
plot(Avals,error+eps*.01)
Title('Relative Error in the Function MySqrt1(A)')
xlabel('A');

function x = Mysqrt1(A)
%
% A is a non-negative real number.
%
% x is the square root of A.

if A==0
x = 0;
else
TwoPower = 1;
m = A;
while m>=1
m = m/4;
TwoPower = 2*TwoPower;
end
while m < .25
m = m*4;
TwoPower = TwoPower/2;
end;
% sqrt(A) = sqrt(m)*TwoPower
x = (17+32*m)/48;
for k=1:4
x = (x + (m/x))/2;
end
x = x*TwoPower;
end

P8.1.2
% Problem P8_1_2
%
% Plots the error in the function Mysqrt with least squares starting
% approximation.

close all
s = 10/27;
t = 88/135;
x = linspace(.25,1)';
y = sqrt(x);
z = s+t*x;
plot(x,y,x,z)
title('sqrt(x) and (50+88x)/135')
Avals = linspace(.25,1,300);
s = zeros(1,300);
for k=1:300
s(k) = MySqrt2(Avals(k));
end
error = abs(s-sqrt(Avals))./sqrt(Avals);
figure
plot(Avals,error+eps*.01)
Title('Relative Error in the Function MySqrt2(A)')
xlabel('A')

function x = Mysqrt2(A)
%
% A is a non-negative real number.
%
% x is the square root of A.

if A==0
x = 0;
else
TwoPower = 1;
m = A;
while m>=1
m = m/4;
TwoPower = 2*TwoPower;
end
while m < .25
m = m*4;
TwoPower = TwoPower/2;
end;
% sqrt(A) = sqrt(m)*TwoPower
x = (50+88*m)/135;
for k=1:4
x = (x + (m/x))/2;
end
x = x*TwoPower;
end

P8.1.3

% Problem P8_1_3
%
% Confirms the error bound (8.1).

close all
x = linspace(.25,1,500)';
y = sqrt(x);
z = (1+2*x)/3;
e = abs(y-z);
plot(x,e)
title(sprintf('Max error = %8.5f',max(e)))

P8.1.4
% Problem P8_1_4
%
% Extension of MySqrt to matrices.
A = [ 0.00 100;...
.01 900;...
1.00 0;...
.000001 40000];
X = MySqrtMat(A);
clc
disp('A = MySqrtMat(A) = ')
for i=1:4
disp(sprintf('%12.6f %12.6f %22.3f %12.3f',A(i,1),A(i,2),X(i,1),X(i,2)))
end
disp(' ')
disp('A random 10-by-7 example with rows 2 and 4 scaled.')
A = rand(10,7);
A(4,:) = .0001*A(4,:);
A(2,:) = 1000*A(2,:)
X = MySqrtMat(A);
Xexact = sqrt(A);
err = abs(X - Xexact)./Xexact

function X = MysqrtMat(A)
%
% A is an m-by-n matrix with non-negative entries.
%
% X(i,j) = sqrt(A(i,j)), i=1:m and j=1:n.

% Initialize arrays. (Make A a vector.)

[r,c] = size(A);
X = zeros(r,c);
xvec = zeros(r*c,1);
avec = zeros(r*c,1);
avec = A(:);
I = find(avec>0);
m = avec(I);
TwoPower = ones(size(m));

% Normalize nonzero entries to [.25,1).


idx = find(m>=1);
while ~isempty(idx)
m(idx) = m(idx)/4;
TwoPower(idx) = 2*TwoPower(idx);
idx = find(m>=1);
end
idx = find(m<.25);
while ~isempty(idx)
m(idx) = 4*m(idx);
TwoPower(idx) = TwoPower(idx)/2;
idx = find(m<.25);
end
% Newton iteration.
xvec(I) = (1 + 2*m)/3;
for k=1:4
xvec(I) = (xvec(I) + m./xvec(I))/2;
end

% Scale the positive square roots and establish the final X array.
xvec(I) = xvec(I).*TwoPower;
X(:) = xvec;

P8.1.5

% Problem P8_1_5
%
% Illustrate a bad bisection implementation
% The midpoint of the initial interval is a root.

clc
x = Bisection1('atan',-1,1,.0000001);
disp(sprintf('Computed root of atan(x) in [-1,1] is %5.3f',x))
disp(sprintf('Exact root of atan(x) in [-1,1] is %5.3f',0))

function root = Bisection1(fname,a,b,delta)


%
% fname is a string that names a continuous function f(x) of
% a single variable.
% a,b define an interval [a,b]
% f is continuous, f(a)f(b) < 0
% delta is a non-negative real.
%
% root is the midpoint of an interval [alpha,beta]
% with the property that f(alpha)f(beta)<=0 and
% |beta-alpha| <= delta+eps*max(|alpha|,|beta|)

fa = feval(fname,a);
fb = feval(fname,b);
if fa*fb > 0
disp('Initial interval is not bracketing.')
return
end
if nargin==3
delta = 0;
end
while abs(a-b) > delta+eps*max(abs(a),abs(b))
mid = (a+b)/2;
fmid = feval(fname,mid);
if fa*fmid<0
% There is a root in [a,mid].
b = mid;
fb = fmid;
else
% There is a root in [mid,b].
a = mid;
fa = fmid;
end
end
root = (a+b)/2;

P8.1.6

% Problem P8_1_6
%
% Illustrate recursive bisection.

clc
x = rBisection('cos',0,pi,.000001);
disp(sprintf('pi = %9.6f',2*x))

function root = rBisection(fname,a,b,delta,fa,fb)


%
% fname is a string that names a continuous function f(x) of
% a single variable.
% a,b define an interval [a,b].
% f is continuous, f(a)f(b) < 0.
% delta non-negative real.
% fa,fb the value of f at a and b (optional).
%
% root is the midpoint of an interval [alpha,beta]
% with the property that f(alpha)f(beta)<=0 and
% |beta-alpha| <= delta+eps*max(|alpha|,|beta|)

if nargin==4
fa = feval(fname,a);
fb = feval(fname,b);
end
if abs(a-b) > delta+eps*max(abs(a),abs(b))
mid = (a+b)/2;
fmid = feval(fname,mid);
if fa*fmid<=0
% There is a root in [a,mid].
b = mid;
fb = fmid;
else
% There is a root in [mid,b].
a = mid;
fa = fmid; end
root = rBisection(fname,a,b,delta,fa,fb);
else
root = (a+b)/2;
end

P8.1.7

% Problem P8_1_7
%
% Roots of real cubics

clc
disp(' b c d Middle Root')
disp('-----------------------------------')
for b = -2:2
for c = -2:2
for d = -2:2
r = MiddleRoot(1,b,c,d);
if ~isempty(r)
disp(sprintf('%3.0f %3.0f %3.0f %20.16f',b,c,d,r))
end
end
end
end

function r = MiddleRoot(a,b,c,d)
%
% a,b,c,d are real scalars.
%
% If ax^3 + bx^2 + cx + d has 3 distinct real roots then
% r is the the middle root. Otherwise, r is the empty matrix.

a1 = 3*a; b1 = 2*b; c1 = c; % Derivative = a1*x^2 + b1*x + c1


discrim = b1^2 - 4*a1*c1;
if (discrim<=0 | a1 == 0)
% Does not have 3 real roots.
r = [];
return
else
L = (-b1 - sqrt(discrim))/(2*a1);
R = (-b1 + sqrt(discrim))/(2*a1);
fL = ((a*L + b)*L + c)*L + d;
fR = ((a*R + b)*R + c)*R + d;
if fL*fR > 0
% Does not have 3 real roots because the critical points are
% on the same side of the x-axis.
r = [];
return
else
% The middle root is in between the two critical x-values.
while abs(L-R) > eps*max(abs(L),abs(R))
mid = (L+R)/2;
fmid = ((a*mid + b)*mid + c)*mid + d;

if fL*fmid<=0
% There is a root in [L,mid].
R = mid;
fR = fmid;
else
% There is a root in [mid,R].
L = mid;
fL = fmid;
end
end
r = (L+R)/2;
end
end

P8.1.8

% Problem P8_1_8
%
% Bisection with an function that requires a quadrature.

tol = 10^(-14);
a = 0; fa = 0;
x = .5; fx = quad8('MyF818',0,x,tol);
b = 1; fb = 1;
while abs(fx-.5)>tol
if fx > .5
% There is a root in [a,x].
b = x;
fb = fx;
else
% There is a root in [x,b].
a = x;
fa = fx;
end
newx = (a+b)/2;
fx = fx + quad8('MyF818',x,newx,tol);
x = newx;
end
clc
disp(sprintf('x = %16.14f',x))

function y = MyF818(x)
y = pi*sin(pi*x/2)/2;

P8.1.9
% Problem P8_1_9
%
% A bisection application with a function that involves
% solving a linear system.

clc

% Playing with MyF819 shows that if alfa = 1 the 2-norm of the


% solution is bigger than 1 and if alfa = 3 then the 2-norm of
% the solution is less than 1.

alfa = Bisection('MyF819',1,3,.000000000001);
disp(sprintf('alfa = %15.12f',alfa))

function nx = MyF819(alfa)
%
% alfa is a real scalar
%
% nx equals x'*x -1 where x satisfies (A+alfaI)x = ones(8,1) and
% A = toeplitz([6 -4 1 0 0 0 0 0]).

x = toeplitz([6+alfa -4 1 0 0 0 0 0])\ones(8,1);
% (There are more efficient ways to solve this linear system.)
nx = x'*x-1;

P8.1.10

Not Available

P8.1.11
Not Available

P8.1.12
Not Available.

P8.1.13

Not Available.

P8.1.14

Not Available.

P8.1.15

Not Available.

P8.1.16

Not Available.

P8.2.1
% Problem P8_2_1
%
% Modified GraphSearch Environment
MyGraphSearch('DistMercEarth',0,1000,50)

function MyGraphSearch(fname,a,b,nfevals)
%
% fname is a string that names a function f(x) that is defined
% on the interval [a,b].
%
% Produces a set of plots of the function f(x). The user
% specifies the x-ranges by mouseclicks. The fourth argument
% s used to determine how the plots are saved. If Save is
% nonzero, then each plot is saved in a separate figure window.
% If Save is zero or if GraphSearch is called with just three
% arguments, then only the final plot is saved. [L,R] is the
% x-range of the final plot. The plots are based on nfevals
% function evaluations.

close all

% Click in the search intervals.


x = linspace(a,b,nfevals);
y = feval(fname,x);
plot(x,y)
AnotherInterval=1;
n=0;
L = min(x);
R = max(x);
d = (R-L)/10;
hold on
while AnotherInterval
xlabel('Enter a point of interest. (Click off x-range to terminate.)')
[x1,y1] = ginput(1);
if (L<=x1) & (x1<=R)
n = n+1;
a(n) = x1-d;
b(n) = x1+d;
else
AnotherInterval = 0;
end
end
hold off

close all
for k=1:n
% Search the kth interval
figure
AnotherPlot = 1;
L = a(k);
R = b(k);
while AnotherPlot
x = linspace(L,R,nfevals);
y = feval(fname,x);
ymin = min(y);
plot(x,y,[L R],[ymin ymin])
title(['The Function ' fname])
v = axis;
v(1) = L;
v(2) = R;
v(3) = v(3)-(v(4)-v(3))/10;
axis(v)
xlabel('Enter New Left Endpoint. (Click off x-range to terminate.)')
text(R,ymin,[' ' num2str(ymin)])
[x1,y1] = ginput(1);
if (x1<L) | (R<x1)
AnotherPlot=0;
else
xlabel('Enter New Right Endpoint. (Click off x-range to terminate.)')
[x2,y2] = ginput(1);
if (x2<L) | (R<x2)
AnotherPlot=0;
else
L = x1;
R = x2;
end
end
xlabel(' ')
end
end
P8.2.2

% Problem P8_2_2
%
% Max area triangle in ellipse

close all

% The optimum triangle is isosceles. Solution with vertical orientation:

tstar = Golden('MyF822A',pi/2,1.5*pi);
A = -MyF822A(tstar);
tvals = linspace(pi/2,1.5*pi);
Avals = -MyF822A(tvals);
plot(tvals,Avals,tstar,A,'*')
title(sprintf('tstar = %6.3f Area = %6.3f',tstar,A))
figure
theta = linspace(0,2*pi);
x = 2*cos(theta);
y = 3*sin(theta);
plot(x,y,[0 2*cos(tstar) -2*cos(tstar) 0],[3 3*sin(tstar) 3*sin(tstar) 3])
axis square equal

% The optimum triangle is isosceles. Solution with horizontal orientation:

tstar = Golden('MyF822B',pi/2,1.5*pi);
A = -MyF822B(tstar);
tvals = linspace(pi/2,1.5*pi);
Avals = -MyF822B(tvals);
figure
plot(tvals,Avals,tstar,A,'*')
title(sprintf('tstar = %6.3f Area = %6.3f',tstar,A))

figure
plot(x,y,[2 2*cos(tstar) 2*cos(tstar) 2],[0 3*sin(tstar) -3*sin(tstar) 0])
axis square equal

function Area = MyF822A(t)


% t is a real scalar
%
% Area is the negative of the area of the triangle with vertices
%
% (0,3), (2cos(t),3sin(t)) , (-2cos(t),3sin(t))

x = 2*cos(t);
y = 3*sin(t);
base = 2*abs(x);
height = 3-y;
Area = -base.*height/2;
function Area = MyF822B(t)
% t is a real scalar
%
% Area is the negative of the area of the triangle with vertices
%
% (2,0), (2cos(t),3sin(t)) , (2cos(t),-3sin(t))

x = 2*cos(t);
y = 3*sin(t);
base = 2*abs(y);
height = 2-x;
Area = -base.*height/2;

P8.2.3

Not Available.

P8.2.4

% Problem 8_2_4
%
% Fitting a circle to nearly circular data.

% Generate some nearly circular data

randn('state',0); % .This way we all get the same answer.


n = 100;
r_true = 10;
delta = .5;
theta = linspace(0,2*pi,n)';
x = r_true*cos(theta); x_noisey = x + delta*randn(n,1);
y = r_true*sin(theta); y_noisey = y + delta*randn(n,1);

% Compute the "best" fitting circle's radius.

r = fmin('F824',0,max(sqrt(x_noisey.^2+y_noisey.^2)),[1],x_noisey,y_noisey);

xfit = r*cos(linspace(0,2*pi,200))';
yfit = r*sin(linspace(0,2*pi,200))';
plot(xfit,yfit,'r',x_noisey,y_noisey,'o')
axis equal
title(sprintf('Optimum r = %6.3f',F824(r,x_noisey,y_noisey)))

function d = F824(r,x,y)
% r is a scalar and x and y are column n-vectors
%
% d = |sqrt(x(1)^2 + y(1)^2)) - r | + ... + |sqrt(x(n)^2 + y(n)^2)) - r |
%
% Each term is the distance from (x(i),y(i)) to the circle centered at (0,0)
% with radius r.
d = sum(abs(sqrt(x.^2 + y.^2)-r));

P8.2.5

Not Available.

P8.2.6
Not Available.

P8.2.7

Not Available.

P8.2.8

Not Available.

P8.2.9

Not Available.

P8.3.1

% Change the linesearch fragment to this:

% Line Search
% Try to get L<=Lmax so either f(xc+(L/2)*sc) < f(xc) or
% f(xc+L*sc) < f(xc).

L = Lmax;
Halvings = 0;
fL2 = feval(fName,xc+L*sc,plist);
fL1 = feval(fName,xc+(L/2)*sc,plist);
fLmax = fL2;
while (fL1>=fc) & (fL2>=fc) & Halvings<=20
L = L/2;
Halvings = Halvings+1;
fL2 = fL1;
fL1 = feval(fName,xc+(L/2)*sc,plist);
end

% Find the quadratic interpolant


% q(lambda) = a(1) + a(2)lambda + a(3)lambda^2
% of (0,fc), (L/2,fL1), (L,fL2). Use Vandermonde approach.

a = [1 0 0 ; 1 (L/2) (L/2)^2 ; 1 L L^2]\[fc; fL1; fL2];


% Find the lambda that minimizes q on [0,Lmax].
Lcrit = -.5*a(2)/a(3);
if (a(3)>0) & (0<=Lcrit) & (Lcrit<=Lmax)
% There is a local minimum in the interval
lambda = Lcrit;
else
% Check endpoints
if fc < fLmax
lambda=0;
else
lambda=Lmax;
end
end

P8.3.2

Not Available.

P8.3.3

Not Available.

P8.3.4

Not Available.

P8.4.1

Not Available.

P8.4.2
Not Available.

P8.4.3

Not Available.

P8.4.4

Not Available.

P8.4.5

Not Available.
P8.4.6

% Problem 8_4_6
% A nonlinear least squares fitting problem.

clc
randn('seed',10);
disp(' L R a(1) a(2) lambda(1) lambda(2)')
disp('----------------------------------------------------------------')
for s = [0 1 2 20; 2 3 4 21]
L = s(1); R = s(2);
[t,fvals] = GenProb(10,L,R,.001);
lambda_best = FMINS('F846',[0;-1],[],[],t,fvals);
a_best = [exp(lambda_best(1)*t) exp(lambda_best(2)*t)]\fvals;
disp(sprintf('%5.0f %5.0f %10.4f %10.4f %10.4f %10.4f',L,R,a_best,lambda_best))
end

function [t,fvals] = GenProb(m,L,R,noise)


t = linspace(L,R,m)';
fvals = 3*exp(-.2*t) + 5*exp(-4*t) + noise*randn(m,1);

function d = F846(lambda,t,fvals)
% lambda is a column 2-vector and t and fvals are column m-vectors.

% Get the best column 2-vector a given lambda, i.e., fit the
% data as best you can given lambda.

E_lambda = [exp(lambda(1)*t) exp(lambda(2)*t)];


a_best = E_lambda\fvals;

% Compute the norm of the residual.


d = norm(E_lambda*a_best - fvals);

P8.4.7
Not Available.

P8.4.8

Not Avialable.
Chapter 9: Problem Solutions
Problems Home

P9.1.1 P9.2.1 P9.3.1


P9.1.2 P9.2.2 P9.3.2
P9.1.3 P9.2.3 P9.3.3
P9.1.4 P9.2.4
P9.2.5
P9.2.6
P9.2.7

P9.1.1

% Problem P9_1_1
%
% Examine Euler's method

close all
[tvals yvals] = FixedEuler('MyF911',1,1,4,10,.01');
plot(tvals,yvals)

function yp = MyF911(t,y)
yp = -t*y + 1/y^2;

P9.1.2

% Problem P9_1_2
%
% Apply Euler's method to a system

clc
[T, Y] = ODE45('MyF912',linspace(0,1,20), [3;-1;4]);
[m,n] = size(Y);
yat1 = Y(m,n);
error = 1;
n = 10;

disp(' n y(1) error')


disp('--------------------------')
while error >=.001
n = 2*n;
t = linspace(0,1,n+1)';
h = 1/n;
u = [3;-1;4];
for k=1:n
u = u + h*MyF912(t(k),u);
end
error = abs(yat1 - u(3))/abs(yat1);
disp(sprintf(' %4.0f %7.4f %7.4f',n,u(3),error))
end

function up = MyF912(t,u)
x = u(1);
xp = u(2);
y = u(3);
up = zeros(3,1);
up(1) = xp;
up(2) = (3 - sin(t))*xp + x/(1 + y^2);
up(3) = -cos(t)*y - xp/(1+t^2);

P9.1.3

% Problem P9_1_3
%
% Apply Euler's method to a system.

n = 100;
t = linspace(0,3,n+1)';
h = 3/n;
Y = zeros(2,n+1);
Y(:,1) = [2;-1];
for k=1:n
Y(:,k+1) = Y(:,k) + h*MyF913(t(k),Y(:,k));
end
plot(t,Y(1,:),t,Y(2,:))
function yp = MyF913(t,y)
yp = [-1 4; -4 -1]*y;

P9.1.4

Not Available

P9.2.1

% Problem 9_2_1
%
% Examine RKF45 applied to y' = t^d, y(1) = 1 which has solution
% y(t) = (t^(d+1) + d)/(d+1). Note that the d+2nd derivative of the solution
% is zero suggesting that a d+1st order method will be exact.

tinitial = 1;
tfinal = 2;
yinitial = 1;
h = tfinal-tinitial;
global d
clc
disp(' d ynew error znew error')
disp('-------------------------------------')
for d=0:5
yexact = (tfinal^(d+1) + d)/(d+1);
[tnew,ynew,znew] = RKF45step('MyF921',tinitial,yinitial,h);
disp(sprintf('%1.0f %8.6e %8.6e',d,abs(yexact-ynew), abs(yexact-znew)))
end

function yp = MyF921(t,y)
global d
yp = t^d;

P9.2.2

% Problem P9_2_2
%
% Error at t= 1 for fixed stepsize RK applied to y' = -y , y(0) = 1.

clc
fevals = zeros(1,5);
guess = [1000 250 26 8 4];
for k=1:5
n=guess(k);
while n>0
h = 1/n;
tc = 0; yc = 1; fc = -1;
for j=1:n
[tc,yc,fc] = RKStep('f1',tc,yc,fc,h,k);
end
error = abs(exp(-1)-yc);
fevals = k*n;
if k==5
fevals = 6*k;
end
disp(sprintf('k = %1.0f, n = %4.0f, Error = %12.8f, f-evals = %4.0f',k,n,error,fevals))
n = input('Enter zero to quit or another positive choice for n:');
end
end

P9.2.3
% Script 9_2_3
%
% Illustrate ode23 and ode45 on the Apollo orbit problem
global fcount
tinitial = 0;
uinitial = [1.2;0;0;-1.0493575];
tfinal = 6.19216933;
mu = 1/82.45;
close all
clc home
disp(' tol ode23 steps f-evaluations')
disp('----------------------------------------')
for tol = [.01 .001 .0001]
fcount = 0;
[t,u] = Ode23('MyF923',tinitial,tfinal,uinitial,tol);
figure
plot(u(:,1),u(:,3),1-mu,0,'o',-mu,0,'o')
title(sprintf('Ode23 (tol = %6.1e)',tol))
disp(sprintf('%6.1e %5.0f %5.0f',tol,length(t),fcount))
end

disp(' ')
disp(' ')
disp(' tol ode45 steps f-evaluations')
disp('----------------------------------------')
for tol = [.01 .001 .0001 .00001]
fcount = 0;
[t,u] = Ode45('MyF923',tinitial,tfinal,uinitial,tol);
figure
plot(u(:,1),u(:,3),1-mu,0,'o',-mu,0,'o')
title(sprintf('Ode45 (tol = %6.1e)',tol))
disp(sprintf('%6.1e %5.0f %5.0f',tol,length(t),fcount))
end

function up = MyF923(t,u)
%
% t is a real scalar
% u is a column 4-vector
%
% up column 4-vector that reflects the rate of change of u at t.

global fcount
fcount = fcount + 1;
x = u(1);
xp = u(2);
y = u(3);
yp = u(4);
mu = 1/82.5;
mu_star = 1-mu;
r1 = sqrt((x+mu)^2 + y^2);
r2 = sqrt((x-mu_star)^2 + y^2);
up = zeros(4,1);
up(1) = u(2);
up(2) = 2*yp + x - mu_star*(x+mu)/r1^3 - mu*(x-mu_star)/r2^3;
up(3) = u(4);
up(4) = -2*xp + y - mu_star*y/r1^3 - mu*y/r2^3;

P9.2.4

% Problem 9_2_4
% Solve the problem
% y'' = -2y' - 2y + 6 y(0) = 3, y(5) = 3+exp(-5)sin(5)
%
% This has solution y(t) = 3 + exp(-t)sin(t).

% Let u1(t) = y(t) and u2(t) = y'(t) then the problem reformulates as
%
% u1'(t) = u2(t) u1(0) = 3
% u2'(t) = -2u2(t) - 2u1(t) + 6 u1(5) = 3+exp(-5)sin(5)
%
% Our goal is to solve for u1(t). Let g(mu) be the estimate of u1(5) obtained
% by applying ode23 to the IVP

% u1'(t) = u2(t) u1(0) = 3


% u2'(t) = -2u2(t) - 2u1(t) + 6 u2(0) = mu
%
% Use fzero to find a zero of G(mu) = g(mu) - u1(5). (Observe that mu = 1 is a root
% since for the true solution u2(0) = y'(0) = 1.)

% We know that mu = 1 is the correct solution so let's start with an initial


% guess mu = .5.

mu_star = fzero('g924fzero',.5);

% Generate the approximate solution and compare.

tspan = linspace(0,5,21)';
[t,u] = ode23('f924ode',tspan,[3;mu_star]);
tau = linspace(0,5,200)';
plot(tau,3+exp(-tau).*sin(tau),t,u(:,1),'o')
legend('3 + exp(-t)sin(t)','Computed approximation',0)

function ypp = f924(t,y,yp)


% The given "second derivative function" y" = f(t,y,y').

ypp = -2*yp - 2*y + 6;


function up = f924ode(t,u)
%
% up(1) = u(2);
% up(2) = f924(t,u(1),u(2))

up = [u(2); f924(t,u(1),u(2))];

function z = g924(mu)
% z is an estimate of u1(5) where
%
% u1'(t) = u2(t) u1(0) = 3
% u2'(t) = -2u2(t) - 2u1(t) + 6 u2(0) = mu

[t,zvals] = ode23('f924ode',[0 5],[3;mu]);

z = zvals(length(t),1);

function r = g924fzero(mu)
% This will be zero when the initial condition u2(0) = mu
% renders the right value for u1(t) at t = 5.

r = g924(mu) - (3+exp(-5)*sin(5));

P9.2.5
Not Available

P9.2.6

% Problem P9_2_6

randn('state',0)
n = 8;

% Generate an n-by-n positive definite matrix.

X = randn(n,n);
A = X'*X;

% Compute its Cholesky factorization via cholSax from Chapter 6...

G = chol(A); % A = GG', thus to solve Az = b, execute z = G'\(G\b);

% We'll solve y' = Cy, y(0) = ones(n,1) where C is the matrix

C = randn(n,n);
C = C-C';

m = 101;
T = 10;

% Solve the IVP. Get solutions at linspace(0,T,m).

tspan = linspace(0,T,m)';
[t,y] = ode23('F926a',tspan,ones(n,1),[],C);

% Note that y(i,:)' is an approximation of the true solution at t(i).

% Let f(t) = y'(t)*A^{-1}y(t). Compute f(i) = y(t(i)), i=1:m

f = zeros(m,1);
for i=1:m
f(i) = y(i,:)*(G\(G'\y(i,:)'));
end

% Generate the spline interpolant and integrate

S = spline(t,f);
Q = quad('F926b',0,T,[],[],S)

function yp = F926a(t,y,flag,C)
yp = C*y;
function s = F926b(t,S)
s = ppval(S,t);

P9.2.7
Noty Available

P9.3.1

% Problem P9_3_1
%
% Specialize the Adams methods for the case y' = Ay.

close all
global A
A = [ -3 1 -2; -1 -5 3; 1 3 -4];

y0 = [1;1;1];
n = 100;
h = 2/n;
t0 = 0;
% Solve y' = [ -3 1 -2; -1 -5 3; 1 3 -4]y , y(0) = [1;1;1]
% across the interval [0,2] using h = 1/50.
% Exact solution usingthe matrix exponential.
F = expm(h*A);
tvals = linspace(t0,t0+n*h,n+1);
yexact = y0;
for k=1:n
yexact = [yexact F*yexact(:,k)];
end

% The Adams-Bashforth solutions.


for k=1:5
[tvals,yvals] = AFixedAB(A,t0,y0,h,k,n);
figure
E = abs(yexact - yvals');
plot(tvals,E(1,:),tvals,E(2,:),tvals,E(3,:))
title(sprintf('AB%1.0f: Error in the solution components',k))
legend('computed y_{1}(t) error', 'computed y_{2}(t) error','computed y_{3}(t) error')
end

% The Adams-Moulton solutions.


for k=1:5
[tvals,yvals] = AFixedAM(A,t0,y0,h,k,n);
figure
E = abs(yexact - yvals');
plot(tvals,E(1,:),tvals,E(2,:),tvals,E(3,:))
title(sprintf('AM%1.0f: Error in the solution components',k))
legend('computed y_{1}(t) error', 'computed y_{2}(t) error','computed y_{3}(t) error')
end

function [tvals,yvals] = AFixedAB(A,t0,y0,h,k,n)


% [tvals,yvals] = AFixedAB(A,t0,y0,h,k,n)
%
% Let A be a given square matrix.
% Produces an approximate solution to the initial value problem
%
% y'(t) = Ay(t) y(t0) = y0
%
% using a strategy that is based upon a k-th order
% Adams-Bashforth method. Stepsize is fixed.
%
% A = square matrix
% t0 = initial time.
% y0 = initial condition vector.
% h = stepsize.
% k = order of method. (1<=k<=5).
% n = number of steps to be taken,
%
% tvals(j) = t0 + (j-1)h, j=1:n+1
% yvals(j,:) = approximate solution at t = tvals(j), j=1:n+1

tvals = linspace(t0,t0+h*n,n+1);
yvals = zeros(n+1,length(y0));
[tvals(1:k),yvals(1:k,:)] = ABStart('MyF931',t0,y0,h,k);

% Store the solutions as column vectors to make the iteration simpler.

yvals = yvals';
for j=k:n
if k==1
yvals(:,j) = yvals(:,j) + h*(A*yvals(:,j));
elseif k==2
yvals(:,j+1) = yvals(:,j) + (h/2)*(A*(3*yvals(:,j) - yvals(:,j-1)));
elseif k==3
yvals(:,j+1) = yvals(:,j) + (h/12)*(A*(23*yvals(:,j) - 16*yvals(:,j-1) + 5*yvals(:,j-2)));
elseif k==4
yvals(:,j+1) = yvals(:,j) + (h/24)*(A*(55*yvals(:,j) - 59*yvals(:,j-1) + 37*yvals(:,j-2) - 9*yvals(:,j-3)));
else
yvals(:,j+1) = yvals(:,j) + (h/720)*(A*(1901*yvals(:,j) - 2774*yvals(:,j-1) + 2616*yvals(:,j-2) - 1274*yvals(:,j-3) + 251*yv
end
end
yvals = yvals';

function [tvals,yvals] = AFixedAM(A,t0,y0,h,k,n)


% [tvals,yvals] = AFixedAM(A,t0,y0,h,k,n)
%
% Let A be a given square matrix.
% Produces an approximate solution to the initial value problem
%
% y'(t) = Ay(t) y(t0) = y0
%
% using a strategy that is based upon a k-th order
% Adams-Moulton method. Stepsize is fixed.
%
% A = square matrix
% t0 = initial time.
% y0 = initial condition vector.
% h = stepsize.
% k = order of method. (1<=k<=5).
% n = number of steps to be taken,
%
% tvals(j) = t0 + (j-1)h, j=1:n+1
% yvals(:j) = approximate solution at t = tvals(j), j=1:n+1

tvals = linspace(t0,t0+h*n,n+1);
yvals = zeros(n+1,length(y0));
[tvals(1:k),yvals(1:k,:)] = ABStart('MyF931',t0,y0,h,k);

yvals = yvals';

I = eye(size(A));
if k==1
[L,U,P] = lu(I - h*A);
elseif k==2
[L,U,P] = lu(I - (h/2)*A);
elseif k==3
[L,U,P] = lu(I - (5*h/12)*A);
elseif k==4
[L,U,P] = lu(I - (9*h/24)*A);
else
[L,U,P] = lu(I - (251*h/720)*A);
end
for j=k:n
if k==1
rhs = yvals(:,j);
elseif k==2
rhs = yvals(:,j) + (h/2) *(A*yvals(:,j));
elseif k==3
rhs = yvals(:,j) + (h/12) *(A*(8*yvals(:,j) - yvals(:,j-1)));
elseif k==4
rhs = yvals(:,j) + (h/24) *(A*(19*yvals(:,j) - 5*yvals(:,j-1) + yvals(:,j-2)));
else
rhs = yvals(:,j) + (h/720)*(A*(646*yvals(:,j) - 264*yvals(:,j-1) + 106*yvals(:,j-2) -19*yvals(:,j-3)));
end
z = LtriSol(L,P*rhs);
yvals(:,j+1) = UTriSol(U,z);
end

yvals = yvals';

function yp = MyF931(t,y)
global A
yp = A*y;

P9.3.2

Not Available.
P9.3.3
Not Available.

You might also like