Dynamic-variable sized arrays in matlab - matlab

This code is working but needs to be improved for efficient calculations
anisLoc=[];% Variable sized array
PiezoLoc=[]; % Variable sized array
for i=1:q % Looking in all q
if ~isempty(LayerProp{i}.c)% if c is not empty then
anisLoc=[anisLoc,i];
c{i}=LayerProp{i}.c;
end
if ~isempty(LayerProp{i}.e) % if e is not empty then
% if LayerProp{i}.e(3,3)
PiezoLoc=[i;PiezoLoc];
e{i}=LayerProp{i}.e;
% end
end
end
anisLoc and Piezoloc are variable sized arrays. I set them on their maximum values q
but they change size and could not empty them after so they produce same answer from initial code!!
anisLoc=zeros(q,1);% Variable sized array
PiezoLoc=zeros(1,q);% Variable sized array
% This loop checks for specific input in data in all
for i=1:q % Looking in all q
if ~isempty(LayerProp{i}.c)% if c is not empty
anisLoc=[i;anisLoc];
c{i}=LayerProp{i}.c;
end
if ~isempty(LayerProp{i}.e) % if e is not empty
% if LayerProp{i}.e(3,3)
PiezoLoc=[PiezoLoc,i];
e{i}=LayerProp{i}.e;
% end
end
end

I believe this should be quite a bit faster:
anisLoc=zeros(q,1);% Variable sized array
PiezoLoc=zeros(1,q);% Variable sized array
% This loop checks for specific input in data in all
k = 0;
m = 0;
for ii=1:q % Looking in all q
if ~isempty(LayerProp{ii}.c)% if c is not empty
k = k + 1;
anisLoc(k)=ii;
c{ii}=LayerProp{ii}.c;
end
if ~isempty(LayerProp{ii}.e) % if e is not empty
% if LayerProp{ii}.e(3,3)
m = m + 1;
PiezoLoc(m) = ii;
e{ii}=LayerProp{ii}.e;
% end
end
anisLoc(k+1:end) = [];
PiezoLoc(m+1:end) = [];
end

Related

Store data during for and while loops

