I want to organize the data returned by sph2cart as a matrix with vector elements, and operate on each element in this matrix (vector-vector or vector-scalar calculation). Here is an example wher I achieve this:
lightV = zeros(1, 1, 3);
lightV(1,1,1) = 0.5;
lightV(1,1,2) = 0.4;
lightV(1,1,3) = 0.7;
[Az El] = meshgrid(0:60:360, 0:15:90);
[x y z] = sph2cart(Az*pi/180, El*pi/180, 1);
refV = zeros(size(Az,1), size(Az,2), 3);
radius = zeros(size(Az,1), size(Az,2));
for i = 1:size(Az,1)
for j = 1:size(Az,2)
refV(i,j,1) = -x(i,j);
refV(i,j,2) = -y(i,j);
refV(i,j,3) = z(i,j);
radius(i,j) = dot(refV(i,j,:), lightV(1,1,:));
end
end
However this looks somewhat redundant, how could I make it more terse?
Well, you can vectorize your code like so:
S.lightV = [0.5, 0.4, 0.7];
[Az, El] = meshgrid(0:60:360, 0:15:90);
[S.x, S.y, S.z] = sph2cart(Az * pi/180, El * pi/180, 1);
S.refV = cat(3, -x, -y, z);
S.radius = sum(bsxfun(#times, S.refV, reshape(S.lightV, 1, 1, [])), 3);
Note the usage of cat to concatenate along the third dimension, and the combination of bsxfun and sum to replace the dot product inside the nested for loop. I've also bound everything together in one struct S.
Related
I am generating two different coordinates (x, y) within a loop. In my code, I have just realised that it is saving the last variable from the loop. I am, however, trying to save all the iterations from the setsize variable. I already tried to save using something like:
circleposition = [0:length(setsize) x(i),y(i)];
But, it seems that I am not doing it correctly, getting the following error:
Subscript indices must either be real positive integers or logicals.-
Error using vertcat
Dimensions of matrices being concatenated are not consistent.
Here is my original code:
setsize = 9;
r = 340;
cx = 500;
cy = 500;
anglesegment = 2 * pi/setsize;
circleposition = [];
for i = drange (0:setsize)
x = r * cos(i*anglesegment) + cx;
y = r * sin(i*anglesegment) + cy;
circleposition = [x,y];
end
Output:
circleposition =
0 1.0000
840.0000 500.0000
It runs only with the first/last iteration. I need to get 9 x's and 9 y's (depending the setsize, variable).
It's kind of hard to follow, which error message comes from which attempt, but let's have a look.
I don't have access to the Parallel Computing Toolbox, which seems necessary to use the for-loop over distributed range drange, but I assume, this loop can be replaced by for i = 0:setsize for testing.
Now, when starting at i = 0, you would try to access x(0) and y(0), which is not allowed (Subscript indices must either be real positive integers or logicals). Also, you would get 10 values instead of 9, as you stated in your question. So, let's start at i = 1.
To store all 9 pairs of x and y, your circleposition should be an 9 x 2 array So, initialize that by, for example, circleposition = zeros(setsize, 2).
Last, you need to use proper indexing to store [x, y] at the i-th row of circleposition, i.e. circleposition(i, :).
So, the corrected code (attention on the replaced drange part) could look like this:
setsize = 9;
r = 340;
cx = 500;
cy = 500;
anglesegment = 2 * pi/setsize;
circleposition = zeros(setsize, 2); % Initialize circleposition appropriately
for i = 1:setsize % Start at i = 1
x = r * cos(i*anglesegment) + cx;
y = r * sin(i*anglesegment) + cy;
circleposition(i, :) = [x, y]; % Correct indexing of the row
end
circleposition % Output
The output would then be:
circleposition =
760.46 718.55
559.04 834.83
330.00 794.45
180.50 616.29
180.50 383.71
330.00 205.55
559.04 165.17
760.46 281.45
840.00 500.00
On the second error (Error using vertcat. Dimensions of matrices being concatenated are not consistent.): I don't see, where you used vertical concatenation at all!?
Hear is code that works:
setsize = 9;
r = 340;
cx = 500;
cy = 500;
anglesegment = 2 * pi/setsize;
circleposition = zeros(setsize + 1, 2); % Changed from circleposition = []
for i = drange (0:setsize)
x = r * cos(i*anglesegment) + cx;
y = r * sin(i*anglesegment) + cy;
circleposition((i+1),:) = [x,y]; % Changed from circleposition = [x,y];
end
Explanation:
The fix was Changing circleposition = [x,y]; to circleposition((i+1),:) = [x,y]. Without ((i+1),:), you are changing the data of circleposition, not adding to it.
Changing circleposition = []; to circleposition = zeros(setsize + 1, 2); was not required, its just recommended to allocate memory for speed, not an issue for small number of elements.
I actually vectorizing one of my code and I have some issues.
This is my initial code:
CoordVorBd = random(N+1,3)
CoordCP = random(N,3)
v = random(1,3)
for i = 1 : N
for j = 1 : N
ri1j = (-CoordVorBd (i,:) + CoordCP(j,:));
vij(i,j,:) = cross(v,ri1j))/(norm(ri1j)
end
end
I have start to vectorize that creating some matrix that contains 3*1 Vectors. My size of matrix is N*N*3.
CoordVorBd1(1:N,:) = CoordVorBd(2:N+1,:);
CoordCP_x= CoordCP(:,1);
CoordCP_y= CoordCP(:,2);
CoordCP_z= CoordCP(:,3);
CoordVorBd_x = CoordVorBd([1:N],1);
CoordVorBd_y = CoordVorBd([1:N],2);
CoordVorBd_z = CoordVorBd([1:N],3);
CoordVorBd1_x = CoordVorBd1(:,1);
CoordVorBd1_y = CoordVorBd1(:,2);
CoordVorBd1_z = CoordVorBd1(:,3);
[X,Y] = meshgrid (1:N);
ri1j_x = (-CoordVorBd_x(X) + CoordCP_x(Y));
ri1j_y = (-CoordVorBd_y(X) + CoordCP_y(Y));
ri1j_z = (-CoordVorBd_z(X) + CoordCP_z(Y));
ri1jmat(:,:,1) = ri1j_x(:,:);
ri1jmat(:,:,2) = ri1j_y(:,:);
ri1jmat(:,:,3) = ri1j_z(:,:);
vmat(:,:,1) = ones(N)*v(1);
vmat(:,:,2) = ones(N)*v(2);
vmat(:,:,3) = ones(N)*v(3);
This code works but is heavy in terms of variable creation. I did'nt achieve to apply the vectorization to all the matrix in one time.
The formule like
ri1jmat(X,Y,1:3) = (-CoordVorBd (X,:) + CoordCP(Y,:));
doesn't work...
If someone have some ideas to have something cleaner.
At this point I have a N*N*3 matrix ri1jmat with all my vectors.
I want to compute the N*N rij1norm matrix that is the norm of the vectors
rij1norm(i,j) = norm(ri1jmat(i,j,1:3))
to be able to vectorize the vij matrix.
vij(:,:,1:3) = (cross(vmat(:,:,1:3),ri1jmat(:,:,1:3))/(ri1jmatnorm(:,:));
The cross product works.
I tried numbers of method without achieve to have this rij1norm matrix without doing a double loop.
If someone have some tricks, thanks by advance.
Here's a vectorized version. Note your original loop didn't include the last column of CoordVorBd, so if that was intentional you need to remove it from the below code as well. I assumed it was a mistake.
CoordVorBd = rand(N+1,3);
CoordCP = rand(N,3);
v = rand(1,3);
repCoordVor=kron(CoordVorBd', ones(1,size(CoordCP,1)))'; %based on http://stackoverflow.com/questions/16266804/matlab-repeat-every-column-sequentially-n-times
repCoordCP=repmat(CoordCP, size(CoordVorBd,1),1); %repeat matrix
V2=-repCoordVor + repCoordCP; %your ri1j
nrm123=sqrt(sum(V2.^2,2)); %vectorized norm for each row
vij_unformatted=cat(3,(v(:,2).*V2(:,3) - V2(:,2).*v(:,3))./nrm123,(v(:,3).*V2(:,1) - V2(:,3).*v(:,1))./nrm123,(v(:,1).*V2(:,2) - V2(:,1).*v(:,2))./nrm123); % cross product, expanded, and each term divided by norm, could use bsxfun(#rdivide,cr123,nrm123) instead, if cr123 is same without divisions
vij=permute(reshape( vij_unformatted,N,N+1,3),[2,1,3]); %reformat to match your vij
Here is another way to do it using arrayfun
% Define a meshgrid of indices to run over
[I, J] = meshgrid(1:N, 1:(N+1));
% Calculate ril for each index
rilj = arrayfun(#(x, y) -CoordVorBd (y,:) + CoordCP(x,:), I, J, 'UniformOutput', false);
%Calculate vij for each point
temp_vij1 = arrayfun(#(x, y) cross(v, rilj{x, y}) / norm(rilj{x, y}), J, I, 'UniformOutput', false);
%Reshape the matrix into desired format
temp_vij2 = cell2mat(temp_vij1);
vij = cat(3, temp_vij2(:, 1:3:end), temp_vij2(:, 2:3:end), temp_vij2(:, 3:3:end));
I have the next question, how to build a matrix with specific values knowing that the size of the matrix is NxN.
Here is my question
I've being trying with the next code:
a = (1+2*Du*dt/dx^2);
b = -Du*dt/dx^2;
main = a*sparse(ones(Nx,1));
off = b*sparse(ones(Nx-1,1));
Bu = diag(main) + diag(off,1) + diag(off,-1);
But as you can see there is not the needed value in (1,1) and (N,N), so how can I build this specific matrices? How would be the code for this in MATLAB?
spdiags is the way to go,
A = sparse(Nx);
A = spdiags(b*ones(Nx-1,1), -1, A);
A = spdiags(a*ones(Nx,1), 0, A);
A = spdiags(b*ones(Nx-1,1), 1, A);
A(1, 1:2) = [1,1];
A(N, N-1:N) = [1,1];
I want to vectorize a 3D function, but the function does not have an analytical expression. For instance, I can vectorize the function
F(x, y, z) = (sin(y)*x, z*y, x*y)
by doing something like
function out = Vect_fn(x, y,z)
out(1) = x.*sin(y);
out(2) = z.*y;
out(3) = x.*y;
end
And then running the script
a = linspace(0,1,10);
[xx, yy, zz] = meshgrid(a, a, a);
D = Vect_fn(xx, yy, zz)
However, suppose the function does not have an analytical expression, for example
function y = Vect_Nexplicit(y0)
%%%%%%y0 is a 3x1 vector%%%%%%%%%%%%%%
t0 = 0.0;
tf = 3.0;
[t, z] = ode45('ODE_fn', [t0,tf], y0);
sz = size(z);
n = sz(1);
y = z(n, :);
end
where ODE_fn is just some function that spits out the right-hand side of a an ODE. Thus the function simply solves an ODE and so the function is not known explicitly. Of course I can use a for loop, but those are slower (esp. in Octave, which I prefer since it has lsode for solving ODEs)
Trying something like
a = linspace(0,1,10);
[xx, yy, zz] = meshgrid(a, a, a);
D = Vect_Nexplicit(xx, yy, zz)
does not work. Also here is the code for ODF_fn:
function ydot = ODE_fn(t, yin)
A = sqrt(3.0);
B = sqrt(2.0);
C = 1.0;
x = yin(1, 1);
y = yin(2,1);
z = yin(3, 1);
M = reshape(yin(4:12), 3, 3);
ydot(1,1) = A*sin(yin(3)) + C*cos(yin(2));
ydot(2,1) = B*sin(yin(1)) + A*cos(yin(3));
ydot(3,1) = C*sin(yin(2)) + B*cos(yin(1));
DV = [0 -C*sin(y) A*cos(z); B*cos(x) 0 -A*sin(z); -B*sin(x) C*cos(y) 0];
Mdot = DV*M;
ydot(4:12,1) = reshape(Mdot, 9, 1);
end
You can solve systems of differential equations with ode45, so if ODE_fn is vectorised you can use your approach. y0 just needs to be a vector too.
You can create a y0 that is [x1, ..., xn, y1, ..., yn, z1, ..., zn, M1_1-9, ... ,Mn_1-9] and then use for x, y, z just the appropriate indexes i.e. 1:n, n+1:2*n, 2*n+1:3n. Then use reshape(yin(3*n+1:end),3,3,n). But I am not sure how to vectorize the matrix multiplication.
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);