CS 101 - Introduction to Computing
Machine Problem 1
Spring 2012
Released: Monday, February 6, 2012
Due Date: Friday, March 2, 2012, 9pm
No Group Work on MPs:
"You may not work in a group as in your lab activities."
Copying code or allowing someone else to copy your code is
cheating. All participants will receive a score of zero. So don't
cheat! You are welcome to talk to the TAs and the professor
during their office hours, or email us and ask questions, but the
TAs should not be asked for help on the MP during lab hours.
Start on this MP as soon as possible after the exam, if not
earlier. The TA will hold office hours on a regular basis, but the
time you will have to wait for the TA's attention generally
increases exponentially as the deadline approaches.
To handin your work follow these
instructions.
1. Problem Statement
You are the engineer of a project that is in charge of building a
suspension bridge over a river. In a nutshell, the bridge consists
of a tower on the left bank, a tower on the right bank, and a
bridge with a number of support (vertical) cables spanning the
river. You are given the heights of the left and right towers. You
are told how many support cables there are going to be across the
bridge and at what distances they are from each other and from the
towers. You therefore also know the total horizontal distance
across the river. You are given the weights that are to be
attached to each cable. You are constrained by the fact that you
have only a certain amount of main (horizontal) cable to work with
(L). Given these constraints, your goal is to calculate the
optimum heights of the support cables so that you can construct
the bridge with exactly the amount of cable that you have with
you, and no more.
Look at the diagram in the next section for a better understanding
of the system.
2. Math
To restate the elements of the system that you are given:
- The heights of the left tower: yL (or y0)
and the right tower: yR (or yn+1)
- The number of support cables: n (n is 5 in the system above)
- The weights suspended from each of the support cables: W = [W1;
W2; W3; .. Wn] = [W1;
W2; W3; W4; W5]
- (W is a column matrix)
- The distances separating the towers and the cables: d = [d1
d2 d3 .. dn dn+1] =
[d1 d2 d3 d4 d5
d6]
- The amount of the main cable available to you: L
You're trying to calculate the heights of the support cables: [y1
y2 y3 .. yn] = [y1 y2
y3 y4 y5]
If you resolve the forces acting on the system at the point
where each support cable (in red) meets the main cable (in blue),
you have a figure that looks like the following:
The weights suspended from the cables exert a force in the
downward direction Wi. Since the system is in a stable
state, we resolve the forces acting along the main cables into
their vertical and horizontal components:


Since all the forces acting in the horizontal direction are
equal, we can call all of them HF.
Also, from geometry (similar triangles):


Equating the forces acting in the vertical direction, we get:


