Most efficient way to put vector on the off-diagonal of the blocks of a block matrix

16 views (last 30 days)
I have two vectors each of the same length, and I want to put these vectors on the diagonal parts of each block of a matrix. Let's say my two vectors are and , both are of length , I am trying to construct an matrix that looks like
For example if my vectors were v = [1 2 3 4 5 6 7 8 9 8 7 6] and u = [9 8 7 6 5 4 3 2 1 2 3 4] then I would mant a matrix that looks like
[0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
9 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0;
0 8 0 3 0 0 0 0 0 0 0 0 0 0 0 0;
0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0;
0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0;
0 0 0 0 6 0 5 0 0 0 0 0 0 0 0 0;
0 0 0 0 0 5 0 6 0 0 0 0 0 0 0 0;
0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0;
0 0 0 0 0 0 0 0 3 0 8 0 0 0 0 0;
0 0 0 0 0 0 0 0 0 2 0 9 0 0 0 0;
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0;
0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0;
0 0 0 0 0 0 0 0 0 0 0 0 2 0 7 0;
0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 6;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0;
]
I would like to know what an efficient way of doing this, particularly with sparse arrays I could create this matrix.

Accepted Answer

Stephen23
Stephen23 on 23 May 2023
Edited: Stephen23 on 23 May 2023
v = [1,2,3,4,5,6,7,8,9,8,7,6];
u = [9,8,7,6,5,4,3,2,1,2,3,4];
L = sqrt(numel(v));
vM = reshape(v,fix(L),[]);
uM = reshape(u,fix(L),[]);
vM(end+1,:) = 0;
uM(end+1,:) = 0;
vM([2:end,1],:) = vM;
N = ceil(L).^2;
M = spdiags([uM(:),vM(:)],[-1,1],N,N);
Checking:
full(M)
ans = 16×16
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 8 0 0 0 0 0

More Answers (2)

David Goodmanson
David Goodmanson on 24 May 2023
Edited: David Goodmanson on 24 May 2023
Hi David,
MODIFIED
see Dyuman's comment to sensibly shorten this original code.
n = 4;
v = [1 2 3 4 5 6 7 8 9 8 7 6]; % length n*(n-1)
u = [9 8 7 6 5 4 3 2 1 2 3 4];
vv = [reshape(v,n-1,n); zeros(1,n)];
vv = vv(:);
vv(end) = [];
uu = [reshape(u,n-1,n); zeros(1,n)];
uu = uu(:);
uu(end) = [];
M = diag(vv,1) + diag(uu,-1)
you can cut even more lines using the code below but at the cost of transparancy, so I think Dyuman's version is the best one.
n = 4;
v = [1 2 3 4 5 6 7 8 9 8 7 6];
u = [9 8 7 6 5 4 3 2 1 2 3 4];
uv = [reshape([v u],n-1,2*n);zeros(1,2*n)];
M = diag(uv(1:n^2-1),1) + diag(uv(n^2+1:2*n^2-1),-1)
  1 Comment
Dyuman Joshi
Dyuman Joshi on 24 May 2023
You can use indexing to shorten the code by 2 lines -
n = 4;
v = [1 2 3 4 5 6 7 8 9 8 7 6]; % length n*(n-1)
u = [9 8 7 6 5 4 3 2 1 2 3 4];
vv = [reshape(v,n-1,n); zeros(1,n)];
vv = vv(1:end-1);
uu = [reshape(u,n-1,n); zeros(1,n)];
uu = uu(1:end-1);
M = diag(vv,1) + diag(uu,-1)
M = 16×16
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 8 0 0 0 0 0

Sign in to comment.


John D'Errico
John D'Errico on 23 May 2023
The most efficient way? SPDIAGS.
help spdiags
SPDIAGS Sparse matrix formed from diagonals. SPDIAGS, which generalizes the function "diag", deals with three matrices, in various combinations, as both input and output. [B,d] = SPDIAGS(A) extracts all nonzero diagonals from the m-by-n matrix A. B is a min(m,n)-by-p matrix whose columns are the p nonzero diagonals of A. d is a vector of length p whose integer components specify the diagonals in A. B = SPDIAGS(A,d) extracts the diagonals specified by d. A = SPDIAGS(B,d,A) replaces the diagonals of A specified by d with the columns of B. The output is sparse. A = SPDIAGS(B,d,m,n) creates an m-by-n sparse matrix from the columns of B and places them along the diagonals specified by d. Roughly, A, B and d are related by for k = 1:p B(:,k) = diag(A,d(k)) end Example: These commands generate a sparse tridiagonal representation of the classic second difference operator on n points. e = ones(n,1); A = spdiags([e -2*e e], -1:1, n, n) Some elements of B, corresponding to positions "outside" of A, are not actually used. They are not referenced when B is an input and are set to zero when B is an output. See the documentation for an illustration of this behavior. See also DIAG, SPEYE. Documentation for spdiags doc spdiags Other uses of spdiags codistributed/spdiags gpuArray/spdiags
For example, to create a 6x6 sparse matrix, with an upper and lower diagonal elements as vectors of length 5, we do this:
V1 = randi(10,[5,1]); % Just some random numbers.
V2 = randi(10,[5,1]);
A = spdiags([[V1;nan],[nan;V2]],[-1,1],6,6)
A =
(2,1) 8 (1,2) 9 (3,2) 10 (2,3) 8 (4,3) 4 (3,4) 6 (5,4) 7 (4,5) 6 (6,5) 2 (5,6) 6
full(A)
ans = 6×6
0 9 0 0 0 0 8 0 8 0 0 0 0 10 0 6 0 0 0 0 4 0 6 0 0 0 0 7 0 6 0 0 0 0 2 0
spy(A)
Note that I padded an extra element, a NaN in this case, at the beginning of V2 and the end of V1.

Categories

Find more on Sparse Matrices in Help Center and File Exchange

Products


Release

R2022a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!