Lab 2 overview:

Contents

Conditional statements: if, else, end

So far we only know how to run one statement after another. All statements in our scripts get run every the time. What if we want some statements to only run under certain conditions? Answer: use a conditional!

The following shows a demo of how "if/else" (conditional) syntax works. The condition will be checked. If it's true, the first block of code will be run. If it's false, the second block of code will be run. Under no circumstances will both blocks be run.

x = 1; % Try changing this value and running the block
if x > 5
    display('a is greater than 5');
else
    display('a is less than or equal to 5');
end
a is less than or equal to 5

What sorts of conditions might we want to check?

x = 10;

x > 0 % is x positive?
x == 2 % is x equal to 2?
mod(x, 2) == 0 % is x divisible by 2? ("mod" is like "remainder")

% These statements output 0 if false, or 1 if true
ans =

     1


ans =

     0


ans =

     1

Check multiple conditions with "elseif"

Each condition will be checked one after another.

As soon as one condition is found to be true, the other conditions will not be checked, even if they are also true. Under no circumstances will more than one block of code be run.

x = 1;
if x > 5
    display('a is greater than 5');
elseif x > 0
    display('a is less than or equal to 5, and greater than 0');
else
    display('a is less than or equal to 0');
end
a is less than or equal to 5, and greater than 0

Brainstorm: When might we want to use a conditional in code?

Repeating code without copy-and-paste

Let's say we want to visualize several vectors:

nVec = 3;
vecLength = 10;

vecs = rand(vecLength, nVec);
% Quick check: are these vectors row vectors or column vectors?
% Notice how the use of descriptive names makes it easier to tell than if we
% had instead written rand(10, 3).

% Display the vectors:
figure; stem(vecs(:, 1));
figure; stem(vecs(:, 2));
figure; stem(vecs(:, 3));
% This is inefficient. What if we had 10 vectors? 100? What if we change
% "nVec" above?

When vectorizing isn't what we want

If you look at the help for "stem" you can see that it will happily make one plot with a new series for each column in a matrix, but what if we really do want separate plots, above, not one big plot?

stem(vecs); % This is nice, but it isn't what we want

For loops

Use a "for loop" to step through different values of a variable. In this example, we step through x = 1, x = 2, ... x = 5. Each time we go through the loop, we run the same block of code with the new x value.

for x = 1:5
    display(x)
end
x =

     1


x =

     2


x =

     3


x =

     4


x =

     5

Using a for loop to solve the "make several plots" problem above

close all;

% Generate vectors
nVec = 3;
vecLength = 10;
vecs = rand(vecLength, nVec);

% Plot vectors
for col = 1:nVec
    figure; stem(vecs(:, col));
end

Style note: good variable names; "magic numbers"; whitespace

close all;
a = rand(10, 3);
for b = 1:3
figure; stem(a(:, b));
end

You can use the "index variable" just to count

Here the variable "i" is used just to count n times through the loop, and we're changing a different variable "b" as we go along

b = 0;
n = 5;
for i = 1:n
    b = b + 2;
    display(b);
end
b =

     2


b =

     4


b =

     6


b =

     8


b =

    10

Note on index variable naming

It's common in most programming languages to use "i" as the index variable when you're using it just to count. However, in MATLAB this overwrites the imaginary number. I still choose to use "i" in my loops, and to use "sqrt(-1)" when I need the imaginary number. You may have a different preference.

While loops

A "while" loop is similar to a for loop, but it stops after a condition gets met, rather than after a set number of loops. This is useful if you don't know how many loops you'll need. For example, if you're running an iterative algorithm and waiting for it to converge; generating random samples until you find one you like; waiting for the user to input the right value; etc.

% Count down from 100 in random intervals, stop when we hit or pass 0:

x = 100;
numIter = 0;
while x > 0
    x = x - randi(10);
    numIter = numIter + 1;
end
display(['Stopped after ', num2str(numIter), ' iterations']);
Stopped after 21 iterations

Avoid loops by vectorizing

In the "stem" example above, we really did have to use a loop to call the "figure" command multiple times. But "for" loops are slow in MATLAB. If your goal is to operate on multiple elements of a vector, or multiple rows/columns of a matrix, you can almost always avoid using a for loop by "vectorizing" the operation: that is to say, by operating on all the elements of the vector at once, or all the rows/columns of the matrix at once. This can take some thinking!

As a rule, if you're writing a for loop, ask yourself: am I operating on multiple numbers/vectors/matrices one after another? Can I rewrite this to do all the operations at once?

Use element-wise operators to avoid looping through a vector

Example: find the square of each element in a vector

% For loop:
vec = rand(1, 10);
squares_go_here = zeros(1, size(vec, 2)); % an empty vector to be filled in
for i = 1:size(vec, 2)
    squares_go_here(i) = sqrt(vec(i));
end

% Vectorized:
squares_vec = sqrt(vec);

Use "repmat" to make copies of a vector to help work with matrices

vec = [1:3]'
mat = [1:4; 5:8; 9:12]

