MATLAB accessing multiple elements in sparse matrix using row and column index vectors - matlab

I feel there should be an easy solution but I can't find it:
I have the sparse matrices A B with the same dimension n*n. I want to create matrix C which copies values in A where B is non-zero.
This is my approach:
[r,c,v] = find(B);
% now I'd like to create an array of values using indices r and c,
% but this doesn't work (wrong syntax)
v2 = A(r,c);
% This won't work either
idx = find(B); % linear indexing, too high-dimensional
v2 = A(idx);
% and create C
C = sparse(r,c,v2,n,n);
Here are some more details:
My matrices are very large, so the solution needs to be efficient. C(B~=0) = B(B~=0); won't do it, unfortunately.
Linear indexing won't work either as the matrices are too large (Matrix is too large to return linear indices.).
Is there really no way to use 2-dimensional indices?
Thanks for your help!

I think C = A .* (B~=0); should work. Only non-zeros will be accessed in the entrywise multiplication of two sparse matrices so it will be fast.

Related

Matrix Multiplication Issue - Matlab

In an attempt to create my own covariance function in MatLab I need to perform matrix multiplication on a row to create a matrix.
Given a matrix D where
D = [-2.2769 0.8746
0.6690 -0.4720
-1.0030 -0.9188
2.6111 0.5162]
Now for each row I need manufacture a matrix. For example the first row R = [-2.2770, 0.8746] I would want the matrix M to be returned where M = [5.1847, -1.9915; -1.9915, 0.7649].
Below is what I have written so far. I am asking for some advice to explain how to use matrix multiplication on a rows to produce matrices?
% Find matrices using matrix multiplication
for i=1:size(D, 1)
P1 = (D(i,:))
P2 = transpose(P1)
M = P1*P2
end
You are trying to compute the outer product of each row with itself stored as individual slices in a 3D matrix.
Your code almost works. What you're doing instead is computing the inner product or the dot product of each row with itself. As such it'll give you a single number instead of a matrix. You need to change the transpose operation so that it's done on P1 not P2 and P2 will now simply be P1. Also you are overwriting the matrix M at each iteration. I'm assuming you'd like to store these as individual slices in a 3D matrix. To do this, allocate a 3D matrix where each 2D slice has an equal number of rows and columns which is the number of columns in D while the total number of slices is equal to the total number of rows in D. Then just index into each slice and place the result accordingly:
M = zeros(size(D,2), size(D,2), size(D,1));
% Find matrices using matrix multiplication
for ii=1:size(D, 1)
P = D(ii,:);
M(:,:,ii) = P.'*P;
end
We get:
>> M
M(:,:,1) =
5.18427361 -1.99137674
-1.99137674 0.76492516
M(:,:,2) =
0.447561 -0.315768
-0.315768 0.222784
M(:,:,3) =
1.006009 0.9215564
0.9215564 0.84419344
M(:,:,4) =
6.81784321 1.34784982
1.34784982 0.26646244
Depending on your taste, I would recommend using bsxfun to help you perform the same operation but perhaps doing it faster:
M = bsxfun(#times, permute(D, [2 3 1]), permute(D, [3 2 1]));
In fact, this solution is related to a similar question I asked in the past: Efficiently compute a 3D matrix of outer products - MATLAB. The only difference is that the question wanted to find the outer product of columns instead of the rows.
The way the code works is that we shift the dimensions with permute of D so that we get two matrices of the sizes 2 x 1 x 4 and 1 x 2 x 4. By performing bsxfun and specifying the times function, this allows you to efficiently compute the matrix of outer products per slice simultaneously.

how to vectorze this kind of loop for array

I'm newbie to Matlab. I have some questions.
How can I vectorise this loop:
epsilon = 0.45;
t = -3.033;
n = 100;
I = ones(n,n);
%// diagonal block 1
DB1 = gallery('tridiag',ones(1,n-1),ones(1,n),ones(1,n-1));
for k = 1:n
DB1(k,k) = epsilon;
end
for k = 1:n-1
DB1(k,k+1) = t*heaviside((-1)^(k+1));
end
for k = 2:n
DB1(k,k-1) = t*heaviside((-1)^k);
and this loop with LRG, R2, R1 are 3D arrays
for k = 2:N
LRG(:,:,k) = inv(R(:,:,k) - R2(:,:,k-1)*LRG(:,:,k-1)*R1(:,:,k-1));
end
Is there any way to handle the third dimension of an array (page) without writing (:,:,...) many times?
You don't need to call gallery: this will just initialize DB1 to a specific sparse tridiagonal matrix, which you manually overwrite afterwards. With n=100, I suspect that you don't actually want to work on sparse matrices. Here's the solution:
epsilon = 0.45; t = -3.033;
n = 100;
DB1 = zeros(n); %initialize to 0
%diagonal indices
inds=sub2ind([n n],1:n,1:n);
DB1(inds) = epsilon;
%superdiagonal
inds=sub2ind([n n],1:n-1,2:n);
DB1(inds) = t*heaviside((-1).^(2:n));
%subdiagonal
inds=sub2ind([n n],2:n,1:n-1);
DB1(inds) = t*heaviside((-1).^(2:n));
Here sub2ind lets you compute linear indices from your matrix indices, allowing you to access "subvectors" in your matrix. Watch out for the exponentialization in the heaviside: you need .^ instead of ^ to perform an element-wise operation using the vector 2:n.
If you insist on getting a sparse matrix, you could either convert this matrix to sparse by calling
DB1=sparse(DB1);
which is the easiest option if your matrix is small. If n was larger and you would really need sparse matrices, then you could set up the sparse matrix itself by
DB1new=sparse([1:n 1:n-1 2:n],[1:n 2:n 1:n-1],...
[epsilon*ones(1,n) t*heaviside((-1).^(2:n)) t*heaviside((-1).^(2:n))],n,n);
This call sets the elements of DB1new one-by-one according to its vector arguments: it takes the first index from the first input argument, the seconf index from the second, and the corresponding element from the third vector (see help sparse in MATLAB).
As for your second question: I'm not sure I understand correctly. Like in my example, sub2ind can be used to transform the multidimensional indices into linear ones, see help sub2ind. But personally, I think using the array notation is far more transparent and less prone to typos. And I think you can't vectorize that loop: you have to explicitly calculate the inverse matrix for each k.

Multiply two matrices in Matlab to obtain 3-dimensional matrix

I have two sparse matrices in Matlab, A and B,
and I want to compute a three-dimensional matrix C such that
C(i,j,k) = A(i,j) * B(j,k)
can I do this without a loop?
(Side question: Is there a name for this operation?)
Edit:
Seems my question has already been asked (just for full matrices):
Create a 3-dim matrix from two 2-dim matrices
For full matrices:
You can do it using bsxfun and shiftdim:
C = bsxfun(#times, A, shiftdim(B,-1))
Explanation: Let A be of size M x N and B of size N x P. Applying shiftdim(B,-1) gives a 1 x N x P array. bsxfun implicitly replicates A along the third dimension and shiftdim(B,-1) along the first to compute the desired element-wise product.
Another possibility, usually less efficient than bsxfun, is to repeat the arrays explicity along the desired dimensions, using repmat:
C = repmat(A, [1 1 size(B,2)]) .* repmat(shiftdim(B,-1), [size(A,1) 1 1])
For sparse matrices:
The result cannot be sparse, as sparse ND-arrays are not supported.. But you can do the computations with sparse A and B using linear indexing:
ind1 = repmat(1:numel(A),1,size(B,2));
ind2 = repmat(1:numel(B),size(A,1),1);
ind2 = ind2(:).';
C = NaN([size(A,1),size(A,2),size(B,2)]); %// preallocate with appropriate shape
C(:) = full(A(ind1).*B(ind2)); %// need to use full if C is to be 3D
Answer to your side question: the name for this operation is a hash join.

Comparing two sets of vectors

I've got matrices A and B
size(A) = [n x]; size(B) = [n y];
Now I need to compare euclidian distance of each column vector of A from each column vector of B. I'm using dist method right now
Q = dist([A B]); Q = Q(1:x, x:end);
But it does also lot of needless work (like calculating distances between vectors of A and B separately).
What is the best way to calculate this?
You are looking for pdist2.
% Compute the ordinary Euclidean distance
D = pdist2(A.',B.','euclidean'); % euclidean distance
You should take the transpose of the matrices since pdist2 assumes the observations are in rows, not in columns.
An alternative solution to pdist2, if you don't have the Statistics Toolbox, is to compute this manually. For example, one way to do it is:
[X, Y] = meshgrid(1:size(A, 2), 1:size(B, 2)); %// or meshgrid(1:x, 1:y)
Q = sqrt(sum((A(:, X(:)) - B(:, Y(:))) .^ 2, 1));
The indices of the columns from A and B for each value in vector Q can be obtained by computing:
[X(:), Y(:)]
where each row contains a pair of indices: the first is the column index in matrix A, and the second is the column index in matrix B.
Another solution if you don't have pdist2 and which may also be faster for very large matrices is to vectorize the following mathematical fact:
||x-y||^2 = ||x||^2 + ||y||^2 - 2*dot(x,y)
where ||a|| is the L2-norm (euclidean norm) of a.
Comments:
C=-2*A'*B (this is a x by y matrix) is the vectorization of the dot products.
||x-y||^2 is the square of the euclidean distance which you are looking for.
Is that enough or do you need the explicit code?
The reason this may be faster asymptotically is that you avoid doing the metric calculation for all x*y comparisons, since you are instead making the bottleneck a matrix multiplication (matrix multiplication is highly optimized in matlab). You are taking advantage of the fact that this is the euclidean distance and not just some unknown metric.

Vector to Matrix syntax in MATLAB

Is there a way to combine 2 vectors in MATLAB such that:
mat = zeros(length(C),length(S));
for j=1:length(C)
mat(j,:)=C(j)*S;
end
Using normal MATLAB syntax similar to:
mat = C * S(1:length(S))
This gives a "Inner matrix dimensions must agree error" because it's trying to do normal matrix operations. This is not a standard Linear Algebra operation so I'm not sure how to correctly express it in MATLAB, but it seems like it should be possible without requiring a loop, which is excessively slow in MATLAB.
From your description, it sounds like a simple matrix operation. You just have to make sure you have the right dimensions for C and S. C should be a column vector (length(C)-by-1) and S should be a row vector (1-by-length(S)). Assuming they are the right dimensions, just do the following:
mat = C*S;
If you're not sure of their dimensions, this should work:
mat = (C(:))*(S(:)');
EDIT: Actually, I went a little crazy with the parentheses. Some of them are unnecessary, since there are no order-of-operation concerns. Here's a cleaner version:
mat = C(:)*S(:)';
EXPLANATION:
The matrix multiplication operator in MATLAB will produce either an inner product (resulting in a scalar value) or an outer product (resulting in a matrix) depending on the dimensions of the vectors it is applied to.
The last equation above produces an outer product because of the use of the colon operator to reshape the dimensions of the vector arguments. The syntax C(:) reshapes the contents of C into a single column vector. The syntax S(:)' reshapes the contents of S into a column vector, then transposes it into a row vector. When multiplied, this results in a matrix of size (length(C)-by-length(S)).
Note: This use of the colon operator is applicable to vectors and matrices of any dimension, allowing you to reshape their contents into a single column vector (which makes some operations easier, as shown by this other SO question).
Try executing this in MATLAB:
mat = C*S'
As In:
C = [1; 2; 3];
S = [2; 2; 9; 1];
mat = zeros(length(C),length(S));
for j=1:length(C)
mat(j,:)=C(j)*S;
end
% Equivalent code:
mat2 = C*S';
myDiff = mat - mat2
Do you mean the following?
mat = zeros(length(C),length(S));
for j=1:length(C)
mat(j,:)=C(j)*S;
end
If so, it's simply matrix multiplication:
C' * S % if C and S are row vectors
C * S' % if C and S are column vectors
If you don't know whether C and S are row vectors or column vectors, you can use a trick to turn them into column vectors, then transpose S before multiplying them:
C(:) * S(:)'
I'm not entirely clear on what you're doing - it looks like your resulting matrix will consist of length(C) rows, where the ith row is the vector S scaled by the ith entry of C (since subscripting a vector gives a scalar). In this case, you can do something like
mat = repmat(C,[1 length(S)]) .* repmat(S, [length(C) 1])
where you tile C across columns, and S down rows.
Try this:
C = 1:3
S = 1:5
mat1 = C'*S
mat2 = bsxfun(#times, C',S)
(esp. good when the function you need isn't simpler MATLAB notation)
--Loren
Try using meshgrid:
[Cm, Sm] = meshgrid(C, S);
mat = Cm .* Sm;
edit: nevermind, matrix multiplication will do too. You just need one column vector C and one row vector S. Then do C * S.