How to create an array in Matlab but run a function at the same time? - arrays

I'm trying to populate an array with values from a function that I am running. In this function I am changing a variable z2 in increments of 0.01 from 0 to 0.99. At the same time I want to populate an array with those values. I've tried a while loop and a for loop. How to write this with only a for loop?
my code:
Ys = y(2000);
Mp = ((max(y)-Ys)/Ys)*100;
[max, i] = max(y);
tp = t(i);
z = sqrt(((log(Mp))^2)/((pi*pi)+(log(Mp))^2));
%Given Equations
wd = pi/tp;
wn = wd/(sqrt(1-(z*z)));
ts = 4.6/(z*wn);
tr = 1.8/wn;
%for loop for z from 0 to 0.99
for i = 1:100;
for z2 = 0:0.01:0.99
%tr*wn = Fa
Fa(i) = 2.917*z2^2 - 0.4167*z2 + 1;
i = i + 1;
if i >= 100;
break;
end
end
end
find_zeta = interp1(Fa, 1.8);
disp(Fa);
disp(find_zeta);
error I am experiencing: I am getting only 1s in my array.

You can do it without a loop, in a 'vectorized' matlab statement
z2 = 0:0.01:0.99;
Fa = 2.917*z2.^2 - 0.4167.*z2 + 1;
First, compute z2 values that you need. Next, operating on the vector element by element (note .^ syntax) compute Fa. There is a fundamental difference between ^ and .^ operators, and you really want to know the difference. In general, you can add a . to an operator, which means that it will work on individual scalar elements of your array. Read about matrix and array operators here. For example:
A = rand(10);
B = rand(10);
% matrix multiplication
C=A*B;
% element by element multiplication
C=A.*B
The complaint you got only means that what you do is inefficient. Since matlab does not know the size of Fa beforehand, it needs to reallocate the array every time you append to it. You can still use it, it is just inefficient.

Index's in matlab start from 1, not from 0 as they do in other programming languages
The error you are getting is about pre-allocating the array. Add Fa = zeros(1,100); to the start of the script and it will go away.
Fa = zeros(1,100);
i = 1;
for z2 = 0:0.01:0.99
Fa(i) = 2.917*z2^2 - 0.4167*z2 + 1;
i = i +1;
end
The use of 'vectorized' as suggested in another answer is more the way to go though.

Related

Finite element analysis, 1D

I want the output to be values of C, at all range of i and t in the loops.
When I run it, I get error in sym/subref, L_tilde, idx.
I do not know what it means.
syms C;
alphaX=0.05;
DiffCoef = 5*10^-5
v = 0.1;
L = 10; xZones = 100;
dx = L/xZones;
T = 150;
u = 0.1;
dt= 0.005;
t = 150;
D = DiffCoef + (alphaX * u);
for i = 1:xZones
for t = 1:xZones
(C(i,t+dt) - C(i,t))/dt = -u(C(i+1,t +dt) - C(i,t+dt))/dx + D(C(i+1,t+dt) - 2*(C(i,t+dt) + C(i-1,t+dt)))/(dx)^2
end
end
Assuming this is Matlab code, there are some problems:
1) you cannot assign two outputs so the statement
(C(i,t+dt) - C(i,t))/dt = ...
is illegal. Matlab expressions can only return one output (without using for example "deal"), so in your case you would have to rearrange your expression as maybe
C(i,t+dt) = ...
C(i,t) = ...
Moreover, as this seems it is a time difference formula typically anything f(t+dt) is the unknown (left hand side), and f(t) are the known values as previous time step (right hand side) so that
f(t+dt) = dt*old_rhs - f(t)
In your case you have prescribed _C(i,t_dt)_ which seems very unusual, so please check your equations.

Grow 3D array in Matlab

Is there a way to grow a 3D array in the third dimension using the end index in a loop in Matlab?
In 2D it can be done like
a = [];
for x = y
a(end + 1, :) = f(x);
end
But in 3D the same thing will not work as a(1,1,end) will try to index a(1,1,1) the first iteration (not a(1,1,0) as one might expect). So I can't do
im = [];
for x = y
im(:, :, end + 1) = g(x);
end
It seems the end of a in third dimension is handled a bit differently than in the first two:
>> a = [];
>> a(end,end,end) = 1
Attempted to access a(0,0,1); index must be a positive integer or logical.
Am I missing something about how end indexing works here?
What you're asking...
If you know the size of g(x), initialize im to an empty 3d-array:
im = zeros(n, m, 0); %instead of im = [];
I think your code should work now.
A better way...
Another note, resizing arrays each iteration is expensive! This doesn't really matter if the array is small, but for huge matrices, there can be a big performance hit.
I'd initialize to:
im = zeros(n, m, length(y));
And then index appropriately. For example:
i = 1;
for x = y
im(:, :, i) = g(x);
i = i + 1;
end
This way you're not assigning new memory and copying over the whole matrix im each time it gets resized!

