Subscript indices must either be real positive integers or logicals error within Matlab decay program - matlab

I am having issues with a code of mine dealing with decay. The error "Subscript indices must either be real positive integers or logicals" continues to occur no matter how many times I attempt to fix the line of code: M=M(t)+h.*F
Here is the complete code so that it may be easier to solve the issue:
M=10000;
M=#(t) M*exp(-4.5*t);
F=-4.5*M(t);
h=.1;
t(1)=0;
tmax=20;
n=(tmax-t(1))/h;
i=1;
while h<=.5
while i<=n
t=t+h;
M=M(t)+h.*F;
data_out=[t,M];
dlmwrite('single_decay_euler_h.txt',data_out,'delimiter','\t','-append');
i=i+1;
end
h=h+.1;
end
Thanks for any help.

In the start, you're setting M = 5000;. In the following line, you're creating an anonymous function also called M:
M=#(t) M*exp(-4.5*t);
Now, your initial M = 5000 variable has been overwritten, and is substituted by the function:
M(t) = 5000 * exp(-4.5*t); %// Note that the first M is used to get 5000
Thereafter you do F = -4.5*M(t). I don't know what the value t is here, but you're giving F the value -4.5 * 5000 * exp(-4.5*t), for some value of t. You are not creating a function F.
In the first iteration of the loop, M=M(t)+h.*F; is interpreted as:
M = 5000 * exp(-4.5*0) + 0.1*F %// Where F has some value determined by previous
%// the function above and the previous value of t
%// -4.5*0 is because t = 0
M is now no longer a function, but a single scalar value. The next iteration t = 0.1. When you do: M=M(t)+h.*F; now, it interprets both the first and second M as a variable, not a function. t is therefore used as an index, instead of being an input parameter to the function M (since you have overwritten it).
When you are writing M(t), you are trying to access the 0.1'th element of the 1x1 matrix (scalar) M, which obviously isn't possible.
Additional notes:
The outer while loop has no purpose as it stands now, since i isn't reset after the inner loop. When you're finished with the first iteration of the outer loop, i is already >n, so it will never enter the inner loop again.
You shouldn't mix variable and function names (as you do with M. Use different names, always. Unless you have a very good reason not to.
data_out=[t,M]; is a growing vector inside a loop. This is considered very bad practice, ans is very slow. It's better to pre-allocate memory for the vector, for instance using data_out = zeros(k,1), and insert new values using indexes, data_out(ii) = M.
It's recommended not to use i and j as variable names in MATLAB as these also represent the imaginary unit sqrt(-1). This might cause some strange bugs if you're not paying attention to it.
You can almost certainly do what you're trying to do without loops. However, the function you have written is not functioning, and it's not explained all too well what you're trying to do, so it's hard to give advice as to how you can get what you want (but I'll give it a try). I'm skipping the dlmwrite-part, because I don't really understand what you want to output.
M = 5000;
t0 = 0;
tmax = 20;
h = 0.1; %// I prefer leading zeros in decimal numbers
t = t0: h: tmax;
data_out = M .* exp(-4.5 * t);

The problem is caused by M(t) in your code, because t is not an integer or logical (t=1,1.1,1.2,...)
You need to change your code to pass an integer as a subscript. Either multiply t by 10, or don't use the matrix M if you don't need it.

Related

Make vector of elements less than each element of another vector

I have a vector, v, of N positive integers whose values I do not know ahead of time. I would like to construct another vector, a, where the values in this new vector are determined by the values in v according to the following rules:
- The elements in a are all integers up to and including the value of each element in v
- 0 entries are included only once, but positive integers appear twice in a row
For example, if v is [1,0,2] then a should be: [0,1,1,0,0,1,1,2,2].
Is there a way to do this without just doing a for-loop with lots of if statements?
I've written the code in loop format but would like a vectorized function to handle it.
The classical version of your problem is to create a vector a with the concatenation of 1:n(i) where n(i) is the ith entry in a vector b, e.g.
b = [1,4,2];
gives a vector a
a = [1,1,2,3,4,1,2];
This problem is solved using cumsum on a vector ones(1,sum(b)) but resetting the sum at the points 1+cumsum(b(1:end-1)) corresponding to where the next sequence starts.
To solve your specific problem, we can do something similar. As you need two entries per step, we use a vector 0.5 * ones(1,sum(b*2+1)) together with floor. As you in addition only want the entry 0 to occur once, we will just have to start each sequence at 0.5 instead of at 0 (which would yield floor([0,0.5,...]) = [0,0,...]).
So in total we have something like
% construct the list of 0.5s
a = 0.5*ones(1,sum(b*2+1))
% Reset the sum where a new sequence should start
a(cumsum(b(1:end-1)*2+1)+1) =a(cumsum(b(1:end-1)*2+1)+1)*2 -(b(1:end-1)+1)
% Cumulate it and find the floor
a = floor(cumsum(a))
Note that all operations here are vectorised!
Benchmark:
You can do a benchmark using the following code
function SO()
b =randi([0,100],[1,1000]);
t1 = timeit(#() Nicky(b));
t2 = timeit(#() Recursive(b));
t3 = timeit(#() oneliner(b));
if all(Nicky(b) == Recursive(b)) && all(Recursive(b) == oneliner(b))
disp("All methods give the same result")
else
disp("Something wrong!")
end
disp("Vectorised time: "+t1+"s")
disp("Recursive time: "+t2+"s")
disp("One-Liner time: "+t3+"s")
end
function [a] = Nicky(b)
a = 0.5*ones(1,sum(b*2+1));
a(cumsum(b(1:end-1)*2+1)+1) =a(cumsum(b(1:end-1)*2+1)+1)*2 -(b(1:end-1)+1);
a = floor(cumsum(a));
end
function out=Recursive(arr)
out=myfun(arr);
function local_out=myfun(arr)
if isscalar(arr)
if arr
local_out=sort([0,1:arr,1:arr]); % this is faster
else
local_out=0;
end
else
local_out=[myfun(arr(1:end-1)),myfun(arr(end))];
end
end
end
function b = oneliner(a)
b = cell2mat(arrayfun(#(x)sort([0,1:x,1:x]),a,'UniformOutput',false));
end
Which gives me
All methods give the same result
Vectorised time: 0.00083574s
Recursive time: 0.0074404s
One-Liner time: 0.0099933s
So the vectorised one is indeed the fastest, by a factor approximately 10.
This can be done with a one-liner using eval:
a = eval(['[' sprintf('sort([0 1:%i 1:%i]) ',[v(:) v(:)]') ']']);
Here is another solution that does not use eval. Not sure what is intended by "vectorized function" but the following code is compact and can be easily made into a function:
a = [];
for i = 1:numel(v)
a = [a sort([0 1:v(i) 1:v(i)])];
end
Is there a way to do this without just doing a for loop with lots of if statements?
Sure. How about recursion? Of course, there is no guarantee that Matlab has tail call optimization.
For example, in a file named filename.m
function out=filename(arr)
out=myfun(in);
function local_out=myfun(arr)
if isscalar(arr)
if arr
local_out=sort([0,1:arr,1:arr]); % this is faster
else
local_out=0;
end
else
local_out=[myfun(arr(1:end-1)),myfun(arr(end))];
end
end
end
in cmd, type
input=[1,0,2];
filename(input);
You can take off the parent function. I added it just hoping Matlab can spot the recursion within filename.m and optimize for it.
would like a vectorized function to handle it.
Sure. Although I don't see the point of vectorizing in such a unique puzzle that is not generalizable to other applications. I also don't foresee a performance boost.
For example, assuming input is 1-by-N. In cmd, type
input=[1,0,2];
cell2mat(arrayfun(#(x)sort([0,1:x,1:x]),input,'UniformOutput',false)
Benchmark
In R2018a
>> clear all
>> in=randi([0,100],[1,100]); N=10000;
>> T=zeros(N,1);tic; for i=1:N; filename(in) ;T(i)=toc;end; mean(T),
ans =
1.5647
>> T=zeros(N,1);tic; for i=1:N; cell2mat(arrayfun(#(x)sort([0,1:x,1:x]),in,'UniformOutput',false)); T(i)=toc;end; mean(T),
ans =
3.8699
Ofc, I tested with a few more different inputs. The 'vectorized' method is always about twice as long.
Conclusion: Recursion is faster.

Implementing forward Euler method in matlab

I'm trying to implement the forward Euler method using matlab, but don't understand the error I'm getting. This is what I have written:
function y = ForwardEulerMethod(f,y0,T,N)
h=T/N;
t=zeros(N+1,1);
for i=0:N
t(i)=i.*h; %line 5
end
y=zeros(N+1,1);
y(0)=y0;
for i=1:N
y(i)=y(i-1)+h.*f(t(i-1),y(i-1));
end
end
My error is with line 5 and says, "Subscript indices must either be real positive integers or logicals." I am familiar with this rule, but don't see how I'm breaking it. I'm just trying to replace a zero at each location in t with a numerical value. What am I missing?
Agree with #vijoc above. You are indexing with 0 at multiple places. You could either change how you are indexing the values or get rid of the for loop altogether, like below:
function y = ForwardEulerMethod(f,y0,T,N)
h=T/N;
t=0:N .* h; % this takes the place of the first for-loop
y=zeros(N+1,1);
y(1)=y0;
for i=2:N+1
y(i)=y(i-1)+h.*f(t(i-1),y(i-1));
end
end
You could even replace the second loop if the function f takes vector inputs like so:
y(1) = y0;
y(2:end) = y(1:end-1) + h .* f(t(1:end-1), y(1:end-1));
You're iterating over i = 0:N and using it as t(i)=i.*h, so you're trying to access t(0) during the first iteration. Matlab indexing starts from 1, hence the error.
You also have other lines which will cause the same error, once the execution gets that far.

Approximating an integral in MATLAB

I've been trying to implement the following integral in MATLAB
Given a number n, I wrote the code that returns an array with n elements, containing approximations of each integral.
First, I tried this using a 'for' loop and the recurrence relationship on the first line. But from the 20th integral and above the values are completely wrong (correct to 0 significant figures and wrong sign).
The same goes if I use the explicit formula on the second line and two 'for' loops.
As n grows larger, so does the error on the approximations.
So the main issue here is that I haven't found a way to minimize the error as much as possible.
Any ideas? Thanks in advance.
Here is an example of the code and the resulting values, using the second formula:
This integral, for positive values of n, cannot have values >1 or <0
First attempt:
I tried the iterative method and found interesting thing. The approximation may not be true for all n. In fact if I keep track of (n-1)*I(n-1) in each loop I can see
I = zeros(20,3);
I(1,1) = 1-1/exp(1);
for ii = 2:20
I(ii,2) = ii-1;
I(ii,3) = (ii-1)*I(ii-1,1);
I(ii,1) = 1-I(ii,3);
end
There is some problem around n=18. In fact, I18 = 0.05719 and 18*I18 = 1.029 which is larger than 1. I don't think there is any numerical error or number overflow in this procedure.
Second attempt:
To make sure the maths is correct (I verified twice on paper) I used trapz to numerically evaluate the integral, and n=18 didn't cause any problem.
>> x = linspace(0,1,1+1e4);
>> f = #(n) exp(-1)*exp(x).*x.^(n-1);
>> f = #(n) exp(-1)*exp(x).*x.^(n-1)*1e-4;
>> trapz(f(5))
ans =
1.708934160520510e-01
>> trapz(f(17))
ans =
5.571936009790170e-02
>> trapz(f(18))
ans =
5.277113416899408e-02
>>
A closer look is as follows. I18 is slightly different (to the 4th significant digit) between the (stable) numerical method and (unstable) iterative method. 18*I18 is therefore possible to exceed 1.
I = zeros(20,3);
I(1,1) = 1-1/exp(1);
for ii = 2:20
I(ii,2) = ii-1;
I(ii,3) = (ii-1)*I(ii-1,1);
I(ii,1) = 1-I(ii,3);
end
J = zeros(20,3);
x = linspace(0,1,1+1e4);
f = #(n) exp(-1)*exp(x).*x.^(n-1)*1e-4;
J(1,1) = trapz(f(1));
for jj = 2:20
J(jj,1) = trapz(f(jj));
J(jj,2) = jj-1;
J(jj,3) = (jj-1)*J(jj-1,1);
end
I suspect there is an error in each iterative step due to the nature of numerical computations. If the iteration is long, the error propagates and, unfortunately in this case, amplifies rapidly. In order to verify this, I combined the above two methods into a hybrid algo. For most of the time the iterative way is used, and once in a while a numerical integral is evaluated from scratch without relying on previous iterations.
K = zeros(40,4);
K(1,1) = 1-1/exp(1);
for kk = 2:40
K(kk,2) = trapz(f(kk));
K(kk,3) = (kk-1)*K(kk-1,1);
K(kk,4) = 1-K(kk,3);
if mod(kk,5) == 0
K(kk,1) = K(kk,2);
else
K(kk,1) = K(kk,4);
end
end
If the iteration lasts more than 4 steps, error amplification will be large enough to invert the sign, and starts nonrecoverable oscillation.
The code should be able to explain all the data structures. Anyway, let me put some focus here. The second column is the result of trapz, which is the numerical integral done on the non-iterative integration definition of I(n). The third column is (n-1)*I(n-1) and should be always positive and less than 1. The forth column is 1-(n-1)*I(n-1) and should always be positive. The first column is the choice I have made between the trapz result and iterative result, to be the "true" value of I(n).
As can be seen here, in each iteration there is a small error compared to the independent numerical way. The error grows in the 3rd and 4th iteration and finally breaks the thing in its 5th. This is observed around n=25, under the case that I pick the numerical result in every 5 loops since the beginning.
Conclusion: There is nothing wrong with any definition of this integral. However the numerical error when evaluating the expressions is unfortunately aggregating, hence limiting the way you can perform the computation.

while loop until certain condition on the function value is met

I'm trying to fit some data with Matlab, using the least square method.
I found best fit parameters, and I want to determine the uncertainty on them now.
To determine the uncertainty on the first parameter, say a, we have seen in course that one should apply a variation to one parameter, until the difference between the function (evaluated at that variation) minus the original function value equals 1.
That is, I have a vector called [bestparam] in my Matlab code, containing the four parameters a, b, c and d.
I also have a function defined in another file, called chi-square, which I evaluated at the best parameters.
I now want to apply a small variation to the parameter a, and keep doing this until chi-square(a + variation) - chi-square = 1. The difference must be exactly one. I implemented for this the following code:
i = 0;
a_new = a + i;
%small variation on the parameter a
new_param = [a_new b c d];
%my new parameters at which I want the function chisquare to be evaluated
newchisquare = feval(#chisquare, [new_param], X, Y, dY);
%the function value
while newchisquare - chisquarevalue ~= 1
i = i + 0.0001;
a_new = a_new + i;
new_param = [a_new b c d];
newchisquare = feval(#chisquare, [new_param], X, Y, dY);
end
disp(a_new);
disp(newchisquare);
But when I execute this loop, it never stops running. When I change the condition to < 1, i.e. that the difference should be larger than one, then it does stop after like 5 seconds. But then the difference between the function values is no longer exactly one. For example, my original function value is 63.5509 and the new one is then 64.6145 which is not exactly 1 larger.
So is there some way to implement the code, and to keep updating the parameter a until the difference is exactly one? Help is appreciated.
Performing numerical methods I wouldn't recommend using operations like == or ~= unless you are sure that you are comparing two integers. Only small deviations of your value may cause your code to never stop. You can apply some tolerance treshold to make your code stop if it is approximately correct:
TOL = 1e-2;
while (abs(newchisquare - chisquarevalue) <= 1 - TOL)
% your code
end

nlfilter taking same values twice

I used nlfilter for a test function of mine as follows:
function funct
clear all;
clc;
I = rand(11,11);
ld = input('Enter the lag = ') % prompt for lag distance
A = nlfilter(I, [7 7], #dirvar);
% Subfunction
function [h] = dirvar(I)
c = (size(I)+1)/2
EW = I(c(1),c(2):end)
h = length(EW) - ld
end
end
The function works fine but it is expected that nlfilter progresses element by element, but in first two iterations the values of EW will be same 0.2089 0.4162 0.9398 0.1058. But then onwards for all iterations the next element is selected, for 3rd it is 0.4162 0.9398 0.1058 0.1920, for 4th it is 0.9398 0.1058 0.1920 0.5201 and so on. Why is it so?
This is nothing to worry about. It happens because nlfilter needs to evaluate your function to know what kind of output to create. So it uses feval once before starting to move across the image. The output from this feval call is what you see the first time.
From the nlfilter code:
% Find out what output type to make.
rows = 0:(nhood(1)-1);
cols = 0:(nhood(2)-1);
b = mkconstarray(class(feval(fun,aa(1+rows,1+cols),params{:})), 0, size(a));
% Apply fun to each neighborhood of a
f = waitbar(0,'Applying neighborhood operation...');
for i=1:ma,
for j=1:na,
x = aa(i+rows,j+cols);
b(i,j) = feval(fun,x,params{:});
end
waitbar(i/ma)
end
The 4th line call to eval is what you observe as the first output from EW, but it is not used to anything other than making the b matrix the right class. All the proper iterations happen in the for loop below. This means that the "duplicate" values you observe does not affect your final output matrix, and you need not worry.
I hope you know what the length function does? It does not give you the Euclidean length of a vector, but rather the largest dimension of a vector (so in your case, that should be 4). If you want the Euclidean length (or 2-norm), use the function norm instead. If your code does the right thing, you might want to use something like:
sz = size(I,2);
h = sz - (sz+1)/2 - ld;
In your example, this means that depending on the lag you provide, the output should be constant. Also note that you might want to put semicolons after each line in your subfunction and that using clear all as the first line of a function is useless since a function will always be executed in its own workspace (that will however clear persistent or global variables, but you don't use them in your code).