MATLAB: Permuting elements in an symmetrical matrix with restricted outcomes

MATLABmatrixmatrix element permutationmatrix permutationpermutationpermuting matrices with restricted outcomessymmetrical

I would like to derive all possible permutations of a matrix A = [0 2 0 1; 2 0 1 0; 0 1 0 1; 1 0 1 0] by permuting the positions of elements with the following restrictions:
[1] Diagonal elements can only be "0" or "2" (i.e. the permuated matrix A = [0 2 0 1; 0 1 2 0; 0 1 0 1; 1 0 1 0] would NOT be valid as "1" is a diagonal element
[2] The positions and values (i.e. 0, 1 or 2) of elements (not including diagonal elements) must be "mirrored" across the diagonal (i.e. the permuted matrix A = [0 2 0 1; 2 0 1 0; 1 0 0 1; 0 1 1 0] is NOT valid
Can someone help me with a code for this ?
Thus far I have been generating all possible permutation matrices for this 4 x 4 matrix (4!) and iteratively multiplying the matrix A by each permutation matrix but I am not sure if I can derive all possible permutations using this method (not to mention it being extremely time consuming as eventually I will be permuting many more, larger matrices)

Best Answer

Since the permuted matrices are all symmetric, you really only need to permute the lower (or upper) triangle of the matrix, excluding the diagonal, and then reflect the values. Since your matrix is 4x4, there are 6 values in the lower triangle excluding the diagonal. That results in 6! permutations (720). But there are also 16 variations of the diagonal (4 choices from 2 values [0,2] with replacement; if that's what you're looking for since it was unlcear).
so in total, there are 720 * 16 = 11520 permutations of matrix A.
Some of the lines below could be combined but I chose to keep them separated to increase readability. There may be additional shortcuts that I didn't think of but the code completes in less than 150 milliseconds.
The code requires Jos' permn() function from the file exchange in order to compute the permutations of diagonal elements with replacement.
See the inline comments for detials.
% Input matrix
A = [0 2 0 1; 2 0 1 0; 0 1 0 1; 1 0 1 0];
% Get index of lower triangle values (below diagonal)
trilMat = tril(reshape(1:numel(A),size(A)),-1);
trilIdx = trilMat(trilMat>0);
% List all permutation of lower triangle indices
trilPerms = perms(trilIdx);
% List accepted diagonal values
diagPermitted = [0,2];
% List all permutation of diagonal values (with replacement)
% https://www.mathworks.com/matlabcentral/fileexchange/7147-permn
diagPerms = permn(diagPermitted,numel(diag(trilMat)));
% Loop through all lower-triangle-permutation and diagonal-permutations
diagMask = logical(eye(size(A)));
nPerms = size(trilPerms,1) * size(diagPerms,1); % total number of permutations
A_PERM = nan([size(A),nPerms]); % all permutations
for i = 1:size(trilPerms,1)
for j = 1:size(diagPerms,1)
c = (i-1)*size(diagPerms,1)+j;
base = zeros(size(A));
base(trilIdx) = A(trilPerms(i,:)); % permute lower triangle
base = base + tril(base,-1).'; % reflect lower triangle
base(diagMask) = diagPerms(j,:); % permute diagonal
if ~issymmetric(base)
% Sanity check: matrix is symmetric
error('Matrix not symmetic. i = %d j = %d',i,j)
end
A_PERM(:,:,c) = base; % store matrix
end
end
A_PERM is a [4 x 4 x 11520] array containing all 11520 permutations of the 4x4 matrix A.
[addendum]
"I would like to restrict the resultant, permuted matrices to also have rows and columns that sum to 3,3,2,2, more specifically two of the rows (or columns) in the "accepted" permuted matrices must sum to 2 and the other two must sum to 3, the order of the row (column) sums does not matter."
It would be complicated to work that restriction into the permutation rules. Instead, keep all of the code above, produce all permutations, and then eliminate any that do no follow that rule. That just requires adding the the following two lines below at the end of the code above.
goodRows = ismember(squeeze(sort(sum(A_PERM,2))).',[2 2 3 3],'rows');
A_PERM(:,:,~goodRows) = [];
To see the sum of each row,
sum(A_PERM,2)
[addendum II]
Extract the unique matrices (see the discussion below for details).
% Extract unique matrices.
% Each matrix is collapsed to a row vector and all
% row vector are combined into a mega-matrix that
% contains all of the A_PERM data. Then we'll
% identify unique rows numbers.
megaMat = squeeze(reshape(A_PERM,1,prod(size(A_PERM,[1,2])),size(A_PERM,3))).';
[~, unqRowIdx] = unique(megaMat,'rows');
A_PERM_UNQ = A_PERM(:,:,unqRowIdx);