Improve Speed of Piecewise Function in MATLAB - matlab

I have a small piecewise function that profiling reveals is taking 60% of the runtime of the program. It is called very often because it goes within some integrals that I perform quite a lot in my code.
According to profiling, it is called 213560 times, taking 47.786 s in total, corresponding to ~220 microseconds per call.
I want to pass it an array, and it should return an array, operating element wise.
I know that using loops in Matlab is very slow and should be avoided, but I'm not sure how to vectorise this sort of function.
function bottleradius = aux_bottle_radius(z_array)
%AUXBOTTLERADIUS Radius of aux bottle
% This returns the radius of the aux bottle as a function of z. It works for all
% heights of aux bottle, just remember to integrate over the right height
% range
bottleradius = zeros(size(z_array));
for i = 1 : max(size(z_array))
if z_array(i)<-30e-3
%door cavity
bottleradius(i) = 34e-3;
elseif z_array(i)>=-30e-3 && z_array(i)<-20e-3
%radiussing door cavity
bottleradius(i) = 34e-3 + 10e-3 - sqrt((10e-3).^2 - (z_array(i)+30e-3).^2);
elseif z_array(i)>=-20e-3 && z_array(i)<-10e-3
%aluminium plate
bottleradius(i) = 46e-3;
elseif z_array(i)>=-10e-3 && z_array(i)<0e-3
%radiussing aluminium plate to main bottle
bottleradius(i) = 46e-3 + 10e-3 - sqrt((10e-3).^2 - (z_array(i)+10e-3).^2);
elseif z_array(i)>=0e-3
%top of Al plate, bottom of main bottle
bottleradius(i) = 185e-3;
else
bottleradius(i) = 0;
end
end
end

You can do that completely vectorized with logical operators. You can essentially replace that code with:
function bottleradius = aux_bottle_radius(z_array)
%// Declare initial output array of all zeroes
bottleradius = zeros(size(z_array));
%// Condition #1 - Check for all values < -30e-3 and set accordingly
bottleradius(z_array < -30e-3) = 34e-3;
%// Condition #2 - Check for all values >= -30e-3 and < -20e-3 and set accordingly
ind = z_array >= -30e-3 & z_array < -20e-3;
bottleradius(ind) = 34e-3 + 10e-3 - sqrt((10e-3).^2 - (z_array(ind)+30e-3).^2);
%// Condition #3 - Check for all values >= -20e-3 and < -10e-3 and set accordingly
bottleradius(z_array >=-20e-3 & z_array < -10e-3) = 46e-3;
%// Condition #4 - Check for all values >= -10e-3 and < 0 and set accordingly
ind = z_array >=-10e-3 & z_array < 0;
bottleradius(ind) = 46e-3 + 10e-3 - sqrt((10e-3).^2 - (z_array(ind)+10e-3).^2);
%// Condition #5 - Check for all values >= 0 and set accordingly
bottleradius(z_array >= 0) = 185e-3;
end
Minor comments
0e-3 doesn't make any sense precision wise. This is essentially the same as 0 and I've changed that in your code.
Note that for conditions #2 and #4, I precompute a logical array that indicates where we would need to access the corresponding values in z_array to make things easier and set those same locations in bottleradius to be the desired computed outputs. I don't do this for the other conditions because you're just setting them to a single constant.
Thankfully, you use element-wise operators for conditions #2 and #4 so there wasn't a need to change the expressions for those conditions.

Related

How do I linearly interpolate past missing values using future values in a while loop?

