Summing elements within an eval statement, in a for loop - matlab

I have written a complete code that runs in MATLAB, but outputs a slightly incorrect result. I need to get the following:
utotal
where
utotal = S1plot + S2plot + ...
until the digit equals (N/2) + 1, where N is even. If N = 10, say, the digit would be 6.
Then I need to evaluate utotal within the script. How can I achieve this?
This is what I have so far:
N = 10;
for alpha = 1:(N/2+1)
eval(['utotal = sum(S' num2str(alpha) 'plot);'])
end
but it doesn't work because it evaluates the following:
utotal = sum(S1plot);
utotal = sum(S2plot);
utotal = sum(S3plot);
utotal = sum(S4plot);
utotal = sum(S5plot);
utotal = sum(S6plot);
Thanks in advance for help.

Here's a workaround you can use for now. Note that this is extremely bad coding practice and the difficulty you're having now is only one of the reasons you shouldn't do it.
%// Generate random data
S1plot = randi(100,51,5);
S2plot = randi(100,51,5);
S3plot = randi(100,51,5);
S4plot = randi(100,51,5);
S5plot = randi(100,51,5);
S6plot = randi(100,51,5);
N = 10;
%// Put individual matrices into 3D matrix S
%// To access matrix Snplot, use S(:,:,n)
%// This is the format these variables should have been in in the first place
for alpha = 1:(N/2+1)
eval(['S(:,:,' num2str(alpha) ') = (S' num2str(alpha) 'plot);'])
end
%// Now sum along the third dimension
utotal = sum(S,3);

See the comments by #beaker. This solution does not do what the OP wants.
I haven't tested this but it should work.
N=10;
for alpha = 1:(N/2+1)
allSum = [allSum 'sum(S' num2str(alpha) 'plot)+'];
end
allSum(end)=';';
eval(['utotal = ' allSum]);

N = 10;
Result =0;
for alpha = 1:(N/2+1)
Result = Result + num2str(alpha)
end
eval(['utotal = sum(S' Result 'plot);'])

Related

Incrementing array within parfor?