Solving this system of equations, we arrive at the general
equation for the system:
Proceeding from here as in Lab
4 , we arrive at the equation for the system in the matrix
form: Dy = wp ; which is
what we solve in the function find_xy.
3. Preparation
Download the checker here (Right Click
and Save Link As) To run the checker type
mp1_check
at the Matlab prompt and choose the menu options.
Download the demo here (Right Click and
Save Link As) To run the demo type
[x,y] = mp1_demo(15)
at the Matlab prompt. You can choose different values besides 15.
Download mp1_data.m
(Right Click and Save Link As) and save it to your home directory.
You will need to call this script in some of the functions that you
are writing in order that you can access the input data. Please do
not copy all the code from mp1_data.m and paste it into the function
where you need to call it. All you need to do is right click on the
link above, save the file under the same name in your home
directory, and, where required, just insert one line that says
mp1_data
That's it. It's that simple. When you hand in your solution, don't
hand in the file mp1_data.m.
We've given it to you only so that you can try out your code to see
that it works. We won't use the same file to test your code. Insert
all of that code, and incur the wrath of your TA and grader!
The functions that you are going to write are:
- main
- diff_L
- find_xy (will be done in Lab
4)
- xy_length
- final_graphs
Note that these functions call each other a lot. So if you get an
error while running the program, and MATLAB tells you that the
error is at Line p, Column q, in file somefile.m, that needn't
necessarily be the case. You might have to trace backward a little
bit to find out where the error actually originated. Quite often,
the error will be in the someotherfile.m
that just called the function somefile,
where the error was manifested. Sometimes you might have to go
back a couple of steps. If you get totally lost, the best thing to
do is start at the beginning, and go through all of your code,
line by line and check everything.
Also, it goes without saying that the first line in all of your
functions (aside from the comments that you write) must be the
function definition. This isn't mentioned in the explanation of
what goes into the functions, but duh!
It would also be a good idea to go through the entire MP before
you start coding. Write down some ideas in pseudo-code on
paper, or on this sheet if you've printed it out, before you
start.
At the end of some of the functions, there is a small footnote
titled Unit Test. These have instructions for testing that
function in isolation. That means that if you test that function
according to the instructions in that section, and something
doesn't work right, then you should fix that before going on
ahead.
When you begin coding, please note that you MUST put your name and
netid, and a short (one or two lines will do) description of that
function, at the beginning of the function, in the form of a
comment. You must do this for each of the five files that you will
be handing in.
4. Implementation
This is where you come in. You might want to write the functions
in the order that they are explained below, rather than in the
order that they're listed under the Preparation section, because
of the manner in which the functions call each other.
- find_xy
find_xy is the
function that we wrote in Lab 4. Flashback: we created the
function find_xy that
would simulate one run of the algorithm to find the optimum
weights. By calling this function over and over again from
within the function diff_L,
and with the help of the functions you see here, we should be
able to find the optimum heights of the cables such that they
fit our predefined total length.
For a detailed description of the function find_xy, refer to the text
in Lab 4.
Note: You can use the same code that we wrote in Lab 4. However,
if you inserted a plot statement into the function find_xy in
the lab, then take it out. The diff_L function will do
the plotting of the cables for you.
Unit test: If you did Lab 4, then this function should be
fine. But to test it again, if you call it from the MATLAB
prompt like so:
>>[x,y] = find_xy(15)
Then you should get the following values for x and y:
x =
0 5.3931 10.3278 14.6056 20.0877 25.0048 30.1779
y =
30.0000 23.4883 19.8001 18.1607 18.3307 20.4408 25.0000
- xy_length
The purpose of the function xy_length is to calculate
the sum of the distances between the points contained in the two
vectors that are given to it as input. The function takes as its
input two vectors x
and y, and its return
value is a single variable (let's call it total), that
represents the sum of the Euclidean distances between each
consecutive pair of points. Let's look at how we would approach
this.
Given two points with coordinates (x1,y1)
and (x2,y2), the geometric (Euclidean)
distance between them is given by the formula

Therefore there are two things you need to note when you're
writing your function xy_length.
One is that you need to use a function that, starting with the
vectors x and y of x-coordinates and
y-coordinates, gives you the differences between consecutive
values. The second is that you need to take the sum of this
result over all such differences, so that you get the total
length of the cable segments that you would need. (This is
what you will return, and therefore what you will compare with
L in the function diff_L.)
Set the value of the return variable total to be the sum of
the distances between each pair of successive cables (left
tower to leftmost cable, between all the cables and rightmost
cable to right tower).
Unit Test: Calculate the distance between the points
(0,0), (3,4) and (6,8):
>> a =
xy_length([0,3,6] , [0,4,8])
You should get the answer as:
a =
10
- diff_L
The purpose of this function is to compute the difference delta between the target
length L and the
total length of the cables running from the left tower to each
of the support cables, to the right tower. The input argument to
this function is the guesstimate of the horizontal force, HF,
and the return value is delta, the error in your approximation
of the length of cables needed for you bridge.
- Make sure you allow global access to the Data vector. This
function will create this vector by appending values to it
during each iteration. For a detailed description, see 3.g
to understand what the vector Data would look like, and see
4.a to understand why we want it to be global.
- Call the mp1_data.m
file as a script. Though it would work the same, please
don't copy all the code from mp1_data.m and paste it here.
As long as you've downloaded the file to your home
directory, you can run it like a script. It reads in the
input data for you.
- Call the function find_xy
on the horizontal force that you passed in. Each iteration
of find_xy gives you two vectors of coordinates of the
topmost points of the vertical cables (and the towers).
Store them in two vectors; say x and y.
- Use these vectors to call xy_length, to find the sum of
the lengths of the cables between the left tower, the
vertical cables and the right tower. Store the return value
of xy_length in a variable; say c1.
- Use the vectors x and y to make a plot of this iteration.
Use blue solid lines with '*' markers. Since diff_L is
called over and over, you'll see a visualization of the
progression from the initial system based on the estimated
horizontal force to the correct answer based on the amount
of cable you have.
- Since there's no major processing involved in this MP, the
visualization that was talked about in part 3.e will flash
before you too fast for you to make any sense of it. To
avoid this, slow it down by inserting a pause of a tenth of
a second.
- Update the global variable Data that you're using to store
the horizontal force and the calculated length for each of
the iterations. Basically, append (attach at the end) a
vector with two elements HF and c1, to the already existing
global vector Data. When you do the update, make sure that
each new iteration adds data to a new row, and that each row
in the Data vector has the horizontal force and the length
for one iteration. What this does is it takes the Data
vector that existed from the previous iteration, and adds
the data from this iteration to it. This kind of creates a
timeline of horizontal forces and lengths. We'll use this
later to plot how we approached the optimal answer.
The Data vector should therefore be a vector with i rows and 2 columns;
where i is the
number of iterations we perform. The first element in each
row (the entire first column) will contain the horizontal
forces that you calculate at each iteration, and the second
element in each row (the entire second column) will contain
the lengths that you calculate at each iteration.
- We're interested in getting the difference between the
current length that we need for all the cables (c1), and the amount of
cable given to us (L),
down to zero. So subtract c1 from L and make this the return
value delta of the
diff_L function. This delta
represents how far away you are from the final answer.
Unit Test: Call diff_L
with the initial value of 15 like so;
>>b = diff_L(15)
You should get the answer as:
b =
3.0722
While the plot should look like this:

- main
This is the function that you will actually call when you
run the program as a whole, to see if everything is working. The function accepts one
input parameter, HF and it returns two vectors x
and y. The function definition line
should look like this:
function [x,y] = main(HF)
That is, when you type in the following at the command
prompt, the program should run and give you the output you
expect, plus the graphs.
>> [x,y]=main(15)
- You need to define a vector called Data to be global, to archive
the horizontal force and the error in approximation of the
total length of cables required. We're doing this so that at
the end of everything we can plot a graph that tells us how
we converged from our initial guess to the right answer. The
reason we're making this variable global is that we want to
be able to access it from inside a number of
functions, and we don't want to go through the pain of
passing it about as an argument and returning it and
what-not. Just accessing one global structure is more
elegant in this case, and much simpler! To enable a function
to access and perhaps update this vector (in spite of not
having been passed it as an argument), we must explicitly
tell MATLAB that the data structure is global. Every
function that wants to access the Data vector has to do
this. Think of it as adding someone to your Buddy List on an
Instant Messenger like MSN or AIM. Both of you have to add
the other to your list so that you can communicate
seamlessly. Same way, both (or all) functions have to say
that they are associated with the vector Data. The functions
are not communicating with each other in this way, but each
of them is communicating with Data.
Initialize the
vector Data to be an empty vector. (This is just to
start off with; we'll add history data to it as we go
along.)
- Add a command that deletes all the open figure windows.
This won't make any sense the first time you run it of
course, but as you go from iteration to iteration, you want
to minimize the clutter on your desktop.
close all
- Find a function that, when passed the name of another
function somefunc as a string, and an initial guess
someguess, finds a root (or a zero) of somefunc near
someguess, and returns that root. Give this function
diff_L in place of
somefunc, HF
in place of someguess, and store the value returned
in a variable true_HF.
- Call the function final_graphs
to plot your progression from the initial guess to the
optimal solution. Use the command,
handles = final_graphs();
If you want to test everything you've written till this
point, before going on to write final_graphs, you can
do so, you just won't be able to see all of the figures that
you'll need in the end. Just leave this line out for the
time being, and insert it when you have written final_graphs.m.
- Just so that you can present the final answer to the user,
call the function find_xy
with true_HF
as it's argument, and store the values returned in vectors x and y.
- final_graphs
Since we're applying an iterative process to get to
the optimal solution, we'd be interested in knowing how we
approached that solution. That's what this function is going to
do. It does not accept any input parameters and it has no return
values. The function
definition line should look like this:
function handles = final_graphs
- final_graphs is
definitely going to want to access the information in our
global vector Data,
so make Data
visible by declaring it as global here too.
- Open a new figure window.
figure
- Read in the data from mp1_data.m,
the same way as above.
- Split up the vector Data
into two vectors Forces
and Lengths as
follows:
Create a vector Forces
that has all the forces that were in Data. To do this, you
need to access the first element in every row of Data.
Create a vector Lengths
that has all the lengths that were in Data. To do this, you
need to access the second element in every row of Data.
Yes, there is a simple way to do this!
- Plot a graph of Lengths vs. Forces (FYI: When you say 'plot
y vs. x', you mean plot(x,y)
). Use red for the color specification, and a *
for the symbol of the points.Use the command,
handles(1) = plot(...);
- Set hold on.
- Create a vector called True_Length
as follows:
True_Length has one
row, and the number of columns is equal to the size of the Forces vector.
Each element of True_Length
has its value equal to L;
the allowed total length of the cables (which is in mp1_data.m and which
you can access because you read it in.)
- Now plot a graph of True_Length
vs. Forces, using
blue for the color specification, and leave the line
solid (the default setting).
Adjust the axes (use the axis function) so that you can view
the picture in better perspective; make the range of the X
axis from the minimum Force to the maximum Force, and the
range of the Y axis from the minimum Length to the maximum
Length. Use the command.
handles(2) = plot(...);
Set the title of the graph to be 'Lengths vs. Forces'. Label
the X axis as 'Forces'. Label the Y axis as 'Lengths'.
Display a legend for the graph, showing which colors signify
what quantity, and position it inside the axes boundary,
obscuring as few points as possible ( use the legend
function, with 0 as the value of the last argument).
- Open a second figure window, and plot a graph of the
Lengths, in red with a * as the symbol. Use
the command,
handles(3) = plot(...);
Set hold on, and then plot a graph of the Forces, in green
with a * as the symbol. Use the command,
handles(4) = plot(...);
Plot the graph True_Length
in blue, with the default solid line. Use the
command,
handles(5) = plot(...);
Let the title of this graph be 'Length vs. iteration'. Label
the X axis as 'iteration'. Label the Y axis as 'Lengths'.
Display the legend of the graph as describing what each of
the three colors mean, and again position it inside the axes
boundary, obscuring as few points as possible( use the
legend function, with 0 as the value of the last argument).
Your should get the following plots (in addition to the plot
by diff_L) when you run main with the initial guess
of 20 for the horizontal force (HF):


Extra credit (5 points): Improving find_xy using spdiags. It's
All So Empty...
1. Introduction to spdiags and Sparse Matrices
Now we're going to perform a little optimization. Notice that the
coefficient matrix we create to solve the system of equations in
find_xy can have a lot of zeroes if there is a large number of
weights. This can bog down calculations, because MATLAB will spend
the time to use each of those zeroes, even though they don't do
anything, and they also take up space because MATLAB stores them
as part of the matrix. The solution to this is to use something
called a sparse matrix. Sparse matrices store only the
non-zero elements; all other elements are assumed to be zero. This
saves on storage space for matrices with lots of zeroes, and it
also speeds up computation, since MATLAB will simply ignore the
non-existent zeroes in the sparse matrix when doing matrix
multiplication.
To create sparse matrices, we'll use the command "spdiags", which
will replace diag. spdiags is a tad more complicated (but more
powerful) than diag. The format for using spdiags is spdiags(D,
c, m, n), where:
- D: matrix containing the diagonal columns.
- c: "control vector" of diagonal positions. c has as
many elements as D has columns.
- m: number of rows in resulting matrix.
- n: number of columns in resulting matrix.
That is, spdiags will take each column of the D matrix and place
it on the diagonal indicated by the control vector in an m x n
sized matrix. Here's an example of how it works. First, we
construct a matrix with the diagonal columns:
>> D = [1:5; 6:10; 11:15]'
D =
1 6 11
2 7 12
3 8 13
4 9 14
5 10 15
Note the three columns of D. We're going to use the columns as
diagonals in a 5 x 5 matrix:
>> A = spdiags(D, [0 1 -2], 5, 5)
A =
(1,1) 1
(3,1) 11
(1,2) 7
(2,2) 2
(4,2) 12
(2,3) 8
(3,3) 3
(5,3) 13
(3,4) 9
(4,4) 4
(4,5) 10
(5,5) 5
How did the columns of D turn into the diagonals of A? Let's break
it down some more, using the same data as above:

Figure 4.
Given D, c, m, and n, the resulting matrix A from
spdiags is of m x n size. Each column of D is used to create a new
diagonal. The first column is on the main diagonal of A, the
second column is on the first upper diagonal, and the third column
is on the second lower diagonal. How did they get there? Note the
control vector c: 0 for the first value, corresponding to the main
diagonal, 1 for the second value (first upper diagonal), and -2
for the last value (second lower diagonal). Thus, the c vector
controls which diagonal the columns of D appear in A.
But wait, you may (should) be saying. How do the
diagonals get created if the column of D isn't the same size as
the diagonal it's being used for? How did the second column of D
end up on the first upper diagonal of A if the column has five
elements to the diagonal's four? The solution is that MATLAB will
drop values from the D columns to make the diagonal fit. In the
case of upper diagonals, the values are dropped starting from
the top of the column matrix. Thus, the 6 from the second
column of D was discarded, and 7 through 10 were used to make the
diagonal. Similarly, in the case of lower diagonals, values are
dropped starting from the bottom of the column matrix. For
the second lower diagonal, 14 and 15 were dropped, while 11
through 13 were used.
2. Implementation
Now let's put all that newfound knowledge of spdiags
to good use. Create a new function called "find_xy2" and store it
in a file called "find_xy2.m". This function will take the same
parameters as find_xy. You can copy most of the code over from
find_xy. Instead of using multiple "diag" calls to create your
coefficients matrix, now you will use a single call to "spdiags"
to create a sparse version of your matrix. That is, the only
changes that need to be made in your code for find_xy is to change
how you create the matrix A (see section 2 Math) . You will need
to make your own columns matrix D and control vector c, then feed
them to spdiags along with the appropriate values for the matrix
dimensions to get the same coefficients matrix as before, but in
sparse form.
To test your new function by running the mp1_check code option 2.
Do not modify your main
function to call find_xy2. The mp1_check program will test this
code separately.
Handin instructions:
To handin your work follow these
instructions.
Enjoy!