I need some help to store values in MATLAB using the following code:
for n = 1:some number
iter = 0;
while % condition
iter = iter + 1 ;
for k = 1:9
% call the integrator 9 times
[t,s] = ode113(#(t,y) eqns, [0 t{k}], X{k}, options);
% X{k} contains 9 initial conditions where each has 6 values
x{k} = s(:,1:6)
% x{k} = stores each arc from integration
x = 1x9 cell array where each cell is #rowsx6
end
end
state(n,:) = x;
end
The issue I am having is that state does not have all n values of x. For example, if n = 2, state is size 2x9 BUT only the x values for n = 2 is stored; nothing is saved for n = 1. I also tried: state{n}(iter,:) inside the while loop and it also only stores the x data from the last iteration. It appears that the variable state is being overwritten. Can someone please point me in the right direction?
got it! thanks for inputs. variable was not assigned properly.

How can I vectorize the loops of this function in Octave?

I want to be able to vectorize the for-loops of this function to then be able to parallelize it in octave. Can these for-loops be vectorized? Thank you very much in advance!
I attach the code of the function commenting on the start and end of each for-loop and if-else.
function [par]=pem_v(tsm,pr)
% tsm and pr are arrays of N by n. % par is an array of N by 8
tss=[27:0.5:32];
tc=[20:0.01:29];
N=size(tsm,1);
% main-loop
for ii=1:N
% I extract the rows in each loop because each one represents a sample
sst=tsm(ii,:); sst=sst'; %then I convert each sample to column vectors
pre=pr(ii,:); pre=pre';
% main-condition
if isnan(nanmean(sst))==1;
par(ii,1:8)=NaN;
else
% first sub-loop
for k=1:length(tss);
idxx=find(sst>=tss(k)-0.25 & sst<=tss(k)+0.25);
out(k)=prctile(pre(idxx),90);
end
% end first sub-loop
tp90=tss(find(max(out)==out));
% second sub-loop
for j=1:length(tc)
cond1=find(sst>=tc(j) & sst<=tp90);
cond2=find(sst>=tp90);
pem=zeros(length(sst),1);
A=[sst(cond1),ones(length(cond1),1)];
B=regress(pre(cond1),A);
pt90=B(1)*(tp90-tc(j));
AA=[(sst(cond2)-tp90)];
BB=regress(pre(cond2)-pt90,AA);
pem(cond1)=max(0,B(1)*(sst(cond1)-tc(j)));
pem(cond2)=max(0,(BB(1)*(sst(cond2)-tp90))+pt90);
clear A B AA BB;
E(j)=sqrt(nansum((pem-pre).^2)/length(pre));
clear pem;
end
% end second sub-loop
tcc=tc(find(E==min(E)));
% sub-condition
if(isempty(tcc)==1);
par(ii,1:9)=NaN;
else
cond1=find(sst>=tcc & sst<=tp90);
cond2=find(sst>=tp90);
pem=zeros(length(sst),1);
A=[sst(cond1),ones(length(cond1),1)];
B=regress(pre(cond1),A);
pt90=B(1)*(tp90-tcc);
AA=[sst(cond2)-tp90];
BB=regress(pre(cond2)-pt90,AA);
pem(cond1)=max(0,B(1)*(sst(cond1)-tcc));
pem(cond2)=max(0,(BB(1)*(sst(cond2)-tp90))+pt90);
RMSE=sqrt(nansum((pem-pre).^2)/length(pre));
% outputs
par(ii,1)=tcc;
par(ii,2)=tp90;
par(ii,3)=B(1);
par(ii,4)=BB(1);
par(ii,5)=RMSE;
par(ii,6)=nanmean(sst);
par(ii,7)=nanmean(pre);
par(ii,8)=nanmean(pem);
end
% end sub-condition
clear pem pre sst RMSE BB B tp90 tcc
end
% end main-condition
end
% end main-loop
You haven't given any example inputs, so I've created some like so:
N = 5; n = 800;
tsm = rand(N,n)*5+27; pr = rand(N,n);
Then, before you even consider vectorising your code, you should keep 4 things in mind...
Avoid calulating the same thing (like the size of a vector) every loop, instead do it before looping
Pre-allocate arrays where possible (declare them as zeros/NaNs etc)
Don't use find to convert logical indices into linear indices, there is no need and it will slow down your code
Don't repeatedly use clear, especially many times within loops. It is slow! Instead, use pre-allocation to ensure the variables are as you expect each loop.
Using the above random inputs, and taking account of these 4 things, the below code is ~65% quicker than your code. Note: this is without even doing any vectorising!
function [par]=pem_v(tsm,pr)
% tsm and pr are arrays of N by n.
% par is an array of N by 8
tss=[27:0.5:32];
tc=[20:0.01:29];
N=size(tsm,1);
% Transpose once here instead of every loop
tsm = tsm';
pr = pr';
% Pre-allocate memory for output 'par'
par = NaN(N, 8);
% Don't compute these every loop, do it before the loop.
% numel simpler than length for vectors, and size is clearer still
ntss = numel(tss);
nsst = size(tsm,1);
ntc = numel(tc);
npr = size(pr, 1);
for ii=1:N
% Extract the columns in each loop because each one represents a sample
sst=tsm(:,ii);
pre=pr(:,ii);
% main-condition. Previously isnan(nanmean(sst))==1, but that's only true if all(isnan(sst))
% We don't need to assign par(ii,1:8)=NaN since we initialised par to a matrix of NaNs
if ~all(isnan(sst));
% first sub-loop, initialise 'out' first
out = zeros(1, ntss);
for k=1:ntss;
% Don't use FIND on an indexing vector. Use the logical index raw, it's quicker
idxx = (sst>=tss(k)-0.25 & sst<=tss(k)+0.25);
% We need a check that some values of idxx are true, otherwise prctile will error.
if nnz(idxx) > 0
out(k) = prctile(pre(idxx), 90);
end
end
% Again, no need for FIND, just reduces speed. This is a theme...
tp90=tss(max(out)==out);
for jj=1:ntc
cond1 = (sst>=tc(jj) & sst<=tp90);
cond2 = (sst>=tp90);
% Use nnz (numer of non-zero) instead of length, since cond1 is now a logical vector of all elements
A = [sst(cond1),ones(nnz(cond1),1)];
B = regress(pre(cond1), A);
pt90 = B(1)*(tp90-tc(jj));
AA = [(sst(cond2)-tp90)];
BB = regress(pre(cond2)-pt90,AA);
pem=zeros(nsst,1);
pem(cond1) = max(0, B(1)*(sst(cond1)-tc(jj)));
pem(cond2) = max(0, (BB(1)*(sst(cond2)-tp90))+pt90);
E(jj) = sqrt(nansum((pem-pre).^2)/npr);
end
tcc = tc(E==min(E));
if ~isempty(tcc);
cond1 = (sst>=tcc & sst<=tp90);
cond2 = (sst>=tp90);
A = [sst(cond1),ones(nnz(cond1),1)];
B = regress(pre(cond1),A);
pt90 = B(1)*(tp90-tcc);
AA = [sst(cond2)-tp90];
BB = regress(pre(cond2)-pt90,AA);
pem = zeros(length(sst),1);
pem(cond1) = max(0, B(1)*(sst(cond1)-tcc));
pem(cond2) = max(0, (BB(1)*(sst(cond2)-tp90))+pt90);
RMSE = sqrt(nansum((pem-pre).^2)/npr);
% Outputs, which we might as well assign all at once!
par(ii,:)=[tcc, tp90, B(1), BB(1), RMSE, ...
nanmean(sst), nanmean(pre), nanmean(pem)];
end
end
end

Looping through to find max value without using max()

I'm trying to iterate in MATLAB (not allowed to use in built functions) to find the maximum value of each row in a certain matrix. I've been able to find the max value of the whole matrix but am unsure about isolating the row and finding the max value (once again without using max()).
My loop currently looks like this:
for i = 1:size(A, 1)
for j = 1:size(A, 2)
if A(i, j) > matrix_max
matrix_max = A(i, j);
row = i;
column = j;
end
end
end
You need a vector of results, not a single value. Note you could initialise this to zero. Don't initialise to zero unless you know you only have positive values. Instead, initialise to -inf using -inf*ones(...), as all values are greater than negative infinity. Or (see the bottom code block) initialise to the first column of A.
% Set up results vector, same number of rows as A, start at negative infinity
rows_max = -inf*ones(size(A,1),1);
% Set up similar to track column number. No need to track row number as doing each row!
col_nums = zeros(size(A,1),1);
% Loop through. i and j = sqrt(-1) by default in MATLAB, use ii and jj instead
for ii = 1:size(A,1)
for jj = 1:size(A,2)
if A(ii,jj) > rows_max(ii)
rows_max(ii) = A(ii,jj);
col_nums(ii) = jj;
end
end
end
Note that if vectorisation doesn't violate your "no built-ins" rule (it should be fine, it's making the most of the MATLAB language), then you can remove the outer (row) loop
rows_max = -inf*ones(size(A,1),1);
col_nums = zeros(size(A,1),1);
for jj = 1:size(A,2)
% Get rows where current column is larger than current max stored in row_max
idx = A(:,jj) > rows_max;
% Store new max values
rows_max(idx) = A(idx,jj);
% Store new column indices
col_nums(idx) = jj;
end
Even better, you can cut your loop short by 1, and initialise to the first column of A.
rows_max = A(:,1); % Set current max to the first column
col_nums = ones(size(A,1),1); % ditto
% Loop from 2nd column now that we've already used the first column
for jj = 2:size(A,2)
idx = A(:,jj) > rows_max;
rows_max(idx) = A(idx,jj);
col_nums(idx) = jj;
end
You can modified it likes the following to get each max for each row:
% initialize
matrix_max = zeros(size(A,1),1);
columns = zeros(size(A,1),1);
% find max
for i = 1:size(A, 1)
matrix_max(i) = A(i,1);
columns(i) = 1;
for j = 2:size(A, 2)
if A(i, j) > matrix_max(i)
matrix_max(i) = A(i, j);
columns(i) = j;
end
end
end