I am using MATLAB R2020a on a MacOS. I am trying to remove outlier values in a while loop. This involves calculating an exponentially weighted moving mean and then comparing this a vector value. If the conditions are met, the vector input is then added to a separate vector of 'acceptable' values. The while loop then advances to the next input and calculates the new exponentially weighted moving average which includes the newly accepted vector input.
However, if the condition is not met, I written code so that, instead of adding the input sample, a zero is added to the vector of 'acceptable' values. Upon the next acceptable value being added, I currently have it so the zero immediately before is replaced by the mean of the 2 framing acceptable values. However, this only accounts for one past zero and not for multiple outliers. Replacing with a framing mean may also introduce aliaising errors.
Is there any way that the zeros can instead be replaced by linearly interpolating the "candidate outlier" point using the gradient based on the framing 2 accepted vector input values? That is, is there a way of counting backwards within the while loop to search for and replace zeros as soon as a new 'acceptable' value is found?
I would very much appreciate any suggestions, thanks in advance.
%Calculate exponentially weighted moving mean and tau without outliers
accepted_means = zeros(length(cycle_periods_filtered),1); % array for accepted exponentially weighted means
accepted_means(1) = cycle_periods_filtered(1);
k = zeros(length(cycle_periods_filtered),1); % array for accepted raw cycle periods
m = zeros(length(cycle_periods_filtered), 1); % array for raw periods for all cycles with outliers replaced by mean of framing values
k(1) = cycle_periods_filtered(1);
m(1) = cycle_periods_filtered(1);
tau = m/3; % pre-allocation for efficiency
i = 2; % index for counting through input signal
j = 2; % index for counting through accepted exponential mean values
n = 2; % index for counting through raw periods of all cycles
cycle_index3(1) = 1;
while i <= length(cycle_periods_filtered)
mavCurrent = (1 - 1/w(j))*accepted_means(j - 1) + (1/w(j))*cycle_periods_filtered(i);
if cycle_periods_filtered(i) < 1.5*(accepted_means(j - 1)) && cycle_periods_filtered(i) > 0.5*(accepted_means(j - 1)) % Identify high and low outliers
accepted_means(j) = mavCurrent;
k(j) = cycle_periods_filtered(i);
m(n) = cycle_periods_filtered(i);
cycle_index3(n) = i;
tau(n) = m(n)/3;
if m(n - 1) == 0
m(n - 1) = (k(j) + k(j - 1))/2;
tau(n - 1) = m(n)/3;
end
j = j + 1;
n = n + 1;
else
m(n) = 0;
n = n + 1;
end
i = i + 1;
end
% Scrap the tail
accepted_means(j - 1:end)=[];
k(j - 1:end) = [];

Find zero crossing of sampled function [duplicate]

I have written a function in MATLAB to count the number of zero crossings given a vector of signal data. If I find a zero crossing, I also check whether the absolute difference between the two vector indices involved is greater than a threshold value - this is to try to reduce the influence of signal noise.
zc = [];
thresh = 2;
for i = 1:length(v)-1
if ( (v(i)>0 && v(i+1)<0) || (v(i)<0 && v(i+1)>0) ) && abs(v(i)-v(i+1)) >= thresh
zc = [zc; i+1];
end
end
zcCount = length(zc);
I used the vector from the zero crossings function here to test it: http://hips.seas.harvard.edu/content/count-zero-crossings-matlab
A = [-0.49840598306643,
1.04975509964655,
-1.67055867973620,
-2.01437026154355,
0.98661592496732,
-0.06048256273708,
1.19294080740269,
2.68558025885591,
0.85373360483580,
1.00554850567375];
It seems to work fine but is there a more efficient way of achieving the same result? E.g. on the above webpage, they simply use the following line to calculate zero crossings:
z=find(diff(v>0)~=0)+1;
Is there a way to incorporate the threshold check into something similarly efficient?
How about
zeroCrossIndex=diff(v>0)~=0
threshholdIndex = diff(v) >= thresh;
zcCount = sum(zeroCrossIndex & threshholdIndex)

Matlab: deciding whether a value is between the column entries of a nx2 matrix

