## Monday, 20 April 2015

### Maths on Steroids (Accelerated Mathematics 1, 2015): 3

If-statements, for-loops, while-loops, what are they good for? Absolutely...everything in programming?

For most of you, last week would have been the first time that you've seen an if-statement in Matlab. And you were given a slightly involved task: you were asked to write up a function that takes in a matrix $$A$$ and does the following:
1. checks if $$A$$ is square;
2. checks if the columns of $$A$$ each add up to $$1$$;
3. checks if the entries of $$A$$ are all non-negative;
4. puts out a message if any of the checks fail;
5. outputs the row-reduced form of $$I-A$$ if $$A$$ satisfies the checks.
To begin with, let's have a think about how to write if-statements for each of the checks in Matlab.

1. If $$A$$ is square.

Here's a simple if-statement to check for square-ness:

[m,n]=size(A);
if m~=n
'Not square enough?'
end

So, what does it do? We first compute the size of the matrix A, this is a vector whose first entry is the number of rows that $$A$$ has and whose second entry is the number of columns. Thus, writing "[m,n]=size(A)" is to assign "[m,n]" to be this vector; i.e.: m=the number of rows of $$A$$ and n=the number of columns of $$A$$. Then, if m isn't equal to n, this little if-statement outputs the message "Not square enough?", but if m is equal to, then nothing happens.

One question that you should be asking at this point is: what's the deal with the semi-colon ";" at the end of the first line. Why do we bother having it at all? Well, semi-colons at the end of a command lets you run that command without displaying the results. For our purposes, we wanted to assign numbers to m and n, but we didn't really want to see them. Hence the semi-colon. On the other hand, we definitely wanted to be able to read the message "Not square enough?", so we didn't put a semi-colon after that.

2. If the columns of $$A$$ add up to $$1$$.

This first idea might be to write something like the following:

[m,n]=size(A);
if sum(A)~=ones(1,n) (THIS IS WRONG!)
'columns need to sum to one!'
end

And where this goes wrong (my mistake - I actually thought that this worked during the Tuesday lab), is that the if-statement then just checks if the first entries of "sum(A)" and "ones(1,n)" are equal. So, it only checks if the first column of $$A$$ adds to $$1$$.

Here's the "intended" if-statement:

[m,n]=size(A);
T=sum(A);
t=0;
for k=1:n
if T(k)~=1
t=t+1;
end
end
if t>0
'columns need to sum to 1!'
end

We start by creating a vector $$T$$ corresponding to the column-sum of $$A$$. Now, we'd like to check if each entry of $$T$$ is equal to $$1$$, and the above code deals with this by creating a variable $$t$$ that increases by $$1$$ for every entry $$T(k)$$ of $$T$$ that isn't equal to $$1$$. So, after going through the entries of $$T$$ using the for-loop in the third line, either $$t=0$$ in which case $$T$$ is a row of $$1$$s, or $$t>0$$, in which case $$T$$ isn't a vector of $$1$$s and hence the columns of $$A$$ don't each sum to $$1$$. Note that again, nothing happens if the columns of $$A$$ each sum to $$1$$.

Aaaaaaand, now that you've understood how that worked, let me tell you about alternative ways of doing step 2. To begin with, let's understand how truth values work. You saw in this tute that writing "m==n" lets you check if m is equal to n. But have you tried typing this into Matlab and seeing what you get? Try "3==3" and "3==2" (without the quotation marks). You should have gotten "ans=$$1$$" and "ans=$$0$$" respectively: that's right! Matlab thinks of $$1$$ as "true" and $$0$$ as "false". Soooo, what happens if I type in something like "[3 2]==[3 3]"? Well...TRY IT! You should get in return a vector "[1 0]", signifying that this is true for the first value, and false for the second value. Sooo....how can we use this to our advantage? Here's a first idea:

[m,n]=size(A);
if sum(sum(A)==ones(1,n))~=n
'columns need to sum to one!'
end

