Create a dynamic matlab function using arrayfun or some other way - matlab

I am looking for a way to create a dynamic functions in length with multiple inputs, tried doing it this way but it seems over kill am guessing slow too, is there a better way of doing this to be compact and fast.
Problem want to create a cos with inputs from nX3 matrix sum(A*cos(W*t + F)) where A, W, F are columns from the matrix sum them all up then divide by its norm. Here is what I have so far .
% example input can have n rows
A = [1 2 3; 4 5 6];
item.fre = 0;
item.amp = 0;
item.pha = 0;
items = repmat(item, size(A, 1), 1);
for i = 1:size(A, 1)
items(i).fre = A(i, 1);
items(i).amp = A(i, 2);
items(i).pha = A(i, 3);
end
fun = #(t) sum(cell2mat(arrayfun(#(i) i.amp*cos(2*pi*t*i.fre + i.pha), items, 'un',0)));
% test run all this steps just to get a norm vector
time = 1:10;
testSignal = fun(time);
testSignal = testSignal/norm(testSignal);

I agree with a comment made by Cris Luengo to forget about anonymous functions and structures, you should try the simplest solution first. It looks like you're trying to add cosines with different amplitudes, frequencies, and phases. Here is how I would do it to make it very readable
A = [1 2 3; 4 5 6];
freq = A(:, 1);
amp = A(:, 2);
phase = A(:, 3);
time = 1:.01:10;
testSignal = zeros(size(time));
for i = 1:length(freq)
testSignal = testSignal + amp(i) * cos(2*pi*freq(i) * time + phase(i));
end
testSignal = testSignal/norm(testSignal);
plot(time, testSignal)
grid on
You could eliminate the amp, phase, and freq variables by accessing the columns of A directly, but that would make the code much less readable.

Related

Take a random draw of all possible pairs of indices in Matlab

Consider a Matlab matrix B which lists all possible unordered pairs (without repetitions) from [1 2 ... n]. For example, if n=4,
B=[1 2;
1 3;
1 4;
2 3;
2 4;
3 4]
Note that B has size n(n-1)/2 x 2
I want to take a random draw of m rows from B and store them in a matrix C. Continuing the example above, I could do that as
m=2;
C=B(randi([1 size(B,1)],m,1),:);
However, in my actual case, n=371293. Hence, I cannot create B and, then, run the code above to obtain C. This is because storing B would require a huge amount of memory.
Could you advise on how I could proceed to create C, without having to first store B? Comments on a different question suggest to
Draw at random m integers between 1 and n(n-1)/2.
I=randi([1 n*(n-1)/2],m,1);
Use ind2sub to obtain C.
Here, I'm struggling to implement the second step.
Thanks to the comments below, I wrote this
n=4;
m=10;
coord=NaN(m,2);
R= randi([1 n^2],m,1);
for i=1:m
[cr, cc]=ind2sub([n,n],R(i));
if cr>cc
coord(i,1)=cc;
coord(i,2)=cr;
elseif cr<cc
coord(i,1)=cr;
coord(i,2)=cc;
end
end
coord(any(isnan(coord),2),:) = []; %delete NaN rows from coord
I guess there are more efficient ways to implement the same thing.
You can use the function named myind2ind in this post to take random rows of all possible unordered pairs without generating all of them.
function [R , C] = myind2ind(ii, N)
jj = N * (N - 1) / 2 + 1 - ii;
r = (1 + sqrt(8 * jj)) / 2;
R = N -floor(r);
idx_first = (floor(r + 1) .* floor(r)) / 2;
C = idx_first-jj + R + 1;
end
I=randi([1 n*(n-1)/2],m,1);
[C1 C2] = myind2ind (I, n);
If you look at the odds, for i=1:n-1, the number of combinations where the first value is equal to i is (n-i) and the total number of cominations is n*(n-1)/2. You can use this law to generate the first column of C. The values of the second column of C can then be generated randomly as integers uniformly distributed in the range [i+1, n]. Here is a code that performs the desired tasks:
clc; clear all; close all;
% Parameters
n = 371293; m = 10;
% Generation of C
R = rand(m,1);
C = zeros(m,2);
s = 0;
t = n*(n-1)/2;
for i=1:n-1
if (i<n-1)
ind_i = R>=s/t & R<(s+n-i)/t;
else % To avoid rounding errors for n>>1, we impose (s+n-i)=t at the last iteration (R<(s+n-i)/t=1 always true)
ind_i = R>=s/t;
end
C(ind_i,1) = i;
C(ind_i,2) = randi([i+1,n],sum(ind_i),1);
s = s+n-i;
end
% Display
C
Output:
C =
84333 266452
46609 223000
176395 328914
84865 94391
104444 227034
221905 302546
227497 335959
188486 344305
164789 266497
153603 354932
Good luck!

MATLAB: Indexing into a matrix with a moving window