% How do we divide every column in the matrix by this one column vector?

denom = repmat(vec, 1, size(mat, 2))
quotient = mat ./ denom

% "Repmat" will make concatenated copies of a matrix. We asked for just one
% copy in the first/row dimension (don't repeat the rows), and multiple
% copies in the second/column dimension (repeat this column over and over).
vec =

     1
     2
     3


mat =

     1     2     3     4
     5     6     7     8
     9    10    11    12


denom =

     1     1     1     1
     2     2     2     2
     3     3     3     3


quotient =

    1.0000    2.0000    3.0000    4.0000
    2.5000    3.0000    3.5000    4.0000
    3.0000    3.3333    3.6667    4.0000

Vectorizing your conditionals

What if you want to operate on only every fifth element of the vector? Or only the even numbers in your vector? Don't you have to loop through the vector and check the conditional every time?

No! You can vectorize your conditionals!

n = 12; mx = 10;
x = randi(mx, 1, n);

display('Is each element in x greater than 5?');
[x; x > 5]

display('Any?')
any(x > 5)

display('All?')
all(x > 5)

display('Which indices?')
idx = find(x > 5)

display('Get the elements at those indices');
x(idx)
Is each element in x greater than 5?

ans =

     4     4     5     2     8     5     5    10     1     7     2     2
     0     0     0     0     1     0     0     1     0     1     0     0

Any?

ans =

     1

All?

ans =

     0

Which indices?

idx =

     5     8    10

Get the elements at those indices

ans =

     8    10     7

Logical indexing

When we write "x(find(x>5))", we are saying "Give us a vector of 1's and 0's as long as x which tells us where the elements we want are; find the indices of the 1's; then get the elements of x at those positions." Using logical indexing, we can skip the "find" step, and just exract the elements at "1" and leave behind the elements at "0". MATLAB actually understands a matrix 1's and 0's as a list of "take this one, leave that one" if the matrix is the same size as the matrix to index into. Like this:

x

display('Get the elements of x that are greater than 5')
x(x>5)

display('Get only the even-indexed elements');
x(mod(1:length(x), 2)==0)
x =

     4     4     5     2     8     5     5    10     1     7     2     2

Get the elements of x that are greater than 5

ans =

     8    10     7

Get only the even-indexed elements

ans =

     4     2     5    10     7     2

When should I use a for loop?

Vectorization practice: compute the dot product

n = 10;
x = rand(1,n);
y = rand(1,n);

% For loop:
d_product_for = 0;
for i = 1:n
	 d_product_for = d_product_for + x(i)*y(i);
end

% Vectorized:
zip = x .* y;
d_product_vec = sum(zip);

d_product_vec = sum(x.*y); % Same thing, just as a single statement

% Using the "dot" function:
d_product_dot = dot(x, y);

% Using matrix multiplication:
d_product_mult = x * y';

% Verify that these dot products all came out to the same number!

More vectorization practice: simulate an experiment

Assume there is a true underlying relationship between the indepedent variable x and the dependent variable y, where y = sqrt(x). Let's simulate taking noisy measurements of the ground truth, and use vectorization to quickly and easily compute the squared error of each mesaurement.

% Create ground truth y values
x = [1:10]';
groundTruth = sqrt(x);

% Simulate one set of noisy measurements of the ground truth
noiseStd = 0.1;
measured = groundTruth + noiseStd*randn(length(x), 1);

% Plot the simulated data we created
figure; plot(x, groundTruth, 'bo-', x, measured, 'ro'); title('Simulated experiment');
legend('Ground truth', 'Noisy measurements (simulated)');

% How do we find the squared error term at each point without a for loop?
squaredErrors = (measured-groundTruth).^2;
figure; plot(squaredErrors, 'ro'); title('Squared error terms for simulated experiment');

Practice continued: a multi-trial experiment

(Use "repmat" to vectorize a matrix operation)

close all;

nTrials = 5;

noise = noiseStd*randn(length(x), nTrials);
measured = repmat(groundTruth, 1, nTrials) + noise;

% Plot the simulated data we created
figure; plot(x, groundTruth, 'bo-', x, measured', 'ro'); title('Simulated experiment');
legend('Ground truth', 'Noisy measurements (simulated)', 'Location', 'Best');

% Compute squared error term for each trial
errors = measured - repmat(groundTruth, 1, nTrials);
squaredErrors = errors.^2;
figure; plot(squaredErrors, 'ro'); title('Squared error terms for simulated experiment');

Practice continued: find the trials where the error was really bad

(Use vectorized conditionals and logical indexing!)

badTrials = errors > 1.5*std(errors(:))
worstSquaredErrors = squaredErrors(badTrials)
badTrials =

     1     0     0     0     0
     0     0     0     0     0
     0     0     0     0     0
     0     0     0     0     0
     0     0     0     0     0
     0     0     0     1     0
     0     0     0     0     0
     0     1     0     0     0
     0     0     0     0     0
     0     1     0     0     0


worstSquaredErrors =

    0.0577
    0.0219
    0.0231
    0.0265