How does a "for" loop in Matlab change its control variable on each iteration? - matlab

>> a = [1 1 2 3 5 8 13 21 13 37];
>> d = [1 2];
>> w = [0 0 0];
for e = d
g = 0;
for f = a
if mod (f, 2) == 0
g = [g f];
end
end
w = [w;g];
end
>> w
The output of W is the matrix
0 0 0
0 2 8
0 2 8
My question is about the for-loops. Do Matlab for-loops take a value, instead of being a logical condition? For example, when looking at 'for f = a' does f take the value of the array A and then the for-loop iterates through F by its columns?
Thanks.

FOR loops have a variable, each pass through the loop the iterator variable takes on the next value in the array. The iterator variable takes on the values of the columns, one at a time. The right hand side of the assignment statement in the foor loop needn't be a numeric array -- it could be a cell array of strings for example, or a structure, etc.
In other words, when you have for
for f=a, ....., end
this is equivalent to writing
for i=1:numel(a)/size(a,1); f=a(:,i); .... ; end
You can easily see this by adding some disp statements into your code:
for e = d
disp(e)
g = 0;
for f = a
disp(f)
if mod (f, 2) == 0
g = [g f];
end
end
w = [w;g];
end
WHILE loops in matlab take a logical condition.
(edit: i forgot that f takes on the value of the columns of a if a is multidimensioned)

Yes, MATLAB loop variables are assigned to "arrays", called vectors in MATLAB parlance. The value of the loop variable automatically iterates over elements of the vector upon each iteration of the loop. A common construct is
for i=1:10 %generates vector of 1,2,...,10
i %will print i=1, i=2, etc.
end
In this case the vector was generated on the fly, and is sequential numbers. But there is no reason you can't pass any arbitrary vector in to iterate over, like "a" in your case. Upon each iteration, "f" takes on the next value in the sequence contained in "a".

Related

Vector Function is Always Constant