I am calculating sort of a histogram based on the distance between a pair of points in 3d space:
numBins = 20;
binWidth = 2.5;
pop = zeros(1,numBins);
parfor j=1:particles
r1 = coords(j,:);
for k=j+1:particles
r2 = coords(k,:);
d = norm(r1-r2);
ind = ceil(d/binWidth);
pop(ind) = pop(ind) + 1;
end
end
This, expectedly, results in
Error: The variable pop in a parfor cannot be classified.
I understand the problem, but I am confused as to how can I solve it.
In principle, what could be done is to have n copies of pop = zeroes(1,numBins) be sent to each of n workers, and joined by adding each copy together at the end of computation. How can I do this here? Or is there another, more standard way of solving the problem?
There is two things that don't work in your code:
1) for k = j+1:particles
In a parfor a nested loop should have fixed bound.
2) pop(ind)
Matlab is afraid that the for-loop order matters and display an error message. Even if, in your specific case, the order doesn't matters (But matlab is not smart enough to know that).
The solution, Linearization:
%Dummy data
numBins = 20;
binWidth = 2.5;
particles = 10;
coords = rand(10,2)*40;
%Initialization
pop = zeros(1,numBins);
parfor j=1:particles
r1 = coords(j,:)
r2 = coords((j+1):end,:)
d = sqrt(sum([r1-r2].^2,2)) % compute each norm at the same time !
pop = pop + histcounts(ceil(d/binWidth),0:numBins)
end
You can create a function that computes the inner loop and use a handle to it in the parfor (I didn't tested it but I think it should work according to the documentation):
function pop = hist_comp(pop,j,particles,coords,binWidth)
r1 = coords(j,:);
for k=j+1:particles
r2 = coords(k,:);
d = norm(r1-r2);
ind = ceil(d/binWidth);
pop(ind) = pop(ind) + 1;
end
end
numBins = 20;
binWidth = 2.5;
particles = 10;
coords = rand(10,2)*5;
pop = zeros(1,numBins);
f = #(pop,j) hist_comp(pop,j,particles,coords,binWidth);
parfor j=1:particles
pop = f(pop,j);
end

average bins along a dimension of a nd array in matlab

To compute the mean of every bins along a dimension of a nd array in matlab, for example, average every 10 elements along dim 4 of a 4d array
x = reshape(1:30*30*20*300,30,30,20,300);
n = 10;
m = size(x,4)/10;
y = nan(30,30,20,m);
for ii = 1 : m
y(:,:,:,ii) = mean(x(:,:,:,(1:n)+(ii-1)*n),4);
end
It looks a bit silly. I think there must be better ways to average the bins?
Besides, is it possible to make the script applicable to general cases, namely, arbitray ndims of array and along an arbitray dim to average?
For the second part of your question you can use this:
x = reshape(1:30*30*20*300,30,30,20,300);
dim = 4;
n = 10;
m = size(x,dim)/10;
y = nan(30,30,20,m);
idx1 = repmat({':'},1,ndims(x));
idx2 = repmat({':'},1,ndims(x));
for ii = 1 : m
idx1{dim} = ii;
idx2{dim} = (1:n)+(ii-1)*n;
y(idx1{:}) = mean(x(idx2{:}),dim);
end
For the first part of the question here is an alternative using cumsum and diff, but it may not be better then the loop solution:
function y = slicedmean(x,slice_size,dim)
s = cumsum(x,dim);
idx1 = repmat({':'},1,ndims(x));
idx2 = repmat({':'},1,ndims(x));
idx1{dim} = slice_size;
idx2{dim} = slice_size:slice_size:size(x,dim);
y = cat(dim,s(idx1{:}),diff(s(idx2{:}),[],dim))/slice_size;
end
Here is a generic solution, using the accumarray function. I haven't tested how fast it is. There might be some room for improvement though.
Basically, accumarray groups the value in x following a matrix of customized index for your question
x = reshape(1:30*30*20*300,30,30,20,300);
s = size(x);
% parameters for averaging
dimAv = 4;
n = 10;
% get linear index
ix = (1:numel(x))';
% transform them to a matrix of index per dimension
% this is a customized version of ind2sub
pcum = [1 cumprod(s(1:end-1))];
sub = zeros(numel(ix),numel(s));
for i = numel(s):-1:1,
ixtmp = rem(ix-1, pcum(i)) + 1;
sub(:,i) = (ix - ixtmp)/pcum(i) + 1;
ix = ixtmp;
end
% correct index for the given dimension
sub(:,dimAv) = floor((sub(:,dimAv)-1)/n)+1;
% run the accumarray to compute the average
sout = s;
sout(dimAv) = ceil(sout(dimAv)/n);
y = accumarray(sub,x(:), sout, #mean);
If you need a faster and memory efficient operation, you'll have to write your own mex function. It shouldn't be so difficult, I think !

How to vectorize a matlab script converting a 3d matrix to a single vector?

I am writing a graphical representation of numerical stability of differential operators and I am having trouble removing a nested for loop. The code loops through all entries in the X,Y, plane and calculates the stability value for each point. This is done by finding the roots of a polynomial of a size dependent on an input variable (length of input vector results in a polynomial 3d matrix of size(m,n,(lenght of input vector)). The main nested for loop is as follows.
for m = 1:length(z2)
for n = 1:length(z1)
pointpoly(1,:) = p(m,n,:);
r = roots(pointpoly);
if isempty(r),r=1e10;end
z(m,n) = max(abs(r));
end
end
The full code of an example numerical method (Trapezoidal Rule) is as follows. Any and all help is appreciated.
alpha = [-1 1];
beta = [.5 .5];
Wind = 2;
Wsize = 500;
if numel(Wind) == 1
Wind(4) = Wind(1);
Wind(3) = -Wind(1);
Wind(2) = Wind(4);
Wind(1) = Wind(3);
end
if numel(Wsize) == 1
Wsize(2) = Wsize;
end
z1 = linspace(Wind(1),Wind(2),Wsize(1));
z2 = linspace(Wind(3),Wind(4),Wsize(2));
[Z1,Z2] = meshgrid(z1,z2);
z = Z1+1i*Z2;
p = zeros(Wsize(2),Wsize(1),length(alpha));
for n = length(alpha):-1:1
p(:,:,(length(alpha)-n+1)) = alpha(n)-z*beta(n);
end
for m = 1:length(z2)
for n = 1:length(z1)
pointpoly(1,:) = p(m,n,:);
r = roots(pointpoly);
if isempty(r),r=1e10;end
z(m,n) = max(abs(r));
end
end
figure()
surf(Z1,Z2,z,'EdgeColor','None');
caxis([0 2])
cmap = jet(255);
cmap((127:129),:) = 0;
colormap(cmap)
view(2);
title(['Alpha Values (',num2str(alpha),') Beta Values (',num2str(beta),')'])
EDIT::
I was able to remove one of the for loops using the reshape command. So;
for m = 1:length(z2)
for n = 1:length(z1)
pointpoly(1,:) = p(m,n,:);
r = roots(pointpoly);
if isempty(r),r=1e10;end
z(m,n) = max(abs(r));
end
end
has now become
gg = reshape(p,[numel(p)/length(alpha) length(alpha)]);
r = zeros(numel(p)/length(alpha),1);
for n = 1:numel(p)/length(alpha)
temp = roots(gg(n,:));
if isempty(temp),temp = 0;end
r(n,1) = max(abs(temp));
end
z = reshape(r,[Wsize(2),Wsize(1)]);
This might be one for loop, but I am still going through the same number of elements. Is there a way to use the roots command on all of my rows at the same time?

What is the fastest way of appending an element to an array?

This is a follow-up question to How to append an element to an array in MATLAB? That question addressed how to append an element to an array. Two approaches are discussed there:
A = [A elem] % for a row array
A = [A; elem] % for a column array
and
A(end+1) = elem;
The second approach has the obvious advantage of being compatible with both row and column arrays.
However, this question is: which of the two approaches is fastest? My intuition tells me that the second one is, but I'd like some evidence for or against that. Any idea?
The second approach (A(end+1) = elem) is faster
According to the benchmarks below (run with the timeit benchmarking function from File Exchange), the second approach (A(end+1) = elem) is faster and should therefore be preferred.
Interestingly, though, the performance gap between the two approaches is much narrower in older versions of MATLAB than it is in more recent versions.
R2008a
R2013a
Benchmark code
function benchmark
n = logspace(2, 5, 40);
% n = logspace(2, 4, 40);
tf = zeros(size(n));
tg = tf;
for k = 1 : numel(n)
x = rand(round(n(k)), 1);
f = #() append(x);
tf(k) = timeit(f);
g = #() addtoend(x);
tg(k) = timeit(g);
end
figure
hold on
plot(n, tf, 'bo')
plot(n, tg, 'ro')
hold off
xlabel('input size')
ylabel('time (s)')
leg = legend('y = [y, x(k)]', 'y(end + 1) = x(k)');
set(leg, 'Location', 'NorthWest');
end
% Approach 1: y = [y, x(k)];
function y = append(x)
y = [];
for k = 1 : numel(x);
y = [y, x(k)];
end
end
% Approach 2: y(end + 1) = x(k);
function y = addtoend(x)
y = [];
for k = 1 : numel(x);
y(end + 1) = x(k);
end
end
How about this?
function somescript
RStime = timeit(#RowSlow)
CStime = timeit(#ColSlow)
RFtime = timeit(#RowFast)
CFtime = timeit(#ColFast)
function RowSlow
rng(1)
A = zeros(1,2);
for i = 1:1e5
A = [A rand(1,1)];
end
end
function ColSlow
rng(1)
A = zeros(2,1);
for i = 1:1e5
A = [A; rand(1,1)];
end
end
function RowFast
rng(1)
A = zeros(1,2);
for i = 1:1e5
A(end+1) = rand(1,1);
end
end
function ColFast
rng(1)
A = zeros(2,1);
for i = 1:1e5
A(end+1) = rand(1,1);
end
end
end
For my machine, this yields the following timings:
RStime =
30.4064
CStime =
29.1075
RFtime =
0.3318
CFtime =
0.3351
The orientation of the vector does not seem to matter that much, but the second approach is about a factor 100 faster on my machine.
In addition to the fast growing method pointing out above (i.e., A(k+1)), you can also get a speed increase from increasing the array size by some multiple, so that allocations become less as the size increases.
On my laptop using R2014b, a conditional doubling of size results in about a factor of 6 speed increase:
>> SO
GATime =
0.0288
DWNTime =
0.0048
In a real application, the size of A would needed to be limited to the needed size or the unfilled results filtered out in some way.
The Code for the SO function is below. I note that I switched to cos(k) since, for some unknown reason, there is a large difference in performance between rand() and rand(1,1) on my machine. But I don't think this affects the outcome too much.
function [] = SO()
GATime = timeit(#GrowAlways)
DWNTime = timeit(#DoubleWhenNeeded)
end
function [] = DoubleWhenNeeded()
A = 0;
sizeA = 1;
for k = 1:1E5
if ((k+1) > sizeA)
A(2*sizeA) = 0;
sizeA = 2*sizeA;
end
A(k+1) = cos(k);
end
end
function [] = GrowAlways()
A = 0;
for k = 1:1E5
A(k+1) = cos(k);
end
end

Handle function implicitly accounting for independent variables

I have that
clc, clear all, close all
tic
k1 = 1E-02:0.1:1E+02;
k2 = 1E-02:0.1:1E+02;
k3 = 1E-02:0.1:1E+02;
k = sqrt(k1.^2+k2.^2+k3.^2);
c = 1.476;
gamma = 3.9;
colors = {'b'};
Ek = (1.453*k.^4)./((1 + k.^2).^(17/6));
E = #(k) (1.453*k.^4)./((1 + k.^2).^(17/6));
E_int = zeros(1,numel(k));
E_int(1) = 1.5;
for i = 2:numel(k)
if k(i) < 400
E_int(i) = E_int(i-1) - integral(E,k(i-1),k(i));
elseif k(i) > 400
E_int(i) = 2.180/(k(i)^(2/3));
end %end if
end %end i
beta = (c*gamma)./(k.*sqrt(E_int));
figure
plot(k,beta,colors{1})
count = 0;
%F_11 = zeros(1,numel(k1));
F_33 = zeros(1,numel(k1));
Afterwards, I should calculate F_33 as
for i = 1:numel(k1)
count = count + 1;
phi_33 = #(k2,k3) (1.453./(4.*pi)).*(((k1(i)^2+k2.^2+(k3 + beta(i).*k1(i)).^2).^2)./((k1(i)^2+k2.^2+k3.^2).^2)).*((k1(i)^2+k2.^2)./((1+k1(i)^2+k2.^2+(k3+beta(i).*k1(i)).^2).^(17/6)));
F_33(count) = 4*integral2(phi_33,0,1000,0,1000);
end
Now let's come to my question. I know from a paper that:
k = sqrt(k1.^2+k2.^2+k3.^2);
k30 = k3 + beta.*k1;
k0 = sqrt(k1.^2+k2.^2+k30.^2);
E_k0 = 1.453.*(k0.^4./((1+k0.^2).^(17/6)));
Therefore the expression for phi_33 would result in
phi_33 = (E_k0./(4*pi.*(k.^4))).*(k1.^2+k2.^2);
The question is: how can I make use of this final expression insted of the long one I'm using at the moment (within the for loop)?
The last expression for phi_33 is easier to handle (especially because of reckless mistakes in writing the former) and it would "pass by reference" (k2,k3), which are the independent variables.
Any hint is more than welcome.
Best regards,
fpe
If I understand you correctly you want to use the new expression in exactly the same way as the old one-liner. You just want to divide your function phi33 into parts because of readability.
You could do this by placing the expression in a separate function taking all values needed for the calculation. Using your old expression exactly this would look something like this:
function phi_33 = phi_33_old(k1,k2,k3,beta,i)
phi_33 = (1.453./(4.*pi)).*(((k1(i)^2+k2.^2+(k3 + beta(i).*k1(i)).^2).^2)./((k1(i)^2+k2.^2+k3.^2).^2)).*((k1(i)^2+k2.^2)./((1+k1(i)^2+k2.^2+(k3+beta(i).*k1(i)).^2).^(17/6)));
end
This function could then be called inside your for-loop like this.
phi_33_test = #(k2,k3) phi_33_old(k1,k2,k3,beta,i);
Using the same style, a new function could be defined as follows.
function phi_33 = phi_33_new(k1,k2,k3,beta,i)
k = sqrt(k1.^2+k2.^2+k3.^2);
k30 = k3 + beta.*k1;
k0 = sqrt(k1.^2+k2.^2+k30.^2);
E_k0 = 1.453.*(k0.^4./((1+k0.^2).^(17/6)));
phi_33_allValues = (E_k0./(4*pi.*(k.^4))).*(k1.^2+k2.^2);
phi_33 = phi_33_allValues(i);
end
Note that here all values of phi_33 are calculated and then the ith value is selected. It is written in this way only to show the similarity to the old case. This new function can now be called inside the for-loop in the same way as the old one.
phi_33 = #(k2,k3) phi_33_new(k1,k2,k3,beta,i);