parfor doesn't consider information about vectors which are used in it

This is a part of my code in Matlab. I tried to make it parallel but there is an error:
The variable gax in a parfor cannot be classified.
I know why the error occurs. because I should tell Matlab that v is an incresing vector which doesn't contain repeated elements. Could anyone help me to use this information to parallelize the code?
v=[1,3,6,8];
ggx=5.*ones(15,14);
gax=ones(15,14);
for m=v
if m > 1
parfor j=1:m-1
gax(j,m-1) = ggx(j,m-1);
end
end
if m<nn
parfor jo=m+1:15
gax(jo,m) = ggx(jo,m);
end
end
end
Optimizing a code should be closely related to its purpose, especially when you use parfor. The code you wrote in the question can be written in a much more efficient way, and definitely, do not need to be parallelized.
However, I understand that you tried to simplify the problem, just to get the idea of how to slice your variables, so here is a fixed version the can run with parfor. But this is surely not the way to write this code:
v = [1,3,6,8];
ggx = 5.*ones(15,14);
gax = ones(15,14);
nn = 5;
for m = v
if m > 1
temp_end = m-1;
temp = ggx(:,temp_end);
parfor ja = 1:temp_end
gax(ja,temp_end) = temp(ja);
end
end
if m < nn
temp = ggx(:,m);
parfor jo = m+1:15
gax(jo,m) = temp(jo);
end
end
end
A vectorized implementation will look like this:
v = [1,3,6,8];
ggx = 5.*ones(15,14);
gax = ones(15,14);
nn = 5;
m1 = v>1; % first condition with logical indexing
temp = v(m1)-1; % get the values from v
r = ones(1,sum(temp)); % generate a vector of indicies
r(cumsum(temp)) = -temp+1; % place the reseting locations
r = cumsum(r); % calculate the indecies
r(cumsum(temp)) = temp; % place the ending points
c = repelem(temp,temp); % create an indecies vector for the columns
inds1 = sub2ind(size(gax),r,c); % convert the indecies to linear
mnn = v<nn; % second condition with logical indexing
temp = v(mnn)+1; % get the values from v
r_max = size(gax,1); % get the height of gax
r_count = r_max-temp+1; % calculate no. of rows per value in v
r = ones(1,sum(r_count)); % generate a vector of indicies
r([1 r_count(1:end-1)+1]) = temp; % set the t indicies
r(cumsum(r_count)+1) = -(r_count-temp)+1; % place the reseting locations
r = cumsum(r(1:end-1)); % calculate the indecies
c = repelem(temp-1,r_count); % create an indecies vector for the columns
inds2 = sub2ind(size(gax),r,c); % convert the indecies to linear
gax([inds1 inds2]) = ggx([inds1 inds2]); % assgin the relevant values
This is indeed quite complicated, and not always necessary. A good thing to remember, though, is that nested for loop are much slower than a single loop, so in some cases (depend on the size of the output), this will may be the fastest solution:
for m = v
if m > 1
gax(1:m-1,m-1) = ggx(1:m-1,m-1);
end
if m<nn
gax(m+1:15,m) = ggx(m+1:15,m);
end
end