New to MATLAB, and I need help with the following issue.
I want to create a function val=F(v,e) that takes in two inputs v, a 1xn vector, and a scalar e, and outputs a scalar val that counts the nonzero entries of the vector v-e, i.e. the vector v with e subtracted from all each of its entries. My code for the function is below:
function val = eff(vec, e)
val = sum( (vec - e > 0) );
end
When I evaluate the function at a single point it works as it should. but I want a plot of this function on (0,1). Plotting it gives a constant value over the entire range of e. I am using the following code on the main
figure
e = linspace(0,1);
plot(e, eff(rand(1,100),e),'o',e, e)
Also, when I use a small vector, say, rand(1,10), I get the following error message:
>Error using -
>
>Matrix dimensions must agree.
>
>Error in eff (line 3)
>
>val = sum( (vec - e > 0 ));
Is my function being too careless with matrix dimensions? Or is there an easier way to evaluate eff over a vector range?
Thanks in advance.
You have created a function which is designed to be applied only with a scalar e argument, and where passing e as an array would potentially cause errors ... but then you call it with e = linspace(0,1) which is an array.
The particular error when e is of size 10 is telling you that you cannot subtract it from a matrix of size 100.
When e happens to have the same size as vec, your function subtracts two equal-sized arrays, and returns their sum, which is a scalar. Therefore your plot is essentially doing something like plot(a_range, a_scalar), which is why it looks constant.
Instead, you should probably collect an array V for each value of e in a for loop, or using arrayfun, e.g.
e = linspace(0,1);
V = arrayfun(#eff, e);
and then plot e against V
Alternatively, you could rewrite your function such that it expects e to be an array, and your return value is an array of the same size as e, filled with the appropriate values.
without using arrayfun, your task can also be accomplished using broadcasting. I noticed you had this question tagged Octave as well as Matlab. Octave uses automatic broadcasting when you attempt elementwise operations with vectors in different dimensions. Matlab can do broadcasting with the bsxfun function. (if you want code that will run in either program, Octave also can use bsxfun.) Also, according to the release notes I believe Matlab 2016b will now include automatic broadcasting, although I cannot confirm yet that it will behave the same as Octave does.
Because your vectors vec and e are both row vectors, when you try to subtract them Matlab/Octave will subtract each element if they have the same size, or give a size mismatch error if they do not.
If you instead create one of the vectors as a column vector, broadcasting can take over. a simple example:
>> a = [1:4]
a =
1 2 3 4
>> b = [1:4]'
b =
1
2
3
4
>> a-b //Error in Matlab versions before 2016b
ans =
0 1 2 3
-1 0 1 2
-2 -1 0 1
-3 -2 -1 0
>> bsxfun(#minus,a,b) //works under Octave or Matlab
ans =
0 1 2 3
-1 0 1 2
-2 -1 0 1
-3 -2 -1 0
So, if you are running Octave, your code will run correctly if you just rewrite your function so the vectors use different dimensions. There are a number of ways to do this, but since both Matlab and Octave default to column ordering, you can use the : operator to force them to work the way you want. E.g.:
>> a = [1:4]
a =
1 2 3 4
>> a(:)
ans =
1
2
3
4
>> a(:)'
ans =
1 2 3 4
>> b = [1:4]'
b =
1
2
3
4
>> b(:)
ans =
1
2
3
4
>> b(:)'
ans =
1 2 3 4
So, after all that, you can rewrite your function:
function val = eff(vec, e)
vec = vec(:);
e = e(:)';
val = sum ( (vec-e ) > 0 );
end
If you're running matlab, or you want code that could run in both Octave and Matlab, you can just replace the last line of the function with:
sum ( bsxfun(#minus,vec,e) > 0 )
Finally, if you want, you can add some 'isvector' error checking at the beginning of the function in case you accidentally pass it an array. And note that had I chosen to make 'vec' a row vector and 'e' a column vector I would have had to tell the sum function which dimension to sum over. (it defaults to summing each column and returning a row vector, which matches the choices I made above.)
you function works fine as long as e is a scaler and not an array or matrix. You can then you looping or arrayfun (as already answered) to get a final answer
figure
e = rand(1,10); % create 10 random e numbers
vec = rand(1,100);
for inc = 1:length(e)
v(inc) = eff(vec,e(inc));
end
scatter(e,v);

if statement with for loop

I am writing a function to calculate a value of p.
The formula is p=r*q + pa. The pa value is column vector which i have got from the ode45 function. I have to change the value of q w.r.t time i.e from 0 to 1 sec q=500, from 1 to 2 sec q=0.
I have written this program:
function [p] = fom(t,pa)
r=0.007;
tin=1;
t=0:0.01:2;
if t <=tin
q = 600;
else t <= 2
q = 0;
end
[t,pa]=ode45('FOM1',[t],[0])
for i=[palv]
p=(r*q)+(i);
end
end
The problem is I am getting correct values after 1 sec in my output but before 1 sec where q is 0 values are not changing.
Is this if statement correct?
The problem is the following code snippet:
if t <=tin
q = 600;
else t <= 2
q = 0;
end
With t <= tin, you compare each element of vector t with tin. The result is again a vector, which contains 1 for all elements in t, which are smaller than tin, and 0 for all elements which are larger than tin, i.e.
[ 1 1 ... 1 1 0 0 ... 0 ]
According to the documentation, if evaluates the following:
An expression is true when its result is nonempty and contains only nonzero elements (logical or real numeric). Otherwise, the expression is false.
As the vector contains ones and zeros, i.e. not only nonzero elements, the this always evaluates to false and the else clause will be executed. q will therefore be a scalar, not a vector, which is always 0. To get a vector q, which is 600 for all t <= tin and zero otherwise, you can use
q = 600*(t <= tin);
which creates such a vector as printed above, and multiplies it by 600.
Now, a number of comments on your code:
Regarding the else: in the else, you can't have any other conditions, so t <= 2 will just be executed and printed to the console, but this does nothing, so you can leave that away.
You have t and pa as inputs to the function, but never use them. t is simply overwritten. So either leave t an input and don't overwrite it, or leave it away as input. Further, you can remove pa as input, as you calculate it using ode45.
I assume for i = palv is a typo and should be for i = pa.
In this for loop, by calling p=(r*q)+(i);, p gets overwritten in every iteration, i.e. the resulting p will be r*q plus the last element of pa.
You can create a vector p without a for loop using
p = r*q + pa
Everything together, the function becomes
function p = fom(t)
r=0.007;
tin=1;
q = 600 * (t <= tin);
[t,pa]=ode45('FOM1',[t],[0])
p = r*q + pa
end
and can be called e.g. by
t = 0:0.01:2
p = fom(t);

Assigning matrix elements to variables in a data set

Hello I'm new to Matlab.
I've written this script :
k2=2*pi();
z1 = 1;
z2 = 2;
z3 = 4;
for l = linspace(0,1,11)
A = [ -1 1 1 0 ; 1 z1/z2 -z1/z2 0 ; 0 exp(-i*k2*l) exp(i*k2*l) -1 ; 0 exp(- i*k2*l) -exp(i*k2*l) -z2/z3];
B = [ 1 ; 1 ; 0 ; 0];
D = inv(A);
C = mtimes(D,B) ;
display(C)
r = C(1,1); % this is supposed to set r = the 1,1 element in the matrix C
t = C(1,4); % see above
end
My idea for taking the values of r and t from C didnt appear to work. How can I do this properly?
Also I want to plot a graph of |r|,|t|, arg(r) and arg(t) for each value of l, my for loop overwrites the values of r and t? how can I either plot one point per loop or make r and t assign the new values so that they become lists of data.
Thanks a lot!
Matlab sets the first dimension of a matrix as row number (i.e. y position).
So you want t=C(4, 1), as you should see that the size of C is 4x1. As a note Matlab is quite good at suppressing singleton dimensions so you could do also do C(1) and C(4).
For your second point you want to set a particular element of r and t in each loop. This is the same as when you access at particular element of C when setting the values. For your case you can use the index l to determine the element. Remembering that in matlab arrays start at element 1 (not 0 as in many other languages). So you want something like r(l+1)=C(1); (or change l to start at 1).
In the more general case if you are not looping over an integer for some reason you may need to create a separate counter variable which you increase in the loop. Also it is good practice to preallocate such arrays when the size is known beforehand, often by r=zeros(11, 1) or similar (note: zeros(11) is an 11x11 matrix). This isn't significant in this case but can drastically increase execution time for large multi-dimensional arrays so is a good practice.

For-loop exits too early, need help as to why

I've written a simple function that takes a vector vec, iterates through it, performing an operation whose result is stored in another vector vecRes of same size at same index, and returns vecRes upon completing the loop. Below is function code:
function [ vecRes ] = squareTerms( vec )
vecSize = size(vec);
vecRes = zeros(vecSize);
for i = 1:vecSize
vecRes(i) = vec(i)^2;
end
end
Problem is that it seems to exit too early, after only one iteration in fact as the output appears as:
vecRes = 1 0 0 0 0 0 0 0 0 0
For input:
vec = 1 2 3 4 5 6 7 8 9 10
I can't figure out why it does so. Any help is greatly appreciated.
Size returns 2 values, rows and columns. Probably you are a having a 1xN vector. So size returns [1 N] and your loop runs 1 time.
>>> size ([1 2 3])
>
> ans =
>
> 1 3
>
>>> 1:size ([1 2 3])
>
> ans =
>
> 1
Others have pointed out the problem. My preferred solution in this sort of case is to use numel, i.e.
vecRes = zeros(size(vec));
for i = 1:numel(vec)
vecRes(i) = vec(i) ^ 2;
end
Of course, in this case, vectorisation is better still:
vecRes = vec .^ 2;
Replace
for i = 1:vecSize
with
for i = 1:vecSize(2)
vecSize is an array of numbers, not just a single value. For example, if vec is a 1 by 8 vector, then size(vec) will return [1, 8].
Therefore, your for-loop-statement,
for i = 1:vecSize
, is actually equivalent to something like:
for i = 1:[1, 8]
This doesn't make much sense. There are a number of ways to fix the problem. You could write:
for i = 1:length(vec)
or
for i = 1:numel(vec) % "numel" stands for "number of elements"
If the vector is 1xn instead of nx1, you can write:
for i = 1:size(vec, 2)
Yet another alternative is:
for i = 1:max(vecSize)
However, the most sensible option is not to write the squareTerms function at all and simply write
vecRes = vec.^2;
Note the dot before the caret. vec^2 and vec . ^2 are not the same thing.
If you put a dot before an operator sign, the operation will be performed element-wise. For example,
C = A * B
performs matrix multiplication, but
C = A .* B
will cause the first element of A to by multiplied by the first element of B, and the result will be assigned to the first element of C. Then, the product of the second elements of A and B will be taken, and the result will be stuck in the second element of C, and so on.
vecRes = vec.^2;

Recursion in Matlab. why is my recursion not working past the first step?

So I am very new to Matlab and I have been tasked with implementing LU factorization. I have to do it recursively because we are not allowed to use for loops in our code, and recursion will give us optimal marks. I have written this code. The code works for the first step, and does what it is supposed to, but for the next two steps, the matrix is not modified at all. I have this code:
function[L, U] = myLU(A,B, pos)
%A = Mtrix that becomes U
%B = Matrix that becomes L
tmp_L = B;
[x,y] = size(A);
if pos > x
L = B;
U = A;
return
else
pos %<-- just to see if it iterates through everything
[tmp_U,tmp_L] = elimMat(A,pos);
myLU(tmp_U,tmp_L, pos+1);
end
L = tmp_L;
U = tmp_U;
end
I where elimMat(A, pos) returns the elimination matrix for column pos. as well as another matrix, which will end up being the matrix of multipliers. What i tried doing is then finding the LU factorization of this matrix A. since elimMat returns L and U(this works, if i do it manually it works), i had to make a function that allows me to do it automatically without using a for loop. I thought i would do it recursively. What i ended up doing is adding another variable B to the function so that i can store intermediate values of my matrix obtained in each step and put it all together later.
So here is my question. Am i implementing the recursion wrong? and if i am how can i fix it? The other thing i wanted to ask is how can i implement this so i do not need variable B as an additional imput, and only use the existing variables, or variables previously defined, to find the solution? I would really like only two inputs in my function: The matrix name and the starting index.
here is elimMat if if helps:
function [M,L] = elimMat(A,k)
%find the size of the matrix
[x,y] = size(A);
tmp_mat = zeros(x,y);
%M = The current matrix we are working on for Elimination -> going to
%become U.
%L = The L part of the matrix we are working on. Contains all the
%multipliers. This is going to be our L matrix.
for i = 1:x
mult = A(i,k)/A(k,k);
if i > k
tmp_mat(i,k) = mult;
P = A(k,:)*mult;
A(i,:) = A(i,:)-P;
elseif i == k
tmp_mat(k,k) = 1;
end
end
M = A;
L = tmp_mat;
end
thanks for any feedback you can provide.
Here is the output: WHAT I GET VS what it should be:
[U = VS [U =
1 2 2 1 2 2
0 -4 -6 0 -4 -6
0 -2 -4] 0 0 2
L = VS [L=
1 0 0 1 0 0
4 0 0 4 1 0
4 0 0] 4 0.5 1
As you can see only the first column is changed
You forgot to catch the output of your recursive call:
[tmp_L, tmp_U] = myLU(tmp_U,tmp_L, pos+1);
Matlab passes variables by value, so a function cannot change its input variable itself (ok, it can, but it's tricky and unsafe).
Your original version didn't return the updated matrices, so the outermost function call encountered the myLU() call, let the recursion unfold and finish, and then went on to use tmp_L and tmp_U as returned from the very first call to elimMAT(A,1).
Note that you might want to standardize your functions such that they return U and L in the same order to avoid confusion.