So, what's going on here? The command "sum(A)==ones(1,n)" checks the column-sum vector of $$A$$ against a row of $$1$$s and outputs a vector filled with $$1$$s and $$0$$s signifying whether the corresponding entries of these two row vectors match up. IF every column sums to $$1$$, then we should expect "sum(A)==ones(1,n)" to produce a length n vector of $$1$$s - signifying "true" in every entry. If not, then some of the outputs will be $$0$$. Now, the sum function not only adds up the columns of a matrix $$A$$, it also adds up all the entries of a row vector (if you didn't know this, you could have still taken a transposition of a row vector and then applied the sum function). So, the above code basically checks if "sum(A)==ones(1,n)" is n and displays a message if it's not.

And here's another variant:

[m,n]=size(A);
if sum(abs(sum(A)-ones(1,n)))>0
'columns need to sum to one!'
end

The absolute value function "abs", when combined with "sum" adds up the difference between sum($$A$$) and a row of $$1$$s. So, if there is no difference, then this should be $$0$$ and if there is a difference, then it'll be strictly above $$0$$.

See if you can come up with something-else?

3. If every entry of $$A$$ is positive.

[m,n]=sum(A);
if A~=abs(A) (THIS IS WRONG!)
'slightly too negative =('
end

Again, the problem here is that "A~=abs(A)" only checks to see if the first two entries of these two matrices are equal, and neglects the rest. =/

The "intended" solution is something like the following:

[m,n]=size(A);
u=0;
for j=1:m
for k=1:n
if A(j,k)<0
u=u+1;
end
end
end
if u~=0
'slightly too negative =('
end

Like before, the $$u$$ here is a marker for how many entries are negative. The two nested for-loops lets us go through all of the entries of $$A$$ and checks if they're (strictly) negative.

And, here's an alternative method - see if you can figure out what's going on:

[m,n]=size(A);
if sum(sum(A==abs(A)))<m*n
'slightly too negative =('
end

And another variant:

[m,n]=sum(A);
if sum(sum(A-abs(A)))>0
'slightly too negative =('
end

4. Putting all of this together in just a single for-loop, and in the form of a function, we have:

Method 1:

function B=xx_solve(A)
[m,n]=size(A);
T=sum(A);
t=0;
for k=1:n
if T(k)~=1
t=t+1;
end
end
u=0;
for j=1:m
for k=1:n
if A(j,k)<0
u=u+1;
end
end
end
if m~=n
'Not square enough?'
elseif t>0
'columns need to sum to 1!'
elseif u>0
'slightly too negative =('
else
B=rref(eye(n)-A);
end

This is the most rudimentary, and in some sense, most "universal" version. The ideas that you use here apply to most programming languages.

One thing that you might ask is why there is a semi-colon after "B=rref(eye(n)-A)", as this would withhold the displaying of the matrix $$B$$ - the desired output? Well, that has to do with the first line: the fact that it's written "B=xx_solve(A)" means that inputting $$A$$ into the function xx_solve will output "B" (provided that everything goes right).

Method 2:

function B=xx_solve(A)
[m,n]=size(A);
if m~=n
'Not square enough?'
elseif sum(sum(A)==ones(1,n))<n
'columns need to sum to 1!'
elseif sum(sum(A==abs(A)))<m*n
'slightly too negative =('
else
B=rref(eye(n)-A);
end

This should be pretty self-explanatory, right?

Method 3:

function B=xx_solve(A)
[m,n]=size(A);
if m~=n
'Not square enough?'
elseif sum(abs(sum(A)-ones(1,n)))>0
'columns need to sum to 1!'
elseif sum(sum(A-abs(A)))>0
'slightly too negative =('
else
B=rref(eye(n)-A);
end

And here's a fnal method...it's the intended method in Matlab, but requires knowing a specialised command called "isequal" that basically tests if two matrices are completely equal.

Method 4:

function B=xx_solve(A)
[m,n]=size(A);
if m~=n
'Not square enough?'
elseif isequal(sum(A),ones(1,n))
'columns need to sum to 1!'
elseif isequal(A,abs(A))
'slightly too negative =('
else
B=rref(eye(n)-A);
end

This is the most efficient method, but it's also pretty Matlab-specific? And to be fair...it's a little bit on the boring side...so...um, totes go with the other methods guys!?!? Totes.