Record the number of iterations in a for loop into a Matlab table

I am trying to keep a track of all the calculations happening under 3 for loops. The data is too big therefore it is hard to keep the track of the data. Hence, I would like to construct a table which will record the number of iterations taking place inside every for loop.
The code:
for i = 1:4
% Calculations
i
for j = 1:3
% Calculations
j
for k = 1:3
% Calculations
k
end
end
end
So, the tabular output which I am expecting is like this,
Can anybody please help me in achieving this task.
You could use ndgrid to create all permutations of your i, j, and k values and then have a single for loop that loops through all permutations.
[ii, jj, kk] = ndgrid(1:4, 1:3, 1:3);
% Pre-allocate your results matrix
results = zeros(size(ii));
for n = 1:numel(ii)
% Do calculation with ii(n), jj(n), kk(n)
results(n) = ii(n) + jj(n) + kk(n);
end
Now if you want to know what the ii, jj, or kk values were for a particular entry in results, you can just index into all variables the same way.
result_of_interest = results(100);
i_of_interest = ii(100);
j_of_interest = jj(100);
k_of_interest = kk(100);
If you really need tabular output, you can transform ii, jj, and kk into your table.
data = cat(2, ii(:), jj(:), kk(:))';
You can try the following code, where you declare the dimension of each loop at the beginning, and allocate a track matrix.
ni=3
nj=4
nk=5
track = zeros(3,ni*nj*nk);
offset = 1
for i = 1:ni
% Calculations
i
for j = 1:nj
% Calculations
j
for k = 1:nk
% Calculations
k
track(1,offset) = i;
track(2,offset) = j;
track(3,offset) = k;
offset = offset + 1;
end
end
end