How to recursively fill an array with functions?

So I'm trying to write a function to generate Hermite polynomials and it's doing something super crazy ... Why does it generate different elements for h when I start with a different n? So inputting Hpoly(2,1) gives
h = [ 1, 2*y, 4*y^2 - 2]
while for Hpoly(3,1) ,
h = [ 1, 2*y, 4*y^2 - 4, 2*y*(4*y^2 - 4) - 8*y]
( (4y^2 - 2) vs (4y^2 - 4) as a third element here )
also, I can't figure out how to actually evaluate the expression. I tried out = subs(h(np1),y,x) but that did nothing.
code:
function out = Hpoly(n, x)
clc;
syms y
np1 = n + 1;
h = [1, 2*y];
f(np1)
function f(np1)
if numel(h) < np1
f(np1 - 1)
h(np1) = 2*y*h(np1-1) - 2*(n-1)*h(np1-2);
end
end
h
y = x;
out = h(np1);
end
-------------------------- EDIT ----------------------------
So I got around that by using a while loop instead. I wonder why the other way didn't work ... (and still can't figure out how to evaluate the expression other than just plug in x from the very beginning ... I suppose that's not that important, but would still be nice to know...)
Sadly, my code isn't as fast as hermiteH :( I wonder why.
function out = Hpoly(n, x)
h = [1, 2*x];
np1 = n + 1;
while np1 > length(h)
h(end+1) = 2*x*h(end) - 2*(length(h)-1)*h(end-1);
end
out = h(end)
end
Why is your code slower? Recursion is not necessarily of Matlab's fortes so you may have improved it by using a recurrence relation. However, hermiteH is written in C and your loop won't be as fast as it could be because you're using a while instead of for and needlessly reallocating memory instead of preallocating it. hermiteH may even use a lookup table for the first coefficients or it might benefit from vectorization using the explicit expression. I might rewrite your function like this:
function h = Hpoly(n,x)
% n - Increasing sequence of integers starting at zero
% x - Point at which to evaluate polynomial, numeric or symbolic value
mx = max(n);
h = cast(zeros(1,mx),class(x)); % Use zeros(1,mx,'like',x) in newer versions of Matlab
h(1) = 1;
if mx > 0
h(2) = 2*x;
end
for i = 2:length(n)-1
h(i+1) = 2*x*h(i)-2*(i-1)*h(i-1);
end
You can then call it with
syms x;
deg = 3;
h = Hpoly(0:deg,x)
which returns [ 1, 2*x, 4*x^2 - 2, 2*x*(4*x^2 - 2) - 8*x] (use expand on the output if you want). Unfortunately, this won't be much faster if x is symbolic.
If you're only interested in numeric results of the the polynomial evaluated at particular values, then it's best to avoid symbolic math altogether. The function above valued for double precision x will be three to four orders of magnitude faster than for symbolic x. For example:
x = pi;
deg = 3;
h = Hpoly(0:deg,x)
yields
h =
1.0e+02 *
0.010000000000000 0.062831853071796 0.374784176043574 2.103511015993210
Note:
The hermiteH function is R2015a+, but assuming that you still have access to the Symbolic Math toolbox and the Matlab version is R2012b+, you can also try calling MuPAD's orthpoly::hermite. hermiteH used this function under the hood. See here for details on how to call MuPAD functions from Matlab. This function is a bit simpler in that it only returns a single term. Using a for loop:
syms x;
deg = 2;
h = sym(zeros(1,deg+1));
for i = 1:deg+1
h(i) = feval(symengine,'orthpoly::hermite',i-1,x);
end
Alternatively, you can use map to vectorize the above:
deg = 2;
h = feval(symengine,'map',0:deg,'n->orthpoly::hermite(n,x)');
Both return [ 1, 2*x, 4*x^2 - 2].

Variable dimensions in file don't match with dimension of indexing subscript if one dimension is singleton