I am writing a Matlab Tool and certain processes have to be automated.
I am running a for loop, in which some decisions need to be made. Here is a piece of my code:
DecisionMatrix = [0.2 0.4; 0.5 0.7];
Beta =0:pi/20:pi;
Span_Loc = 0.5*(1-cos(Beta))';
for i=1:length(Span_Loc)
Position = Span(i)
% Check Clean of High Lift
if Position >= DecisionMatrix(1,1) && Position <= DecisionMatrix(1,2)
% HighLift run code here
elseif Position >= DecisionMatrix(2,1) && Position <= DecisionMatrix(2,2)
else
% Clean run code here
end
end
Herein, DecisionMatrix is a variable size matrix which is nx2 always. What I want to do is to determine when the value of Position is between the entries of any row of DecisionMatrix. This should be easy when DecisionMatrix is a constant matrix (as shown above). However, this matrix has a variable number of rows.
Hence, how would you do this?
Thanks in advance!!
To determine when the value of Position (scalar) is between the entries of any row of DecisionMatrix (2-column matrix):
result = any(Position>=DecisionMatrix(:,1) & Position<=DecisionMatrix(:,2));
The above gives a logical result (true or false). If you need to know the indices of the rows that fulfill the condition:
result = find(Position>=DecisionMatrix(:,1) & Position<=DecisionMatrix(:,2));
You can fix your code by introducing another loop and coming out of it when you find the required row.
DecisionMatrix = [0.2 0.4; 0.5 0.7];
Beta =0:pi/20:pi;
Span_Loc = 0.5*(1-cos(Beta))';
for p=1:length(Span_Loc)
Position = Span(p);
for q=1:n
if Position >= DecisionMatrix(q,1) && Position <= DecisionMatrix(q,2)
%do what you want when the condition is true
break
end
end
end

Loop through data set until specific point - Matlab

I've run an experiment where a machine exerts a force on a bridge until it breaks. I need to cycle through my entire data set and calculate the toughness until I've hit the breaking point (fdValue1).
The toughness is calculated by summing up all the rectangles under my load vs. distance curve. (Basically the integral)
However, I have not been able to find a reasonable way of doing so and my current loop is an infinite loop and I'm not sure why.
%Initializing variables
bridge1Data = xlsread('Bridge1Data.xlsx', 'A2:C2971');
bridge2Data = xlsread('Bridge2Data.xlsx', 'A2:C1440');
bridge1Load = bridge1Data(:, 2);
bridge2Load = bridge2Data(:, 2);
bridge1Dist = bridge1Data(:, 3);
bridge2Dist = bridge2Data(:, 3);
[row1, col1] = size(bridge1Dist);
[row2, col2] = size(bridge2Dist);
bridge1Disp = zeros(row1, col1);
bridge2Disp = zeros(row2, col2);
fdValue1 = 0.000407350000000029;
&Main code
%Displacement
for k = 2:length(bridge1Dist)
bridge1Disp(k-1, 1) = bridge1Dist(k, 1) - bridge1Dist(k-1, 1);
end
%Max Load Bridge 1
maxLoad1 = 0;
for n = 1:length(bridge1Load)
for k = 1
if bridge1Load(n, k) > maxLoad1
maxLoad1 = bridge1Load(n, k);
end
end
end
%Cycle through data till failure, change proj data
totalRect1 = 0;
for j = 2:length(bridge1Disp)
while bridge1Disp(j, 1) ~= fdValue1
rectangle = (bridge1Disp(j, 1) - bridge1Disp(j-1, 1))*...
((bridge1Load(j, 1) + bridge1Load(j-1, 1))/2);
totalRect1 = totalRect1 + rectangle;
end
end
Basically, I make an array for the load and distance the machine pushes down on the bridge, assign a 'Failure Distance' value (fdValue) which should be used to determine when we stop calculating toughness. I then calculate displacement, calculate the maximum load. Then through the variable 'rectangle', calculate each rectangle and sum them all up in 'totalRect1', and use that to calculate the toughness by finding the area under the curve. Is anyone able to see why the loop is an infinite loop?
Thanks
The problem with the condition while bridge1Disp(j, 1) ~= fdValue1 is that you need to check for <= and not for (in)equality, since double numbers will almost never evaluate to be equal even if they seem so. To read more about this you can look here and also google for matlab double comparison. Generally it has something to do with precision issues.
Usually when checking for double equality you should use something like if abs(val-TARGET)<1E-4, and specify some tolerance which you are willing to permit.
Regardless,
You don't need to use loops for what you're trying to do. I'm guessing this comes from some C-programming habits, which are not required in MATLAB.
The 1st loop (Displacement), which computes the difference between every two adjacent elements can be replaced by the function diff like so:
bridge1Disp = diff(bridge1Dist);
The 2nd loop construct (Max Load Bridge 1), which retrieves the maximum element of bridge1Load can be replaced by the command max as follows:
maxLoad1 = max(bridge1Load);
For the last loop construct (Cycle ...) consider the functions I've mentioned above, and also find.
In the code section
%Cycle through data till failure, change proj data
totalRect1 = 0;
for j = 2:length(bridge1Disp)
while bridge1Disp(j, 1) ~= fdValue1
rectangle = (bridge1Disp(j, 1) - bridge1Disp(j-1, 1))*...
((bridge1Load(j, 1) + bridge1Load(j-1, 1))/2);
totalRect1 = totalRect1 + rectangle;
end
end
the test condition of the while loop is
bridge1Disp(j, 1) ~= fdValue1
nevertheless, in the while loop the value of bridge1Disp(j, 1) does not change so if at the first iteratiion of the while loop bridge1Disp(j, 1) is ~= fdValue1 the loop will never end.
Hope this helps.