I am looking for an effective way in order to index into a matrix using a moving window.
% Create logical matrix
startRows = repmat([10,10,30,10,40], 1, 3);
logicalMat = true(100, 5, 3);
logicalMat((1:100)' < startRows) = 0;
% Create source matrix
window = 10;
sourceMat = randi(10, [100, 5, 3]);
sourceMat((1:100)' < startRows-(window-1)) = NaN;
% Compute target matrix: For every cell in targetMat, where logicalMat is true,
% compute the product of multVec and the previous 10 rows.
targetMat = nan(100,5);
multVec = randi(10, [1, 10]);
% For instance, targetMat(10,1) should be the product of multVec and sourceMat(1:10,1).
% targetMat(11,1) = multVec * sourceMat(2:11, 1) and so on...
% targetMat(30,3) = multVec * sourceMat(21:30, 3) and so on...
% I am interested in the technique how to address the "moving window".
One possible solution might be using a for-loop. However, I would like to know whether there exists a more elegant solution. Also, if I expand the example into the third dimension, I would need a third for-loop. Is it possible to vectorize this problem?
% Possible solution using loop
[nRows, nCols, nPages] = size(sourceMat);
for p = 1 : nPages
for c = 1 : nCols
for r = startRows(c) : nRows
targetMat(r, c, p) = multVec * sourceMat((r - window + 1) : r, c, p);
end
end
end
As stated in the comments, it's really hard to advise without more specifics, but you could try try the filter2 or imfilter functions for this type of thing. With more description of what you are trying to achieve and why, someone may be able to give a more specific answer

How to fill columns of a matrix with random numbers of specific range of a 100*5 matrix?

I've a matrix of order 100*5 . Now the objective is to fill each columns of the matrix with random integer within a specific range. Now the problem is for every column the range of the random number changes. For instance, for the first column, the range is 1 to 100 , for the second its -10 to 1 and so on till 5th column.
This is what I've tried:
b = [0,100;-10,1;0,1;-1,1;10,20]
range = b(:,2) - b(:,1)
offset = b(:,1)
A = round(rand(100,5) * range - offset)
which is from this question. However this generates an error,
Error using * Inner matrix dimensions must agree.
What's possibly causing this and how to resolve it ?
lets bsxfun this thing!
A = round(bsxfun(#minus,bsxfun(#times,rand(100,5) ,range'), offset'))
As an alternative solution, you could use repmat to complete what you already had:
b = [0, 100; -10, 1; 0, 1; -1, 1; 10, 20].';
rng = b(2, :) - b(1, :);
ofst = b(1, :);
A = round(rand(100,5) .* repmat(rng, 100, 1) + repmat(ofst, 100, 1));
You don't have to define rng or ofst, and this can be simply written as:
A = round(rand(10,5) .* repmat(diff(b), 10, 1) + repmat(b(1,:), 10, 1));
Out of curiousity I wrote this quick benchmark* to compare to Ander's bsxfun method. It appears that bsxfun has some initial overhead which means for 5 columns (test other cases yourself) and less than a few thousand rows, repmat is quicker. Above this, the creation of additional large arrays by repmat probably causes a slow down, and we see bsxfun is much quicker.
For future readers if this doesn't apply to you: with broadcasting introduced from R2016b you may find you can dodge using bsxfun and repmat entirely.
*benchmarking code. Tested on Windows 64-bit R2015b, your mileage may vary etc.
function benchie()
b = [0, 100; -10, 1; 0, 1; -1, 1; 10, 20].';
Tb = [];
Tr = [];
K = 20;
for k = 1:K
n = 2^k;
fb = #()bsxfunMethod(b,n);
fr = #()repmatMethod(b,n);
Tb(end+1) = timeit(fb);
Tr(end+1) = timeit(fr);
end
figure; plot(2.^(1:K), Tb, 2.^(1:K), Tr); legend('bsxfun', 'repmat');
end
function bsxfunMethod(b, n)
round(bsxfun(#minus,bsxfun(#times, rand(n,5), diff(b)), b(1,:)));
end
function repmatMethod(b, n)
round(rand(n,5) .* repmat(diff(b), n, 1) + repmat(b(1,:), n, 1));
end
You can use arrayfun, even though I don't see any harm in using loops and writing more readable code as in Steve's answer.
A = cell2mat(arrayfun(#(imin, imax) randi([imin, imax], 100, 1), b(:,1), b(:,2), 'uni', 0)')
You can do this with randi, passing in rows of b to its first argument:
b = [0,100;-10,1;0,1;-1,1;10,20];
A = zeros(100,5);
f=#(ii)randi(b(ii,:),100,1);
for ii = 1:size(A,2)
A(:,ii) = f(ii);
end
I suspect there is a way of doing this without looping through rows/columns, probably with bsxfun.

create a matrix without using loop or nested loop operations in MATLAB

I want to create a matrix without using any loop, like in the following program I made in MATLAB 2011a for forming matrix n(x,y).
segment1 and segment2 have the same dimensions (you can take any two matrices).
segment1 = [1 2 3;4 5 6];
segment2 = [5 2 6;9 1 2];
seg1_max = max(max(segment1));
seg2_max = max(max(segment2));
n = zeros(seg1_max, seg2_max);
i = 1; j = 1;
while i<=size(segment1, 1)
while j<=size(segment1, 2)
x = segment1(i, j);
y = segment2(i, j);
n(x,y) = n(x,y)+1;
j = j+1;
end;
i = i+1; j = 1;
end;
I have also made this program using for loop, but i want to get matrix n(x,y) without using loop operations.
The function accumarray can be used for this. Given your segment1 and segment2, the following computes m, which will be the same as your n:
x = segment1(:);
y = segment2(:);
m = accumarray([x y], ones(size(x)));

Calculate a tricky sum in Matlab

With the following variables:
m = 1:4; n = 1:32;
phi = linspace(0, 2*pi, 100);
theta = linspace(-pi, pi, 50);
S_mn = <a 4x32 coefficient matrix, corresponding to m and n>;
how do I compute the sum over m and n of S_mn*exp(1i*(m*theta + n*phi)), i.e.
I've thought of things like
[m, n] = meshgrid(m,n);
[theta, phi] = meshgrid(theta,phi);
r_mn = S_mn.*exp(1i*(m.*theta + n.*phi));
thesum = sum(r_mn(:));
but that requires theta and phi to have the same number of elements as m and n, and it gives me just one element in return - I want a matrix the the size of meshgrid(theta,phi), regardless of the sizes of theta and phi (i.e. I want to be able to evaluate the sum as a function of theta and phi).
How do I do this calculation in matlab?
Since I don't know what S is...
S = randn(4,32);
[m,n] = ndgrid(1:4,1:32);
fun = #(theta,phi) sum(sum(S.*exp(sqrt(-1)*(m*theta + n*phi))));
Works fine for me.
fun(pi,3*pi/2)
ans =
-15.8643373238676 - 1.45785698818839i
If you now wish to do this for a large set of values phi and theta, a pair of loops now are the trivial solution. Or, you can do it all in one computation, although the arrays will get larger. Still not hard. WTP?
You do realize that both meshgrid and ndgrid take more than just two arguments? So it is time to learn how to use bsxfun, and then squeeze.
[m,n,theta,phi] = ndgrid(1:4,1:32,linspace(-pi, pi, 50),linspace(0, 2*pi, 100));
res = bsxfun(#times,S,exp(sqrt(-1)*(m.*theta + n.*phi)));
res = squeeze(sum(sum(res,1),2));
Or do this, which will be a bit faster. The previous computation took my machine .07 seconds. This last one took .05, so some savings by using bsxfun heavily.
m = (1:4)';
n = 1:32;
[theta,phi] = ndgrid(linspace(-pi, pi, 50),linspace(0, 2*pi, 100));
theta = reshape(theta,[1,1,size(theta)]);
phi = reshape(phi,size(theta));
res = bsxfun(#plus,bsxfun(#times,m,theta*sqrt(-1)),bsxfun(#times,n,phi*sqrt(-1)));
res = bsxfun(#times,S,exp(res));
res = squeeze(sum(sum(res,1),2));
If you need to do the above 2000 times, so it should take 100 seconds to do. WTP? Get some coffee and relax.
First save the size of each variable:
size_m = size(m);
size_n = size(n);
size_theta = size(theta);
size_phi = size(phi);
Use ngrid function like this:
[theta, phi, m, n] = ngrid(theta, phi, m, n)
This will give you an array with 4 dimensions (one for each of your variables: theta, phi, m, n). Now you can calculate this:
m.*theta + n.*phi
Now you need to make S_mn have 4 dimensions with sizes size_theta, size_phi, size_m, size_n like this:
S_tpmn = repmat(S_mn, [size_theta size_phi size_m size_n]);
Now you can calculate your sum like this:
aux_sum = S_tpmn.*exp(1i*(m.*theta + n.*phi));
Finally you can sum along the last 2 dimensions (m and n) to get an array with 2 dimensions with size size_theta by size_phi:
final_sum = sum(sum(aux_sum, 4), 3);
Note: I don't have access to Matlab right now, so I can't test if this actually works.
There are several ways you could go about this.
One way is to create a function(-handle) that returns the sum as a function of theta and phi, and then use arrayfun to do the sums. Another is to fully vectorize the computation, though that will use more memory.
The arrayfun version:
[m, n] = meshgrid(m,n);
sumHandle = #(theta,phi)sum(reshape(...
S_mn.*exp(1i(m*theta + n*phi)),...
[],1))
[theta, phi] = meshgrid(theta,phi);
sumAsFunOfThetaPhi = arrayfun(sumHandle,theta,phi);
The vectorized version:
[m, n] = meshgrid(m,n);
m = permute(m(:),[2 4 1 3]); %# vector along dim 3
n = permute(n(:),[2 3 4 1]); %# vector along dim 4
S_mn = repmat( permute(S_mn,[3 4 1 2]), length(theta),length(phi));
theta = theta(:); %# vector along dim 1 (phi is along dim 2 b/c of linspace)
fullSum = S_mn.* exp( 1i*(...
bsxfun(#plus,...
bsxfun(#times, m, theta),...
bsxfun(#times, n, phi),...
)));
sumAsFunOfThetaPhi = sum(sum( fullSum, 3),4);