I want to test a function func(par1,par2,par3) with all combinations of the parameters par1, par2 and par3 and store the output in a .mat file. My code looks like this at the moment:
n1 = 3;
n2 = 1;
n3 = 2;
parList1 = rand(1,n1); % n1,n2,n3 is just some integer
parList2 = rand(1,n2); % the lists are edited by hand in the actual script
parList3 = rand(1,n3);
saveFile = matfile('file.mat','Writable',true);
% allocate memory
saveFile.output = NaN(numel(parList1),numel(parList2),numel(parList3));
counter1 = 0;
for par1 = parList1
counter1 = counter1 + 1;
counter2 = 0; % reset inner counter
for par2 = parList2
counter2 = counter2 + 1;
counter3 = 0; % reset inner counter
for par3 = parList3
counter3 = counter3 + 1;
saveFile.output(counter1,counter2,counter3) = sum([par1,par2,par3]);
end
end
end
This works except if parList3 has only one item, i.e. if n3 = 1. Then the saveFile.output has singleton dimensions and I get the error
Variable 'output' has 2 dimensions in the file, this does not match the 3 dimensions in the indexing subscripts.
Is there a elegant way to fix this?
The expression in the for statement needs to be a row array, not a column array as in your example. The loops will exit after the first value with your code. Set a breakpoint on the saveFile.output command to see what I mean. With a column array, par1 will not be a scalar as desired, but the whole parList1 column. With a row array, par1 will iterate through each value of parList1 as intended
Another thing is that you need to reset your inner counters (counter2 and counter2) or your second and third dimensions will blow up larger than you expected.
The n3=1 problem is expected behavior because matfile defines the variables with fixed number of dimensions and it will treat saveFile.output as 2D. Once you have fixed those issues, you can solve the n3=1 problem by changing the line,
saveFile.output(counter1,counter2,counter3) = sum([par1,par2,par3]);
to
if n3==1, saveFile.output(counter1,counter2) = sum([par1,par2,par3]);
else saveFile.output(counter1,counter2,counter3) = sum([par1,par2,par3]);
end
By now I realized that actually in matfiles all the singleton dimensions, except for the first two are removed.
In my actual programm I decided to save the data in the file linearly and circumvent matfile's lack of linear indexing capability by using the functions sub2ind and ind2sub.

Basic MATLAB variable manipulation

I have a dwc = [3001 x 2 double] which more or less is a sin function, I have a for loop finding top values in dwc(:,2). Lets say that there is a top value in dwc(531,2) which way is best way or what is easy to take dwc(531,1) and dwc(531,2) and make an M = [num_of_top_points x 2 double]?
For the following loop, what do I do?
j = 0;
for i = 2:size(dwcL01,1)-1
if dwcL01(i,2) > dwcL01(i-1,2) && dwcL01(i,2) > dwcL01(i+1,2)
j = j+1;
?? = dwcL01(i,:);
end
end
This is how you complete your loop
j = 0;
M = [];
for i = 2:size(dwcL01,1)-1
if dwcL01(i,2) > dwcL01(i-1,2) && dwcL01(i,2) > dwcL01(i+1,2)
j = j+1;
M(j, :) = dwcL01(i, :);
end
end
But you could do this much more efficiently by vectorizing
%//Some example data
x = -4*pi:0.5:4*pi;
y = cos(x);
dwcL01 = [x(:), y(:)]; %// (:) just makes it a column
%// Finding the peaks using diff and sign. Note that I add the first element to the beginning as diff reduces the size by one so this prevents offsetting
F = diff(sign(diff([dwcL01(1,2);dwcL01(:,2)]))) < 0;
M = [dwcL01(F,:)', dwcL01(F,:)'];
plot(x, y, M(:,1), M(:,2), '*r')
How that works is first we find the difference of each element consecutive element pair. Now when the sign changes, that means we've hit a max or min. If the sign change is negative then the gradient went from positive to negative which is a max. So I use diff(sign()) to find the points where the sign changes and then > 0 to create a logical matrix with false everywhere expect for the max. Then I use logical indexing to extract the max.
You could append it to a matrix (let's call it dwcL01_max) - this isn't the fastest way because the matrix size changes each loop but it works:
dwcL01_max = [dwcL01_max dwcL01(i,:)];
The other option would be to use the builtin findpeaks (from the signal proc toolbox)
[~, dwcL01_peaks] = findpeaks(dwcL01(:,2));
dwcL01_max = dwcL01(dwcL01_peaks, :);

Resources