Using spfun with two identically ordered sparse matrices - matlab

I got two sparse matrices A and B, which have identical sparsity pattern (all nonzeros are at exactly the same locations):
i = randi(1000,[50,1]);
j = randi(1000,[50,1]);
a = rand(50,1);
b = rand(50,1);
A = sparse(i,j,a,1000,1000);
B = sparse(i,j,b,1000,1000);
I would like to calculate efficiently exp(A-B) only for the nonzeros, and save it back into A.
I tried to use spfun for that task:
f = #(x,y) exp(x-y);
A = spfun(f,A,B);
but I got an error in spfun saying: "Too many input arguments."
Can anyone suggest an efficient way to calculate it?
It should be calculated many times.
Thanks!
Edit: mikkola suggested A = spfun(#f,A-B) which solves the problem, but the question remains how to do it with a function of two variables that can't be solved using the same trick. For example:
g = #(x,y) x.*cos(y);
A = spfun(#g,A,B);

You can't use
A = spfun(#exp, A-B);
because for entries where A an B are equal you will get 0 instead of 1.
To solve that, you can compute the vector of exponentials at the nonzero components, and then build a sparse matrix from that:
A = sparse(i,j,exp(nonzeros(A)-nonzeros(B))); %// if you have i, j stored
or
A(find(A)) = exp(nonzeros(A)-nonzeros(B));

Edit
According to the documentation, spfun can take only two inputs: a function and one sparse matrix.
So you cannot do directly what you want to do. The best solution is probably what has been suggested in the comments, i.e.:
res = spfun(#exp, A-B);
Best,

Related

Why I am getting matrix dimension error in the line while calculating n?

Can you please tell me what's wrong with the following code?
function [n]=calculate_n(p,delta)
e = 1.6*power(10,-19);
k = 1.38*power(10,-23);
T = 298;
co = 3.25*power(10,13)*e*power(10,4);
er=12.5;
eo=1.0;
Nv=3*power(10,13);
us = log((p*e)/sqrt(2*k*T*er*eo*Nv))*2*k*T;
tmp = delta+(e*e*p)/co+us;
n = 1/(exp((tmp))+1);
end
I am getting matrix dimension error while calculating n. Please help me.
Caller:
e = 1.6*power(10,-19);
x = logspace(13,18);
y=calculate_n(x,0.2*e);
semilogx(x,y,'-s');
grid on;
Just replace n = 1/(exp((tmp))+1); with n = 1./(exp(tmp)+1);. But beware, tmp is so small for these values that exp(tmp) will always be 1. Also, there is a surplus bracket around tmp, you might want to check if you put them correctly.
Edit:
The reason is that A/B tries to solve the system of linear equations A*x = B for x which is not what you wanted. It threw an error because it requires both variables to have the same number of columns. A./B performs element-wise matrix division which is what you wanted. However, if A and B are singular A/B = A./B. See the documentation for more info.

matlab inline function with argument conditions

folks,
I am wondering if it is possible to write the following function of r as an inline function in matlab. I tried to include the condition as a separate factor such as *(r>a) and I got NaN due to the division of 1/r^3 when r is 0.
I fould a simple way out. It's basically what Shai and Jigg suggested, i.e. using an extra multiplicative factor of (r>a).
To get rid of NaN, we just need to add eps to the denominator of 1/r3, i.e.
1/(r+eps)^3 *(r>a)
First, you haven't stated what should actually happen if r = 0. Mathematically the term gets infinity. I assumed you rather want to set it to zero. And what should happen for r = a? Just another ill-defined case, are you sure your formula is correct?
If you have the Statistics Toolbox you can use nansum. If not, I'd say there is no way around to write your own function similar to nansum, which can't be done inline.
r = -5:1:5;
a = 1;
R = 42; %// rest of your function
%// not working, or removing of nan afterwards required
X = #( r ) (r>=a).*(a./r).^3*R;
%// inline solution with statistics toolbox
Y = #( r ) arrayfun(#(x) nansum( (x>=a)*(a/x)^3*R ), r);
output = [X(r)' Y(r)']
nansum is not vectorized, if you still want to use it for vectors wrap it into arrayfun.
The code of nansum does exactly what was suggested in the comments (output(isnan(output))=0), I'm probably not allowed to copy&paste it here. It filters out all NaN and then sums the input. Use open nansum to have insight.
As pointed out by Jigg, similar functions like nanmean would do the trick as well.
You can try
chi = 1; %// arbitrary value
a = 1; %// arbitrary value
theta = pi/3; %// arbitrary value
nu = #( r ) (r>a).*( (chi/3).*((a.^3)./(r.^3)).*(3*cos(theta).^2 -1);

Error: Matrix dimensions must agree for plot

having a problem with my "new love", matlab: I wrote a function to calculate an integral using the trapz-method: `
function [L]=bogenlaenge_innen(schwingungen)
R = 1500; %Ablegeradius
OA = 1; %Amplitude
S = schwingungen; %Schwingungszahl
B = 3.175; %Tapebreite
phi = 0:2.*pi./10000:2.*pi;
BL = sqrt((R-B).^2+2.*(R-B).*OA.*sin(S.*phi)+OA.^2.*(sin(S.*phi)).^2+OA.^2.*S.^2.*(cos(S.*phi)).^2);
L = trapz(phi,BL)`
this works fine when i start it with one specific number out of the command-window. Now I want to plot the values of "L" for several S.
I did the following in a new *.m-file:
W = (0:1:1500);
T = bogenlaenge_innen(W);
plot(W,T)
And there it is:
Error using .*
Matrix dimensions must agree.
What is wrong? Is it just a dot somewhere? I am using matlab for the second day now, so please be patient.... ;) Thank you so much in advance!
PS: just ignore the german part of the code, it does not really matter :)
In your code, the arrays S and phi in the expression sin(S.*phi) should have same size or one of them should be a constant in order the code works
The error is most likely because you have made it so that the number of elements in schwingungen, i.e. W in your code, must be equal to the number of elements in phi. Since size(W) gives you a different result from size(0:2.*pi./10000:2.*pi), you get the error.
The way .* works is that is multiplies each corresponding elements of two matrices provided that they either have the same dimensions or that one of them is a scalar. So your code will work when schwingungen is a scalar, but not when it's a vector as chances are it has a different number of elements from the way you hard coded phi.
The simplest course of action (not necessarily the most Matlabesque though) for you is to loop through the different values of S:
W = (0:1:1500);
T = zeros(size(W); %Preallocate for speed)
for ii = 1:length(W)
T(ii) = bogenlaenge_innen(W(ii));
end
plot(W,T)
In your function you define phi as a vector of 10001 elements.
In this same function you do S.*phi, so if S is not the same length as phi, you will get the "dimensions must agree" error.
In your call to the function you are doing it with a vector of length 1501, so there is your error.
Regards

Multiplying each element of a vector with a matrix

I have a vector v and a matrix M. I want to multiply each element of v with M and then sum all the resulting matrices.
for i=1:length(v)
lala(:,:,i) = v(i).*M;
end
sum(lala, 3)
Is it possible to do this without a for loop?
I think Danil Asotsky's answer is correct. He's exploiting the the linearity of the operation here. I just want to give another solution, using use the Kronecker tensor product, that does not dependent on this linearity property, hence still works with operation other than sum:
kvM = kron(v,M);
result = sum(reshape(kvM,[size(M) numel(v)]),3)
It's too late at my local time, and I dont feel like explaining the details of why this works, if you can't figure out from matlab help and wikipedia, then comment below and I'll explain for you.
Do you have single matrix M that don't depend from i?
In this case sum(v(i) * M) = sum(v(i)) *M.
E.g you will have expected result for code:
v_sum = sum(v);
lala_sum = v_sum * M;

Vectorizing the creation of a matrix of successive powers

Let x=1:100 and N=1:10. I would like to create a matrix x^N so that the ith column contains the entries [1 i i^2 ... i^N].
I can easily do this using for loops. But is there a way to do this using vectorized code?
I'd go for:
x = 1:100;
N = 1:10;
Solution = repmat(x,[length(N)+1 1]).^repmat(([0 N])',[1 length(x)]);
Another solution (probably much more efficient):
Solution = [ones(size(x)); cumprod(repmat(x,[length(N) 1]),1)];
Or even:
Solution = bsxfun(#power,x,[0 N]');
Hope this helps.
Sounds like a Vandermonde matrix. So use vander:
A = vander(1:100);
A = A(1:10, :);
Since your matrices aren't that big, the most straight-forward way to do this would be to use MESHGRID and the element-wise power operator .^:
[x,N] = meshgrid(1:100,0:10);
x = x.^N;
This creates an 11-by-100 matrix where each column i contains [i^0; i^1; i^2; ... i^10].
Not sure if it really fits your question.
bsxfun(#power, cumsum(ones(100,10),2), cumsum(ones(100,10),1))
EDIT:
As pointed out by Adrien, my first attempt was not compliant with the OP question.
xn = 100;
N=10;
solution = [ones(1,xn); bsxfun(#power, cumsum(ones(N,xn),2), cumsum(ones(N,xn),1))];
Why not use an easy to understand for loop?
c = [1:10]'; %count to 100 for full scale problem
for i = 1:4; %loop to 10 for full scale problem
M(:,i) = c.^(i-1)
end
It takes more thinking to understand the clever vectorized versions of this code that people have shown. Mine is more of a barbarian way of doing things, but anyone reading it will understand it.
I prefer easy to understand code.
(yes, I could have pre-allocated. Not worth the lowered clarity for small cases like this.)