Problems with Plotting Matlab Function

I am Beginner in Matlab, i would like to plot system concentration vs time plot at a certain time interval following is the code that i have written
%Input function of 9 samples with activity and time calibrated with Well
%counter value approx : 1.856 from all 9 input values of 3 patients
function c_o = Sample_function(td,t_max,A,B)
t =(0 : 100 :5000); % time of the sample post injection in mins
c =(0 : 2275.3 :113765);
A_max= max(c); %Max value of Concentration (Peak of the curve)
if (t >=0 && t <= td)
c_o(t)=0;
else if(td <=t && t<=t_max)
c_o(t)= A_max*(t-td);
else if(t >= t_max)
c_o(t)=(A(1)*exp(-B(1)*(t-t_max)))+(A(2)*exp(-B(2)*(t- t_max)))+...
(A(3)*exp(-B(3)*(t-t_max)));
end
fprintf('plotting Data ...\n');
hold on;
figure;
plot(c_o);
xlabel('Activity of the sample Ba/ml ');
ylabel('time of the sample in minutes');
title (' Input function: Activity sample VS time ');
pause;
end
I am getting following error
Operands to the || and && operators must be convertible to logical scalar values.
Error in Sample_function (line 18)
if (t >=0 && t <= td)
Kindly .Let me know if my logic is incorrect
Your t is not a single value to compare with 0 so it cannot evaluate to true or false.
You want to do this with logical indexing
c_o = zeros(size(t));
c_o(t>=0 & t<=td) = 0; % this line is actually redundant and unnecessary since we initialized the vector to zeros
c_o(t>td & t<=t_max) = A_max*(t(t>td & t<=t_max)-td);
c_o(t>t_max) = (A(1)*exp(-B(1)*(t(t>t_max)-t_max)))+(A(2)*exp(-B(2)*(t(t>t_max)- t_max)))...
+ (A(3)*exp(-B(3)*(t(t>t_max)-t_max)));
You could also make this a little prettier (and easier to read) by assigning the logical indexes to variables:
reg1 = (t>=0 & t<=td);
reg2 = (t>td & t<=t_max);
reg3 = (t>t_max);
Then, for instance, the second assignment becomes the much more readable:
c_o(reg2) = A_max*(t(reg2)-td);
t is written as a array of numbers. So, it can't be compared with a scalar value ex. 0.
Try it in a for loop
for i=1:length(t)
if (t(i) >=0 && t(i) <= td)
c_o(t(i))=0;
else if(td <=t(i) && t(i)<=t_max)
c_o(t(i)))= A_max*(t(i)-td);
else if(t(i) >= t_max)
c_o(t)=(A(1)*exp(-B(1)*(t(i)-t_max)))+(A(2)*exp(-B(2)*(t(i)- t_max)))...
+ (A(3)*exp(-B(3)*(t(i)-t_max)));
end
end