I have a 4-D matrix A
of size NxNxPxQ
. How can I easily change the diagonal values to 1 for each NxN
2-D submatrix in a vectorized way?

- 73
- 1
- 7
3 Answers
Incorporating gnovice's suggestion, an easy way to index the elements is:
[N,~,P,Q]=size(A);%# get dimensions of your matrix
diagIndex=repmat(logical(eye(N)),[1 1 P Q]);%# get logical indices of the diagonals
A(diagIndex)=1;%# now index your matrix and set the diagonals to 1.

- 41,765
- 7
- 81
- 98
-
1You can actually avoid the need for using FIND by creating the identity matrix as a logical matrix, then doing logical indexing: `diagIndex = repmat(logical(eye(dim1)),[1 1 dim3 dim4]);` – gnovice Mar 16 '11 at 05:13
You can actually do this very simply by directly computing the linear indices for every diagonal element, then setting them to 1:
[N,N,P,Q] = size(A);
diagIndex = cumsum([1:(N+1):N^2; N^2.*ones(P*Q-1,N)]);
A(diagIndex) = 1;
The above example finds the N
diagonal indices for the first N
-by-N
matrix (1:(N+1):N^2
). Each subsequent N
-by-N
matrix (P*Q-1
of them) is offset by N^2
elements from the last, so a matrix of size PQ-1
-by-N
containing only the value N^2
is appended to the linear indices for the diagonal of the first matrix. When a cumulative sum is performed over each column using the function CUMSUM, the resulting matrix contains the linear indices for all diagonal elements of the 4-D matrix.

- 125,304
- 15
- 256
- 359
You can use direct indexing, and some faffing about with repmat, to add the indexes for a single 50x50 diagonal to the offsets within the larger matrix of each 50x50 block:
Here's an example for a smaller problem:
A = NaN(10,10,5,3);
inner = repmat(sub2ind([10 10], [1:10],[1:10]), 5*3, 10); % diagonals
outer = repmat([10*10 * [0:5*3-1]]', 1, 10*10); % offsets to blocks
diags = inner + outer;
A(diags(:)) = 1;

- 5,863
- 2
- 29
- 46